require 'cucumber/cucumber_expressions/cucumber_expression_generator' require 'cucumber/cucumber_expressions/cucumber_expression' require 'cucumber/cucumber_expressions/parameter_type' require 'cucumber/cucumber_expressions/parameter_type_registry' module Cucumber module CucumberExpressions describe CucumberExpressionGenerator do class Currency end before do @parameter_type_registry = ParameterTypeRegistry.new @generator = CucumberExpressionGenerator.new(@parameter_type_registry) end it "documents expression generation" do parameter_registry = ParameterTypeRegistry.new ### [generate-expression] generator = CucumberExpressionGenerator.new(parameter_registry) undefined_step_text = "I have 2 cucumbers and 1.5 tomato" generated_expression = generator.generate_expressions(undefined_step_text)[0] expect(generated_expression.source).to eq("I have {int} cucumbers and {float} tomato") expect(generated_expression.parameter_types[1].type).to eq(Float) ### [generate-expression] end it "generates expression for no args" do assert_expression("hello", [], "hello") end it "generates expression with escaped left parenthesis" do assert_expression( "\\(iii)", [], "(iii)") end it "generates expression with escaped left curly brace" do assert_expression( "\\{iii}", [], "{iii}") end it "generates expression with escaped slashes" do assert_expression( "The {int}\\/{int}\\/{int} hey", ["int", "int2", "int3"], "The 1814/05/17 hey") end it "generates expression for int float arg" do assert_expression( "I have {int} cukes and {float} euro", ["int", "float"], "I have 2 cukes and 1.5 euro") end it "generates expression for strings" do assert_expression( "I like {string} and {string}", ["string", "string2"], 'I like "bangers" and \'mash\'') end it "generates expression with % sign" do assert_expression( "I am {int}% foobar", ["int"], 'I am 20% foobar') end it "generates expression for just int" do assert_expression( "{int}", ["int"], "99999") end it "numbers only second argument when builtin type is not reserved keyword" do assert_expression( "I have {int} cukes and {int} euro", ["int", "int2"], "I have 2 cukes and 5 euro") end it "numbers only second argument when type is not reserved keyword" do @parameter_type_registry.define_parameter_type(ParameterType.new( 'currency', '[A-Z]{3}', Currency, lambda {|s| Currency.new(s)}, true, true )) assert_expression( "I have a {currency} account and a {currency} account", ["currency", "currency2"], "I have a EUR account and a GBP account") end it "exposes parameters in generated expression" do expression = @generator.generate_expressions("I have 2 cukes and 1.5 euro")[0] types = expression.parameter_types.map(&:type) expect(types).to eq([Integer, Float]) end it "matches parameter types with optional capture groups" do @parameter_type_registry.define_parameter_type(ParameterType.new( 'optional-flight', /(1st flight)?/, String, lambda {|s| s}, true, false )) @parameter_type_registry.define_parameter_type(ParameterType.new( 'optional-hotel', /(1 hotel)?/, String, lambda {|s| s}, true, false )) expression = @generator.generate_expressions("I reach Stage 4: 1st flight -1 hotel")[0] # While you would expect this to be `I reach Stage {int}: {optional-flight} -{optional-hotel}` # the `-1` causes {int} to match just before {optional-hotel}. expect(expression.source).to eq("I reach Stage {int}: {optional-flight} {int} hotel") end it "generates at most 256 expressions" do for i in 0..3 @parameter_type_registry.define_parameter_type(ParameterType.new( "my-type-#{i}", /([a-z] )*?[a-z]/, String, lambda {|s| s}, true, false )) end # This would otherwise generate 4^11=4194300 expressions and consume just shy of 1.5GB. expressions = @generator.generate_expressions("a s i m p l e s t e p") expect(expressions.length).to eq(256) end it "prefers expression with longest non empty match" do @parameter_type_registry.define_parameter_type(ParameterType.new( 'zero-or-more', /[a-z]*/, String, lambda {|s| s}, true, false )) @parameter_type_registry.define_parameter_type(ParameterType.new( 'exactly-one', /[a-z]/, String, lambda {|s| s}, true, false )) expressions = @generator.generate_expressions("a simple step") expect(expressions.length).to eq(2) expect(expressions[0].source).to eq("{exactly-one} {zero-or-more} {zero-or-more}") expect(expressions[1].source).to eq("{zero-or-more} {zero-or-more} {zero-or-more}") end context "does not suggest parameter when match is" do before do @parameter_type_registry.define_parameter_type(ParameterType.new( 'direction', /(up|down)/, String, lambda {|s| s}, true, false )) end it "at the beginning of a word" do expect(@generator.generate_expressions("When I download a picture")[0].source).not_to eq("When I {direction}load a picture") expect(@generator.generate_expressions("When I download a picture")[0].source).to eq("When I download a picture") end it "inside a word" do expect(@generator.generate_expressions("When I watch the muppet show")[0].source).not_to eq("When I watch the m{direction}pet show") expect(@generator.generate_expressions("When I watch the muppet show")[0].source).to eq("When I watch the muppet show") end it "at the end of a word" do expect(@generator.generate_expressions("When I create a group")[0].source).not_to eq("When I create a gro{direction}") expect(@generator.generate_expressions("When I create a group")[0].source).to eq("When I create a group") end end context "does suggest parameter when match is" do before do @parameter_type_registry.define_parameter_type(ParameterType.new( 'direction', /(up|down)/, String, lambda {|s| s}, true, false )) end it "a full word" do expect(@generator.generate_expressions("When I go down the road")[0].source).to eq("When I go {direction} the road") expect(@generator.generate_expressions("When I walk up the hill")[0].source).to eq("When I walk {direction} the hill") expect(@generator.generate_expressions("up the hill, the road goes down")[0].source).to eq("{direction} the hill, the road goes {direction}") end it 'wrapped around punctuation characters' do expect(@generator.generate_expressions("When direction is:down")[0].source).to eq("When direction is:{direction}") expect(@generator.generate_expressions("Then direction is down.")[0].source).to eq("Then direction is {direction}.") end end def assert_expression(expected_expression, expected_argument_names, text) generated_expression = @generator.generate_expressions(text)[0] expect(generated_expression.parameter_names).to eq(expected_argument_names) expect(generated_expression.source).to eq(expected_expression) cucumber_expression = CucumberExpression.new(generated_expression.source, @parameter_type_registry) match = cucumber_expression.match(text) if match.nil? raise "Expected text '#{text}' to match generated expression '#{generated_expression.source}'" end expect(match.length).to eq(expected_argument_names.length) end end end end