Files
2024-08-20 18:28:42 +00:00

161 lines
4.3 KiB
Swift

import Accessibility
import CustomDump
@preconcurrency import InlineSnapshotTesting
import IssueReporting
import XCTest
class BaseIntegrationTests: XCTestCase {
@MainActor
var app: XCUIApplication!
var logs: XCUIElement!
private var _expectRuntimeWarnings: (filePath: StaticString, line: UInt)?
func expectRuntimeWarnings(filePath: StaticString = #filePath, line: UInt = #line) {
self._expectRuntimeWarnings = (filePath, line)
}
override func invokeTest() {
withSnapshotTesting {
super.invokeTest()
}
}
@MainActor
override func setUp() async throws {
// SnapshotTesting.isRecording = true
// self.continueAfterFailure = false
self.app = XCUIApplication()
self.app.launchEnvironment["UI_TEST"] = "true"
self.app.launch()
self.app.activate()
self.logs = self.app.staticTexts["composable-architecture.debug.logs"]
}
@MainActor
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,
"\(self.name) emitted an unexpected runtime warning"
)
}
}
@MainActor
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")!)
}
@MainActor
func assertLogs(
_ logConfiguration: LogConfiguration = .unordered,
matches expectedLogs: (() -> String)? = nil,
fileID: StaticString = #fileID,
filePath: StaticString = #filePath,
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")
}
withExpectedIssue(isIntermittent: true) {
assertInlineSnapshot(
of: logs,
as: ._lines,
matches: expectedLogs,
fileID: fileID,
file: filePath,
function: function,
line: line,
column: column
)
}
}
}
enum LogConfiguration {
case exact
case unordered
}
extension Snapshotting<String, String> {
fileprivate static nonisolated(unsafe) let _lines = Snapshotting(
pathExtension: "txt",
diffing: Diffing(
toData: { Data($0.utf8) },
fromData: { String(decoding: $0, as: UTF8.self) }
) { old, new in
guard old != new else { return nil }
let newLines = new.split(separator: "\n", omittingEmptySubsequences: false)
let oldLines = old.split(separator: "\n", omittingEmptySubsequences: false)
let difference = newLines.difference(from: oldLines)
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 oldLine = 0
var newLine = 0
while oldLine < oldLines.count || newLine < newLines.count {
if let removal = removals[oldLine] {
result += "\(oldPrefix) \(removal)\n"
oldLine += 1
} else if let insertion = insertions[newLine] {
result += "\(newPrefix) \(insertion)\n"
newLine += 1
} else {
result += "\(prefix) \(oldLines[oldLine])\n"
oldLine += 1
newLine += 1
}
}
let attachment = XCTAttachment(
data: Data(result.utf8),
uniformTypeIdentifier: "public.patch-file"
)
return (result, [attachment])
}
)
}
private let oldPrefix = "\u{2212}"
private let newPrefix = "+"
private let prefix = "\u{2007}"