Skip to content

Commit

Permalink
Merge pull request #76 from Hsilgos/implement_ExecuteFirstStep
Browse files Browse the repository at this point in the history
Adds function ExecuteFirstStep to run step which matches given definition
  • Loading branch information
Tyler-Keith-Thompson authored Feb 12, 2023
2 parents f44796a + 5f54fd6 commit d415a8f
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/Tyler-Keith-Thompson/CucumberSwiftExpressions.git",
"state" : {
"revision" : "d6bcc190304d5096ae64b95bd6adc7a0342f1245",
"version" : "0.0.7"
"revision" : "5200dc2c5d7cd31f07a13f030ab0027224d2dbea",
"version" : "0.0.8"
}
},
{
Expand Down
11 changes: 7 additions & 4 deletions Sources/CucumberSwift/Gherkin/Parser/Step.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ public class Step: CustomStringConvertible {
public private(set) var location: Lexer.Position
public internal(set) var testCase: XCTestCase?

typealias MatchesExpression = ((_ str: String) -> Bool)
typealias Execute = ((_ match: String, _ steps: Step) throws -> Void)

var result: Reporter.Result = .pending
var execute: (() throws -> Void)?
var execute: Execute?
var executeSelector: Selector?
var executeClass: AnyClass?
var executeInstance: NSObject?
var regex: String = ""
var matchesExpression: MatchesExpression?
var errorMessage: String = ""
var startTime: Date?
var endTime: Date?
Expand Down Expand Up @@ -99,8 +102,8 @@ public class Step: CustomStringConvertible {
init(with execute: @escaping (([String], Step) -> Void), match: String?, position: Lexer.Position) {
location = position
self.match ?= match
self.execute = {
execute(self.match.matches(for: self.regex), self)
self.execute = { _, step in
execute(self.match.matches(for: ""), step)
}
}

Expand Down
124 changes: 65 additions & 59 deletions Sources/CucumberSwift/Runner/Cucumber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,99 +172,105 @@ import CucumberSwiftExpressions
.map { Feature(with: $0, uri: uri) })
}

func attachClosureToSteps(keyword: Step.Keyword? = nil, regex: String, callback: @escaping (([String], Step) throws -> Void), line: Int, file: StaticString) {
features
func executeFirstStep(keyword: Step.Keyword? = nil, matching: String) {
let firstMatchingStep = features
.flatMap { $0.scenarios.flatMap { $0.steps } }
.filter { step -> Bool in
.first {step -> Bool in
if let k = keyword,
step.keyword.contains(k) {
return !step.match.matches(for: regex).isEmpty
return step.matchesExpression?(matching) == true
} else if keyword == nil {
return !step.match.matches(for: regex).isEmpty
return step.matchesExpression?(matching) == true
}
return false
}
.forEach { step in
step.result = .undefined
step.execute = { try callback(step.match.matches(for: step.regex), step) }
step.regex = regex
step.sourceLine = line
step.sourceFile = file
}

if let firstMatchingStep = firstMatchingStep {
XCTAssertNoThrow(try firstMatchingStep.execute?(matching, firstMatchingStep))
} else {
XCTFail("No CucumberSwift expression found that matches step '\(matching)'")
}
}

func attachClosureToSteps(keyword: Step.Keyword? = nil,
expression: CucumberExpression,
callback: @escaping ((CucumberSwiftExpressions.Match, Step) throws -> Void),
line: Int,
file: StaticString) {
private func attachClosureToSteps(keyword: Step.Keyword?,
execute: Step.Execute? = nil,
matchesExpression: @escaping Step.MatchesExpression,
line: Int,
file: StaticString,
executeSelector: Selector? = nil,
executeClass: AnyClass? = nil) {
features
.flatMap { $0.scenarios.flatMap { $0.steps } }
.filter { step -> Bool in
if let k = keyword,
step.keyword.contains(k) {
return expression.match(in: step.match) != nil
return matchesExpression(step.match)
} else if keyword == nil {
return expression.match(in: step.match) != nil
return matchesExpression(step.match)
}
return false
}
.forEach { step in
step.result = .undefined
step.execute = { try callback(try XCTUnwrap(expression.match(in: step.match)), step) }
step.execute = execute
step.matchesExpression = matchesExpression
step.sourceLine = line
step.sourceFile = file
step.executeSelector = executeSelector
step.executeClass = executeClass
}
}

func attachClosureToSteps(keyword: Step.Keyword? = nil,
regex: String,
callback: @escaping (([String], Step) throws -> Void),
line: Int,
file: StaticString) {
attachClosureToSteps(keyword: keyword,
execute: { match, step in try callback(match.matches(for: regex), step) },
matchesExpression: { str in !str.matches(for: regex).isEmpty },
line: line,
file: file)
}

func attachClosureToSteps(keyword: Step.Keyword? = nil,
expression: CucumberExpression,
callback: @escaping ((CucumberSwiftExpressions.Match, Step) throws -> Void),
line: Int,
file: StaticString) {
attachClosureToSteps(keyword: keyword,
execute: { match, step in try callback(try XCTUnwrap(expression.match(in: match)), step) },
matchesExpression: { str in expression.match(in: str) != nil },
line: line,
file: file)
}

#if compiler(>=5.7) && canImport(_StringProcessing)
@available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *)
func attachClosureToSteps<Output>(keyword: Step.Keyword? = nil,
regex: Regex<Output>,
callback: @escaping ((Regex<Output>.Match, Step) throws -> Void),
line: Int,
file: StaticString) {
features
.flatMap { $0.scenarios.flatMap { $0.steps } }
.filter { step -> Bool in
if let k = keyword,
step.keyword.contains(k) {
let match = try? regex.wholeMatch(in: step.match)
return match != nil
} else if keyword == nil {
let match = try? regex.wholeMatch(in: step.match)
return match != nil
}
return false
}
.forEach { step in
step.result = .undefined
step.execute = { try callback(try XCTUnwrap(regex.wholeMatch(in: step.match)), step) }
step.sourceLine = line
step.sourceFile = file
}
attachClosureToSteps(keyword: keyword,
execute: { match, step in try callback(try XCTUnwrap(regex.wholeMatch(in: match)), step) },
matchesExpression: { str in (try? regex.wholeMatch(in: str)) != nil },
line: line,
file: file)
}
#endif

func attachClosureToSteps(keyword: Step.Keyword? = nil, regex: String, class: AnyClass, selector: Selector, line: Int, file: StaticString) {
features
.flatMap { $0.scenarios.flatMap { $0.steps } }
.filter { step -> Bool in
if let k = keyword,
step.keyword.contains(k) {
return !step.match.matches(for: regex).isEmpty
} else if keyword == nil {
return !step.match.matches(for: regex).isEmpty
}
return false
}
.forEach { step in
step.result = .undefined
step.executeSelector = selector
step.executeClass = `class`
step.regex = regex
step.sourceLine = line
step.sourceFile = file
}
func attachClosureToSteps(keyword: Step.Keyword? = nil,
regex: String,
class: AnyClass,
selector: Selector,
line: Int,
file: StaticString) {
attachClosureToSteps(keyword: keyword,
matchesExpression: { str in !str.matches(for: regex).isEmpty },
line: line,
file: file,
executeSelector: selector,
executeClass: `class`)
}
}
2 changes: 1 addition & 1 deletion Sources/CucumberSwift/Runner/CucumberTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ extension Step {
instance.perform(selector)
}
} else {
try execute?()
try execute?(self.match, self)
}
if execute != nil && result != .failed {
result = .passed
Expand Down
4 changes: 4 additions & 0 deletions Sources/CucumberSwift/Runner/Globals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ public func BeforeStep(priority: UInt? = nil, closure: @escaping ((Step) -> Void
public func AfterStep(priority: UInt? = nil, closure: @escaping ((Step) -> Void)) {
Cucumber.shared.afterStepHooks.append(.init(priority: priority, hook: closure))
}
// Execute a step matching the given step definition
public func ExecuteFirstStep(keyword: Step.Keyword? = nil, matching: String) {
Cucumber.shared.executeFirstStep(keyword: keyword, matching: matching)
}
Loading

0 comments on commit d415a8f

Please sign in to comment.