defmodule Gherkin do
@moduledoc """
See `Gherkin.parse/1` for primary usage.
"""
alias Gherkin.Elements.{Feature, Scenario, ScenarioOutline}
alias Gherkin.Parser
@doc """
Primary helper function for parsing binary or streams through `Gherkin`. To use
simply call this function passing in the full text of the file or a file stream.
Example:
iex> "test/fixtures/coffee.feature" |> File.read!() |> Gherkin.parse()
%Gherkin.Elements.Feature{
description: "As a Barrista
Coffee should not be served until paid for
Coffee should not be served until the button has been pressed
If there is no coffee left then money should be refunded
",
line: 1,
name: "Serve coffee",
scenarios: [
%Gherkin.Elements.Scenario{
line: 7,
name: "Buy last coffee",
steps: [
%Gherkin.Elements.Step{
keyword: "Given",
line: 8,
text: "there are 1 coffees left in the machine"
}
],
}
],
}
# Also supports file streams for larger files (must read by lines, bytes not supported)
iex> "test/fixtures/coffee.feature" |> File.stream!() |> Gherkin.parse()
%Gherkin.Elements.Feature{
description: "As a Barrista
Coffee should not be served until paid for
Coffee should not be served until the button has been pressed
If there is no coffee left then money should be refunded
",
line: 1,
name: "Serve coffee",
scenarios: [
%Gherkin.Elements.Scenario{
line: 7,
name: "Buy last coffee",
steps: [
%Gherkin.Elements.Step{
keyword: "Given",
line: 8,
text: "there are 1 coffees left in the machine"
}
],
}
],
}
"""
def parse(string_or_stream) do
Parser.parse_feature(string_or_stream)
end
@doc """
Primary helper function for parsing file through `Gherkin`. To use
simply call this function passing in the relative path to file.
Example:
iex> Gherkin.parse_file("test/fixtures/coffee.feature")
%Gherkin.Elements.Feature{
description: "As a Barrista
Coffee should not be served until paid for
Coffee should not be served until the button has been pressed
If there is no coffee left then money should be refunded
",
file: "test/fixtures/coffee.feature",
line: 1,
name: "Serve coffee",
scenarios: [
%Gherkin.Elements.Scenario{
line: 7,
name: "Buy last coffee",
steps: [
%Gherkin.Elements.Step{
keyword: "Given",
line: 8,
text: "there are 1 coffees left in the machine"
}
],
}
],
}
"""
def parse_file(file_name) do
file_name
|> File.read!()
|> Parser.parse_feature(file_name)
end
@doc """
Given a `Gherkin.Element.Feature`, changes all `Gherkin.Elements.ScenarioOutline`s
into `Gherkin.ElementScenario` as a flattened list of scenarios.
"""
def flatten(feature = %Feature{scenarios: scenarios}) do
%{
feature
| scenarios:
scenarios
|> Enum.map(fn
# Nothing to do
scenario = %Scenario{} -> scenario
outline = %ScenarioOutline{} -> scenarios_for(outline)
end)
|> List.flatten()
}
end
@doc """
Changes a `Gherkin.Elements.ScenarioOutline` into multiple `Gherkin.Elements.Scenario`s
so that they may be executed in the same manner.
Given an outline, its easy to run all scenarios:
outline = %Gherkin.Elements.ScenarioOutline{}
Gherkin.scenarios_for(outline) |> Enum.each(&run_scenario/1)
"""
def scenarios_for(%ScenarioOutline{
name: name,
tags: tags,
steps: steps,
examples: examples,
line: line
}) do
examples
|> Enum.with_index(1)
|> Enum.map(fn {example, index} ->
%Scenario{
name: name <> " (Example #{index})",
tags: tags,
line: line,
steps:
Enum.map(steps, fn step ->
%{
step
| text:
Enum.reduce(example, step.text, fn {k, v}, t ->
String.replace(t, ~r/<#{k}>/, v)
end)
}
end)
}
end)
end
end