diff --git a/CucumberSwift/Gherkin/Parser/Feature.swift b/CucumberSwift/Gherkin/Parser/Feature.swift index f355f4e5..341c3866 100644 --- a/CucumberSwift/Gherkin/Parser/Feature.swift +++ b/CucumberSwift/Gherkin/Parser/Feature.swift @@ -35,7 +35,7 @@ public class Feature : Taggable, Positionable { if let sn = node as? AST.ScenarioNode { scenarios.append(Scenario(with: sn, tags:tags, stepNodes: backgroundSteps)) } else if let son = node as? AST.ScenarioOutlineNode { - let generatedScenarios = ScenarioOutlineParser.parse(son, featureTags: tags, backgroundStepNodes: backgroundSteps) + let generatedScenarios = ScenarioOutlineParser.parse(son, featureTags: tags, backgroundStepNodes: backgroundSteps, uri: uri) scenarios.append(contentsOf: generatedScenarios) } else if let rn = node as? AST.RuleNode { scenarios.append(contentsOf: RuleParser.parse(rn, featureTags: tags, backgroundStepNodes: backgroundSteps)) diff --git a/CucumberSwift/Gherkin/Parser/ScenarioOutlineParser.swift b/CucumberSwift/Gherkin/Parser/ScenarioOutlineParser.swift index 86b20849..f38a739f 100644 --- a/CucumberSwift/Gherkin/Parser/ScenarioOutlineParser.swift +++ b/CucumberSwift/Gherkin/Parser/ScenarioOutlineParser.swift @@ -28,7 +28,7 @@ fileprivate extension Sequence where Element == Lexer.Token { } class ScenarioOutlineParser { - static func parse(_ scenarioOutlineNode:AST.ScenarioOutlineNode, featureTags:[String], backgroundStepNodes:[AST.StepNode]) -> [Scenario] { + static func parse(_ scenarioOutlineNode:AST.ScenarioOutlineNode, featureTags:[String], backgroundStepNodes:[AST.StepNode], uri:String = "") -> [Scenario] { let tags = featureTags.appending(contentsOf: scenarioOutlineNode.tokens.compactMap { if case Lexer.Token.tag(_, let tag) = $0 { return tag @@ -45,7 +45,9 @@ class ScenarioOutlineParser { stepNodes: scenarioOutlineNode .children .compactMap { $0 as? AST.StepNode }, - backgroundStepNodes: backgroundStepNodes) } + backgroundStepNodes: backgroundStepNodes, + uri: uri) + } } static func getExamplesFrom(_ scenarioOutlineNode:AST.ScenarioOutlineNode) -> [[Lexer.Token]] { @@ -54,9 +56,17 @@ class ScenarioOutlineParser { }.groupedByExample() } - private static func parseExample(titleLine: [Lexer.Token]?, tokens: [Lexer.Token], outlineTags:[String], stepNodes:[AST.StepNode], backgroundStepNodes:[AST.StepNode]) -> [Scenario] { + private static func validateTable(_ lines: [[Lexer.Token]], uri:String) { + guard let header = lines.first else { return } + if let _ = lines.first(where: { $0.count != header.count }) { + Gherkin.errors.append("File: \(uri) inconsistent cell count within the table") + } + } + + private static func parseExample(titleLine: [Lexer.Token]?, tokens: [Lexer.Token], outlineTags:[String], stepNodes:[AST.StepNode], backgroundStepNodes:[AST.StepNode], uri:String) -> [Scenario] { var scenarios = [Scenario]() let lines = tokens.filter{ $0.isTableCell() || $0.isNewline() }.groupedByLine() + validateTable(lines, uri: uri) let headerLookup:[String:Int]? = lines.first?.enumerated().reduce(into: [:]) { if case Lexer.Token.tableCell(_, let headerText) = $1.element { $0?[headerText] = $1.offset diff --git a/CucumberSwiftTests/Gherkin/ErrorTests.swift b/CucumberSwiftTests/Gherkin/ErrorTests.swift index 827e0c8a..dd6448fc 100644 --- a/CucumberSwiftTests/Gherkin/ErrorTests.swift +++ b/CucumberSwiftTests/Gherkin/ErrorTests.swift @@ -45,6 +45,26 @@ class ErrorsTests : XCTestCase { XCTAssert(Gherkin.errors.contains("File: unexpected_eof.feature unexpected end of file, expected: #TagLine, #ScenarioLine, #Comment, #Empty")) } + func testInconsistenCellCount() { + Cucumber.shared.parseIntoFeatures(""" + Feature: Inconsistent cell counts + + Scenario: minimalistic + Given a data table with inconsistent cell count + | foo | bar | + | boz | + + + Scenario Outline: minimalistic + Given the + + Examples: + | what | + | minimalism | extra | + """, uri: "inconsistent_cell_count.feature") + XCTAssert(Gherkin.errors.contains("File: inconsistent_cell_count.feature inconsistent cell count within the table")) + } + override func tearDown() { Gherkin.errors.removeAll() }