Files
swift-composable-architectu…/Examples/Integration/IntegrationUITests/Internal/BaseIntegrationTests.swift
2023-10-09 19:06:59 +00:00

141 lines
3.9 KiB
Swift

import CustomDump
import InlineSnapshotTesting
import XCTest
class BaseIntegrationTests: XCTestCase {
var app: XCUIApplication!
var logs: XCUIElement!
private var _expectRuntimeWarnings: (file: StaticString, line: UInt)?
func expectRuntimeWarnings(file: StaticString = #file, line: UInt = #line) {
self._expectRuntimeWarnings = (file, line)
}
override func setUp() {
// SnapshotTesting.isRecording = true
// self.continueAfterFailure = false
self.app = XCUIApplication()
self.app.launchEnvironment["UI_TEST"] = "true"
self.app.launch()
self.logs = self.app.staticTexts["composable-architecture.debug.logs"]
}
override func tearDown() {
super.tearDown()
if let (file, line) = self._expectRuntimeWarnings {
XCTAssert(
self.app.staticTexts["Runtime warning"].waitForExistence(timeout: 1),
"Expected runtime warning(s)",
file: file,
line: line
)
} else {
XCTAssertFalse(self.app.staticTexts["Runtime warning"].exists)
}
SnapshotTesting.isRecording = false
}
func clearLogs() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
let alert = XCUIApplication(bundleIdentifier: "com.apple.springboard").alerts
let open = alert.buttons["Open"]
if alert.firstMatch.waitForExistence(timeout: 0.3),
open.waitForExistence(timeout: 0.3)
{
alert.buttons["Open"].tap()
}
}
XCUIDevice.shared.system.open(URL(string: "integration:///clear-logs")!)
}
func assertLogs(
_ logConfiguration: LogConfiguration = .unordered,
matches expectedLogs: (() -> String)? = nil,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
column: UInt = #column
) {
defer { self.clearLogs() }
let logs: String
switch logConfiguration {
case .exact:
logs = self.logs.label
case .unordered:
logs = self.logs.label.split(separator: "\n").sorted().joined(separator: "\n")
}
assertInlineSnapshot(
of: logs,
as: ._lines,
matches: expectedLogs,
file: file,
function: function,
line: line,
column: column
)
}
}
enum LogConfiguration {
case exact
case unordered
}
extension Snapshotting where Value == String, Format == String {
fileprivate static let _lines = Snapshotting(
pathExtension: "txt",
diffing: Diffing(
toData: { Data($0.utf8) },
fromData: { String(decoding: $0, as: UTF8.self) }
) { actual, expected in
guard expected != actual else { return nil }
let actualLines = actual.split(separator: "\n", omittingEmptySubsequences: false)
let expectedLines = expected.split(separator: "\n", omittingEmptySubsequences: false)
let difference = actualLines.difference(from: expectedLines)
var result = ""
var insertions = [Int: Substring]()
var removals = [Int: Substring]()
for change in difference {
switch change {
case let .insert(offset, element, _):
insertions[offset] = element
case let .remove(offset, element, _):
removals[offset] = element
}
}
var expectedLine = 0
var actualLine = 0
while expectedLine < expectedLines.count || actualLine < actualLines.count {
if let removal = removals[expectedLine] {
result += "\(expectedPrefix) \(removal)\n"
expectedLine += 1
} else if let insertion = insertions[actualLine] {
result += "\(actualPrefix) \(insertion)\n"
actualLine += 1
} else {
result += "\(prefix) \(expectedLines[expectedLine])\n"
expectedLine += 1
actualLine += 1
}
}
let attachment = XCTAttachment(
data: Data(result.utf8),
uniformTypeIdentifier: "public.patch-file"
)
return (result, [attachment])
}
)
}
private let expectedPrefix = "\u{2212}"
private let actualPrefix = "+"
private let prefix = "\u{2007}"