Skip to content

Commit

Permalink
[main] - Fix issue where quotes basically didn't parse anything insid…
Browse files Browse the repository at this point in the history
…e of them - TT
  • Loading branch information
Tyler-Keith-Thompson committed Mar 13, 2023
1 parent b78e072 commit 9549a98
Show file tree
Hide file tree
Showing 15 changed files with 119 additions and 54 deletions.
2 changes: 1 addition & 1 deletion CucumberSwift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'CucumberSwift'
s.version = '4.1.1'
s.version = '4.2.0'
s.summary = 'A lightweight swift only cucumber implementation.'

s.description = <<-DESC
Expand Down
8 changes: 8 additions & 0 deletions CucumberSwift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@
CAAD49C1266368D6007B6E6E /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = CAAD49C0266368D6007B6E6E /* .swiftlint.yml */; };
CAB20B3C24CCF5C10024C703 /* RuleDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB20B3B24CCF5C10024C703 /* RuleDSL.swift */; };
CAB20B3E24CCF6B20024C703 /* DSLRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB20B3D24CCF6B20024C703 /* DSLRuleTests.swift */; };
CAB40A5729BEE22900A01980 /* StubGeneratorToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB40A5629BEE22900A01980 /* StubGeneratorToken.swift */; };
CAB40A5929BEE28700A01980 /* StubGeneratorLexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB40A5829BEE28700A01980 /* StubGeneratorLexer.swift */; };
CADD26DD265B1D1F00EE8707 /* JSONReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADD26DC265B1D1F00EE8707 /* JSONReporter.swift */; };
CAEE41EE28B324BF000D4703 /* CucumberSwiftExpressions in Frameworks */ = {isa = PBXBuildFile; productRef = CAEE41ED28B324BF000D4703 /* CucumberSwiftExpressions */; };
CAF87A55281453EF00F7946A /* CucumberSwift.docc in Sources */ = {isa = PBXBuildFile; fileRef = CAF87A54281453EF00F7946A /* CucumberSwift.docc */; };
Expand Down Expand Up @@ -240,6 +242,8 @@
CAAD49C0266368D6007B6E6E /* .swiftlint.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = "<group>"; };
CAB20B3B24CCF5C10024C703 /* RuleDSL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleDSL.swift; sourceTree = "<group>"; };
CAB20B3D24CCF6B20024C703 /* DSLRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DSLRuleTests.swift; sourceTree = "<group>"; };
CAB40A5629BEE22900A01980 /* StubGeneratorToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubGeneratorToken.swift; sourceTree = "<group>"; };
CAB40A5829BEE28700A01980 /* StubGeneratorLexer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubGeneratorLexer.swift; sourceTree = "<group>"; };
CABED02C256A17F2001E92C1 /* CucumberSwift.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = CucumberSwift.xctestplan; sourceTree = "<group>"; };
CADD26DC265B1D1F00EE8707 /* JSONReporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONReporter.swift; sourceTree = "<group>"; };
CAF51EA42557B1C70095A5C4 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -322,6 +326,8 @@
children = (
AD01723021166C3D00FBC9AF /* StubGenerator.swift */,
AD017233211671D200FBC9AF /* Method.swift */,
CAB40A5629BEE22900A01980 /* StubGeneratorToken.swift */,
CAB40A5829BEE28700A01980 /* StubGeneratorLexer.swift */,
);
path = StubGeneration;
sourceTree = "<group>";
Expand Down Expand Up @@ -868,6 +874,7 @@
1DBF9329239C97AC0035722A /* RuleParser.swift in Sources */,
1DE862E02391F7F70027326A /* Position.swift in Sources */,
CA09729D264F517200573DA5 /* CucumberTestObservable.swift in Sources */,
CAB40A5929BEE28700A01980 /* StubGeneratorLexer.swift in Sources */,
AD7F1C0E2104DB240004852A /* Language.swift in Sources */,
AD99DB59213269D500E57823 /* StepImplementation.swift in Sources */,
CA4E2D0324CCDDDC00A44983 /* BackgroundDSL.swift in Sources */,
Expand All @@ -883,6 +890,7 @@
ADAA738E20A9356000D2344C /* CollectionExtensions.swift in Sources */,
1DA761A2239A7A74001CBA5B /* ASTToken.swift in Sources */,
AD9EB47B2079CFA1002494C0 /* Cucumber.swift in Sources */,
CAB40A5729BEE22900A01980 /* StubGeneratorToken.swift in Sources */,
AD9EB4832079D606002494C0 /* Scenario.swift in Sources */,
CA1C47E72634C671008D8E12 /* Hook.swift in Sources */,
AD01723121166C3D00FBC9AF /* StubGenerator.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ precedencegroup MightBePrecedence {
}

infix operator ?= : MightBePrecedence
internal func ?=<T> ( lhs: inout T, rhs: T?) {
internal func ?= <T> ( lhs: inout T, rhs: T?) {
if let r = rhs {
lhs = r
}
Expand Down
1 change: 0 additions & 1 deletion Sources/CucumberSwift/Extensions/CharacterExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ extension Character {
isNewline ||
isTagMarker ||
isQuote ||
isNumeric ||
isTableCellDelimiter ||
isHeaderToken ||
isEscapeCharacter
Expand Down
4 changes: 1 addition & 3 deletions Sources/CucumberSwift/Gherkin/Lexer/Lexer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ public class Lexer: StringReader {
} else {
return advance(.match(position, "\(char)" + readLineUntil { $0.isSymbol }))
}
case _ where char.isNumeric: return .integer(position, readLineUntil { !$0.isNumeric })
case _ where lastKeyword != nil: return .match(position, readLineUntil { $0.isSymbol })
default: return advance(advanceToNextToken())
}
Expand Down Expand Up @@ -189,8 +188,7 @@ public class Lexer: StringReader {
return advance(.docString(position, DocString(literal: docStringValues.dropFirst().joined(separator: "\n"),
contentType: docStringValues.first?.trimmingCharacters(in: .whitespacesAndNewlines))))
} else if char == .quote {
let str = advance(readLineUntil { $0.isQuote })
return advance(.string(position, str))
return advance(.match(position, "\(Character.quote)"))
}
return nil
}
Expand Down
22 changes: 0 additions & 22 deletions Sources/CucumberSwift/Gherkin/Lexer/Token.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ extension Sequence where Element == Lexer.Token {
extension Lexer {
indirect enum Token: Equatable, Hashable {
case newLine(Lexer.Position)
case integer(Lexer.Position, String)
case string(Lexer.Position, String)
case docString(Lexer.Position, DocString)
case match(Lexer.Position, String)
case title(Lexer.Position, String)
Expand All @@ -45,8 +43,6 @@ extension Lexer {
var position: Lexer.Position {
switch self {
case .newLine(let pos): return pos
case .integer(let pos, _): return pos
case .string(let pos, _): return pos
case .docString(let pos, _): return pos
case .match(let pos, _): return pos
case .title(let pos, _): return pos
Expand All @@ -71,10 +67,6 @@ extension Lexer {
return description1 == description2
case let (.tag(_, tag1), .tag(_, tag2)):
return tag1 == tag2
case let (.integer(_, num1), .integer(_, num2)):
return num1 == num2
case let (.string(_, string1), .string(_, string2)):
return string1 == string2
case let (.docString(_, string1), .docString(_, string2)):
return string1.literal == string2.literal
case let (.tableHeader(_, tableHeader1), .tableHeader(_, tableHeader2)):
Expand All @@ -89,8 +81,6 @@ extension Lexer {
var valueDescription: String {
switch self {
case .newLine: return "\n"
case .integer(_, let val): return "\(val)"
case .string(_, let val): return "\(val)"
case .docString(_, let val): return "\(val)"
case .match(_, let val): return "\(val)"
case .title(_, let val): return "\(val)"
Expand Down Expand Up @@ -122,18 +112,6 @@ extension Lexer {
}
return false
}
func isString() -> Bool {
if case .string = self {
return true
}
return false
}
func isInteger() -> Bool {
if case .integer = self {
return true
}
return false
}
func isDescription() -> Bool {
if case .description = self {
return true
Expand Down
4 changes: 0 additions & 4 deletions Sources/CucumberSwift/Gherkin/Parser/Step.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ public class Step: CustomStringConvertible {
keyword = kw
} else if case Lexer.Token.match(_, let m) = token {
match += m
} else if case Lexer.Token.string(_, let s) = token {
match += "\"\(s)\""
} else if case Lexer.Token.integer(_, let n) = token {
match += n
} else if case Lexer.Token.tableHeader(_, let h) = token {
match += h
} else if case Lexer.Token.docString(_, let s) = token {
Expand Down
2 changes: 1 addition & 1 deletion Sources/CucumberSwift/Runner/Cucumber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
import XCTest
import CucumberSwiftExpressions

@objc public class Cucumber: NSObject {
@objc public class Cucumber: NSObject { // swiftlint:disable:this type_body_length
static var shared = Cucumber()

var features = [Feature]()
Expand Down
2 changes: 1 addition & 1 deletion Sources/CucumberSwift/Runner/CucumberTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ open class CucumberTest: XCTestCase {
scenario
.steps
.lazy
.compactMap { step -> (step: Step, XCTestCase.Type, Selector)? in
.compactMap { step -> (step: Step, XCTestCase.Type, Selector)? in // swiftlint:disable:this large_tuple
if let (testCase, methodSelector) = TestCaseGenerator.initWith(className: className.appending(scenario.title.toClassString()),
method: step.method) {
return (step, testCase, methodSelector)
Expand Down
15 changes: 8 additions & 7 deletions Sources/CucumberSwift/StubGeneration/StubGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

import Foundation
enum StubGenerator {
private static func regexForTokens(_ tokens: [Lexer.Token]) -> String {
private static func regexForTokens(_ tokens: [Token]) -> String {
var regex = ""
for token in tokens {
if case Lexer.Token.match(_, let m) = token {
if case .match(let m) = token {
regex += NSRegularExpression
.escapedPattern(for: m)
} else if case Lexer.Token.string = token {
} else if case .string = token {
regex += "\\\"(.*?)\\\""
} else if case Lexer.Token.integer = token {
} else if case .int = token {
regex += "(\\d+)"
}
}
Expand All @@ -37,9 +37,10 @@ enum StubGenerator {
let methods = executableSteps
.filter { !$0.canExecute }
.reduce(into: [(step: Step, method: Method)]()) {
let regex = regexForTokens($1.tokens)
let stringCount = $1.tokens.filter { $0.isString() }.count
let integerCount = $1.tokens.filter { $0.isInteger() }.count
let tokens = StubGenerator.Lexer($1.match).lex()
let regex = regexForTokens(tokens)
let stringCount = tokens.filter { $0.isString() }.count
let integerCount = tokens.filter { $0.isInteger() }.count
let matchesParameter = (stringCount > 0 || integerCount > 0) ? "matches" : "_"
let variables = [
(type: "string", count: stringCount),
Expand Down
43 changes: 43 additions & 0 deletions Sources/CucumberSwift/StubGeneration/StubGeneratorLexer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// StubGeneratorLexer.swift
// CucumberSwift
//
// Created by Tyler Thompson on 3/12/23.
// Copyright © 2023 Tyler Thompson. All rights reserved.
//

import Foundation
extension StubGenerator {
public class Lexer: StringReader {
override internal init(_ str: String) {
super.init(str)
}

@discardableResult private func advance<T>(_ t: @autoclosure () -> T) -> T {
advanceIndex()
return t()
}

internal func advanceToNextToken() -> Token? {
guard let char = currentChar else { return nil }

switch char {
case .quote:
let str = advance(readUntil { $0.isQuote })
return advance(.string(value: str))
case _ where char.isNumeric:
let allIntegerValues = readUntil { !$0.isNumeric }
return .int(value: allIntegerValues)
default: return .match(value: readUntil { $0.isQuote || $0.isNumeric })
}
}

internal func lex() -> [Token] {
var toks = [Token]()
while let tok = advanceToNextToken() {
toks.append(tok)
}
return toks
}
}
}
31 changes: 31 additions & 0 deletions Sources/CucumberSwift/StubGeneration/StubGeneratorToken.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// StubGeneratorToken.swift
// CucumberSwift
//
// Created by Tyler Thompson on 3/12/23.
// Copyright © 2023 Tyler Thompson. All rights reserved.
//

import Foundation

extension StubGenerator {
enum Token {
case match(value: String)
case string(value: String)
case int(value: String)

func isString() -> Bool {
if case .string = self {
return true
}
return false
}

func isInteger() -> Bool {
if case .int = self {
return true
}
return false
}
}
}
4 changes: 0 additions & 4 deletions Tests/CucumberSwiftTests/CucumberTests/CucumberTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ extension Collection where Element == Lexer.Token {
public var text: String {
return compactMap { (token) -> String? in
switch token {
case .integer(_, let t):
return t
case .string(_, let t):
return "\"\(t)\""
case .match(_, let t):
return t
case .tableHeader(_, let t):
Expand Down
15 changes: 15 additions & 0 deletions Tests/CucumberSwiftTests/Gherkin/TableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,21 @@ class TableTests: XCTestCase {
XCTAssertEqual(firstScenario?.title, "the u|o (example 1)")
}

func testTableCellInQuotes() {
let cucumber = Cucumber(withString: """
Feature: Some terse yet descriptive text of what is desired
Scenario Outline: the "<one>"
Given the "<two>"
Examples:
| one | two |
| u\\|o | dos |
""")
let firstScenario = cucumber.features.first?.scenarios.first
XCTAssertEqual(firstScenario?.title, "the \"u|o\" (example 1)")
XCTAssertEqual(firstScenario?.steps.first?.match, "the \"dos\"")
}

func testTableGetAttachedToSteps() {
Cucumber.shared.features.removeAll()
Cucumber.shared.parseIntoFeatures("""
Expand Down
18 changes: 9 additions & 9 deletions Tests/CucumberSwiftTests/Reporter/ReporterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class ReporterTests: XCTestCase {
XCTAssertEqual(scenarios?.first?["name"] as? String, "S1")
XCTAssertEqual(scenarios?.first?["description"] as? String, "")
}

func testStepsAreWrittenToFile() throws {
let reporter = try XCTUnwrap(Cucumber.shared.reporters.compactMap { $0 as? CucumberJSONReporter }.first)
Feature("F1") {
Expand All @@ -70,7 +70,7 @@ class ReporterTests: XCTestCase {
}
reporter.testSuiteStarted(at: Date())
Cucumber.shared.executeFeatures()

let actual = try XCTUnwrap(try JSONSerialization.jsonObject(with: JSONEncoder().encode(reporter.features)) as? [[AnyHashable: Any]])
XCTAssertEqual(actual.count, 1)
let scenarios = actual.first?["elements"] as? [[AnyHashable: Any]]
Expand All @@ -82,7 +82,7 @@ class ReporterTests: XCTestCase {
let result = steps?.first?["result"] as? [AnyHashable: Any]
XCTAssertEqual(result?["status"] as? String, "passed")
}

func testFailingStepsAreWrittenToFile() throws {
enum Err: Error { case e1 }
let reporter = try XCTUnwrap(Cucumber.shared.reporters.compactMap { $0 as? CucumberJSONReporter }.first)
Expand All @@ -94,7 +94,7 @@ class ReporterTests: XCTestCase {
reporter.didStart(scenario: scenario, at: Date())
reporter.didStart(step: step, at: Date())
reporter.didFinish(step: step, result: .failed(Err.e1.localizedDescription), duration: .init(value: 1, unit: .seconds))

let actual = try XCTUnwrap(try JSONSerialization.jsonObject(with: JSONEncoder().encode(reporter.features)) as? [[AnyHashable: Any]])
XCTAssertEqual(actual.count, 1)
let scenarios = actual.first?["elements"] as? [[AnyHashable: Any]]
Expand All @@ -109,19 +109,19 @@ class ReporterTests: XCTestCase {
let actualDuration = try XCTUnwrap(result?["duration"] as? Double)
XCTAssertEqual(actualDuration, 1_000_000_000, accuracy: 0.9)
}

func testPendingStepsAreWrittenToFile() throws {
let reporter = try XCTUnwrap(Cucumber.shared.reporters.compactMap { $0 as? CucumberJSONReporter }.first)

let step = Given(I: print(""))
let scenario = Scenario("S1") { step }
let feature = Feature("F1") { scenario }

reporter.testSuiteStarted(at: Date())
reporter.didStart(feature: feature, at: Date())
reporter.didStart(scenario: scenario, at: Date())
reporter.didStart(step: step, at: Date())

let actual = try XCTUnwrap(try JSONSerialization.jsonObject(with: JSONEncoder().encode(reporter.features)) as? [[AnyHashable: Any]])
XCTAssertEqual(actual.count, 1)
let scenarios = actual.first?["elements"] as? [[AnyHashable: Any]]
Expand All @@ -133,7 +133,7 @@ class ReporterTests: XCTestCase {
let result = steps?.first?["result"] as? [AnyHashable: Any]
XCTAssertEqual(result?["status"] as? String, "pending")
}

func testReporterJsonConformsToCucumberJsonSchema() throws {
let path = URL(fileURLWithPath: #file)
.deletingLastPathComponent()
Expand Down

0 comments on commit 9549a98

Please sign in to comment.