summaryrefslogtreecommitdiff
path: root/src/steps.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/steps.rs')
-rw-r--r--src/steps.rs124
1 files changed, 113 insertions, 11 deletions
diff --git a/src/steps.rs b/src/steps.rs
index ccbc588..7f5e7d4 100644
--- a/src/steps.rs
+++ b/src/steps.rs
@@ -1,4 +1,4 @@
-use crate::SubplotError;
+use crate::{html::Location, SubplotError};
use serde::{Deserialize, Serialize};
use std::fmt;
@@ -16,15 +16,17 @@ pub struct ScenarioStep {
kind: StepKind,
keyword: String,
text: String,
+ origin: Location,
}
impl ScenarioStep {
/// Construct a new step.
- pub fn new(kind: StepKind, keyword: &str, text: &str) -> ScenarioStep {
+ pub fn new(kind: StepKind, keyword: &str, text: &str, origin: Location) -> ScenarioStep {
ScenarioStep {
kind,
keyword: keyword.to_owned(),
text: text.to_owned(),
+ origin,
}
}
@@ -50,7 +52,12 @@ impl ScenarioStep {
pub fn new_from_str(
text: &str,
default: Option<StepKind>,
+ origin: Location,
) -> Result<ScenarioStep, SubplotError> {
+ if text.trim_start() != text {
+ return Err(SubplotError::NotAtBoln(text.into()));
+ }
+
let mut words = text.split_whitespace();
let keyword = match words.next() {
@@ -75,7 +82,11 @@ impl ScenarioStep {
if joined.len() > 1 {
joined.pop();
}
- Ok(ScenarioStep::new(kind, keyword, &joined))
+ Ok(ScenarioStep::new(kind, keyword, &joined, origin))
+ }
+
+ pub(crate) fn origin(&self) -> &Location {
+ &self.origin
}
}
@@ -85,6 +96,85 @@ impl fmt::Display for ScenarioStep {
}
}
+/// Parse a scenario snippet into a vector of steps.
+pub(crate) fn parse_scenario_snippet(
+ text: &str,
+ loc: &Location,
+) -> Result<Vec<ScenarioStep>, SubplotError> {
+ let mut steps = vec![];
+ let mut prevkind = None;
+ for (idx, line) in text.lines().enumerate() {
+ let line_loc = match loc.clone() {
+ Location::Known {
+ filename,
+ line,
+ col,
+ } => Location::Known {
+ filename,
+ line: line + idx,
+ col,
+ },
+ Location::Unknown => Location::Unknown,
+ };
+ if !line.trim().is_empty() {
+ let step = ScenarioStep::new_from_str(line, prevkind, line_loc)?;
+ prevkind = Some(step.kind());
+ steps.push(step);
+ }
+ }
+ Ok(steps)
+}
+
+#[cfg(test)]
+mod test_steps_parser {
+ use super::{parse_scenario_snippet, Location, ScenarioStep, StepKind, SubplotError};
+ use std::path::Path;
+
+ fn parse(text: &str) -> Result<Vec<ScenarioStep>, SubplotError> {
+ let loc = Location::new(Path::new("test"), 1, 1);
+ parse_scenario_snippet(text, &loc)
+ }
+
+ #[test]
+ fn empty_string() {
+ assert_eq!(parse("").unwrap(), vec![]);
+ }
+
+ #[test]
+ fn simple() {
+ assert_eq!(
+ parse("given foo").unwrap(),
+ vec![ScenarioStep::new(
+ StepKind::Given,
+ "given",
+ "foo",
+ Location::new(Path::new("test"), 1, 1),
+ )]
+ );
+ }
+
+ #[test]
+ fn two_simple() {
+ assert_eq!(
+ parse("given foo\nthen bar\n").unwrap(),
+ vec![
+ ScenarioStep::new(
+ StepKind::Given,
+ "given",
+ "foo",
+ Location::new(Path::new("test"), 1, 1),
+ ),
+ ScenarioStep::new(
+ StepKind::Then,
+ "then",
+ "bar",
+ Location::new(Path::new("test"), 2, 1),
+ )
+ ]
+ );
+ }
+}
+
/// The kind of scenario step we have: given, when, or then.
///
/// This needs to be extended if the Subplot language gets extended with other
@@ -109,53 +199,65 @@ impl fmt::Display for StepKind {
StepKind::When => "when",
StepKind::Then => "then",
};
- write!(f, "{}", s)
+ write!(f, "{s}")
}
}
#[cfg(test)]
mod test {
+ use crate::html::Location;
+
use super::{ScenarioStep, StepKind, SubplotError};
#[test]
fn parses_given() {
- let step = ScenarioStep::new_from_str("GIVEN I am Tomjon", None).unwrap();
+ let step =
+ ScenarioStep::new_from_str("GIVEN I am Tomjon", None, Location::Unknown).unwrap();
assert_eq!(step.kind(), StepKind::Given);
assert_eq!(step.text(), "I am Tomjon");
}
#[test]
fn parses_given_with_extra_spaces() {
- let step = ScenarioStep::new_from_str(" given I am Tomjon ", None).unwrap();
+ let step =
+ ScenarioStep::new_from_str("given I am Tomjon ", None, Location::Unknown)
+ .unwrap();
assert_eq!(step.kind(), StepKind::Given);
assert_eq!(step.text(), "I am Tomjon");
}
#[test]
fn parses_when() {
- let step = ScenarioStep::new_from_str("when I declare myself king", None).unwrap();
+ let step =
+ ScenarioStep::new_from_str("when I declare myself king", None, Location::Unknown)
+ .unwrap();
assert_eq!(step.kind(), StepKind::When);
assert_eq!(step.text(), "I declare myself king");
}
#[test]
fn parses_then() {
- let step = ScenarioStep::new_from_str("thEN everyone accepts it", None).unwrap();
+ let step = ScenarioStep::new_from_str("thEN everyone accepts it", None, Location::Unknown)
+ .unwrap();
assert_eq!(step.kind(), StepKind::Then);
assert_eq!(step.text(), "everyone accepts it");
}
#[test]
fn parses_and() {
- let step =
- ScenarioStep::new_from_str("and everyone accepts it", Some(StepKind::Then)).unwrap();
+ let step = ScenarioStep::new_from_str(
+ "and everyone accepts it",
+ Some(StepKind::Then),
+ Location::Unknown,
+ )
+ .unwrap();
assert_eq!(step.kind(), StepKind::Then);
assert_eq!(step.text(), "everyone accepts it");
}
#[test]
fn fails_to_parse_and() {
- let step = ScenarioStep::new_from_str("and everyone accepts it", None);
+ let step = ScenarioStep::new_from_str("and everyone accepts it", None, Location::Unknown);
assert!(step.is_err());
match step.err() {
None => unreachable!(),