Skip to content

Commit

Permalink
Merge pull request #92 from RockLobster/feature/docstring_rawliteral
Browse files Browse the repository at this point in the history
Exposing a rawLiteral on DocString for situation where sanitization is too eager
  • Loading branch information
Tyler-Keith-Thompson authored Jun 28, 2023
2 parents 4ded8ae + 27eb53b commit 245a339
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 6 deletions.
27 changes: 21 additions & 6 deletions Sources/CucumberSwift/Gherkin/Lexer/Lexer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,18 @@ public class Lexer: StringReader {
}
}

@discardableResult internal func readDocString(_ evaluation: ((Character) -> Bool)) -> String {
@discardableResult internal func readDocString(
_ evaluation: ((Character) -> Bool)
) -> (docString: String, rawDocString: String) {
var str = ""
var rawStr = ""
while let char = currentChar {
if char.isEscapeCharacter,
let next = nextChar,
next.isDocStringLiteral {
str.append(next)
rawStr.append(char)
rawStr.append(next)
advanceIndex()
advanceIndex()
continue
Expand All @@ -90,9 +95,10 @@ public class Lexer: StringReader {
break
}
str.append(char)
rawStr.append(char)
advanceIndex()
}
return str
return (str, rawStr)
}

@discardableResult internal func stripSpaceIfNecessary() -> Bool {
Expand Down Expand Up @@ -159,13 +165,16 @@ public class Lexer: StringReader {
let open = lookAheadAtLineUntil { !$0.isDocStringLiteral }
if open.isDocStringLiteral() {
readLineUntil { !$0.isDocStringLiteral }
let docStringValues = readDocString {

let (docString, rawDocString) = readDocString {
if $0.isDocStringLiteral {
let close = lookAheadAtLineUntil { !$0.isDocStringLiteral }
if close == open { return true }
}
return false
}.components(separatedBy: "\n")
}

let docStringValues = docString.components(separatedBy: "\n")
.enumerated()
.reduce(into: (whitespaceCount: 0, trimmedLines: [String]())) { res, e in
let (offset, line) = e
Expand All @@ -181,8 +190,14 @@ public class Lexer: StringReader {
.dropLast { $0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }

readLineUntil { !$0.isDocStringLiteral }
return advance(.docString(position, DocString(literal: docStringValues.dropFirst().joined(separator: "\n"),
contentType: docStringValues.first?.trimmingCharacters(in: .whitespacesAndNewlines))))
return advance(.docString(
position,
DocString(
rawLiteral: rawDocString,
literal: docStringValues.dropFirst().joined(separator: "\n"),
contentType: docStringValues.first?.trimmingCharacters(in: .whitespacesAndNewlines)
)
))
} else if char == .quote {
return advance(.match(position, "\(Character.quote)"))
}
Expand Down
1 change: 1 addition & 0 deletions Sources/CucumberSwift/Gherkin/Parser/DocString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import Foundation
public struct DocString: Hashable {
public let rawLiteral: String
public var literal: String
public var contentType: String?
}
26 changes: 26 additions & 0 deletions Tests/CucumberSwiftTests/Gherkin/DocstringTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,30 @@ class DocstringTests: XCTestCase {
third line
""")
}

func testDocStringPreservesEscapedQuotesWithinJSONString() throws {
let cucumber = Cucumber(withString:
#"""
Feature: DocString variations
Scenario: minimalistic
Given a DocString with JSON with escaped quotes in a string
"""
{
"stringWithEscapedQuote":"String with a \"quote\" or two"
}
"""
"""#
)
let firstStep = cucumber.features.first?.scenarios.first?.steps.first
let jsonString = try XCTUnwrap(firstStep?.docString?.rawLiteral)
let jsonData = try XCTUnwrap(jsonString.data(using: .utf8))

struct MyJsonType: Decodable {
let stringWithEscapedQuote: String
}

let jsonObject = try JSONDecoder().decode(MyJsonType.self, from: jsonData)
XCTAssertEqual(jsonObject.stringWithEscapedQuote, "String with a \"quote\" or two")
}
}

0 comments on commit 245a339

Please sign in to comment.