Automate App Store screenshots via XCUITests

Replace preview-based screenshot rendering with simulator XCUITests
driven by shooter.sh (per-language locale + status bar overrides,
xcparse extraction). Add batteries-list accessibility identifier and
update CLAUDE.md screenshot docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-22 10:39:14 +02:00
parent 8b30fabaa2
commit b448a1b4f7
6 changed files with 546 additions and 538 deletions

View File

@@ -125,32 +125,21 @@ final class CableUITestsScreenshot: XCTestCase {
try super.setUpWithError()
continueAfterFailure = false
XCUIDevice.shared.orientation = .portrait
//ensureDoNotDisturbEnabled()
//dismissSystemOverlays()
}
override func tearDownWithError() throws {
try super.tearDownWithError()
//dismissSystemOverlays()
}
// MARK: - Onboarding Screenshots
@MainActor
func testOnboardingScreenshots() throws {
let app = launchApp(arguments: ["--uitest-reset-data"])
waitForStability(long: true)
// Wait for Apple Intelligence and other system notifications to appear, then dismiss
RunLoop.current.run(until: Date().addingTimeInterval(6))
dismissNotificationBannersIfNeeded()
waitForStability(long: true)
dismissNotificationBannersIfNeeded()
waitForStability(long: true)
dismissNotificationBannersIfNeeded()
waitForStability(long: true)
dismissNotificationBannersIfNeeded()
let createSystemButton = app.buttons["create-system-button"]
XCTAssertTrue(createSystemButton.waitForExistence(timeout: 8))
waitForStability(long: true)
dismissNotificationBannersIfNeeded()
waitForStability(long: true)
waitForStability()
takeScreenshot(named: "01-OnboardingSystemsView")
createSystemButton.tap()
@@ -159,14 +148,15 @@ final class CableUITestsScreenshot: XCTestCase {
XCTAssertTrue(addLoadButton.waitForExistence(timeout: 8))
let browseLibraryButton = button(in: app.buttons, for: .browseLibrary)
XCTAssertTrue(browseLibraryButton.waitForExistence(timeout: 4))
waitForStability()
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "02-OnboardingSystemView")
browseLibraryButton.tap()
let libraryCloseButton = app.buttons["library-view-close-button"]
XCTAssertTrue(libraryCloseButton.waitForExistence(timeout: 8))
waitForStability(long: true)
// Wait for library items AND remote icons to load from PocketBase
RunLoop.current.run(until: Date().addingTimeInterval(15))
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "04-ComponentSelectorView")
libraryCloseButton.tap()
@@ -175,20 +165,130 @@ final class CableUITestsScreenshot: XCTestCase {
let newLoadButton = button(in: app.buttons, for: .defaultLoadName)
XCTAssertTrue(newLoadButton.waitForExistence(timeout: 8))
waitForStability(long: true)
waitForStability()
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "03-LoadEditorView")
}
// MARK: - Sample Data Screenshots
@MainActor
func testSampleDataScreenshots() throws {
let app = launchAppWithSampleData()
dismissNotificationBannersIfNeeded()
// Systems list
let systemsList = resolvedSystemsList(in: app)
XCTAssertTrue(systemsList.waitForExistence(timeout: 4))
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "05-SystemsWithSampleData")
// Navigate to first system
openFirstSystem(in: app, systemsList: systemsList)
// Overview tab wait for navigation animation to complete
waitForStability()
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "06-AdventureVanOverview")
// Bill of Materials
let bomElement = resolveBillOfMaterialsElement(in: app)
if !bomElement.waitForExistence(timeout: 6) {
bringElementIntoView(bomElement, in: app)
}
XCTAssertTrue(bomElement.exists)
if !bomElement.isHittable {
bringElementIntoView(bomElement, in: app, requireHittable: true)
}
tapElement(bomElement)
waitForStability()
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "08-BillOfMaterials")
let closeButton = app.buttons["system-bom-close-button"]
XCTAssertTrue(closeButton.waitForExistence(timeout: 6))
closeButton.tap()
// Components tab
tapTab(.componentsTab, in: app)
let loadsList = resolvedLoadsList(in: app)
XCTAssertTrue(loadsList.waitForExistence(timeout: 4))
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "07-AdventureVanLoads")
// Open first load Calculator
let firstLoad = loadsList.cells.element(boundBy: 0)
XCTAssertTrue(firstLoad.waitForExistence(timeout: 2))
firstLoad.tap()
waitForStability()
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "09-AdventureVanCalculator")
// Navigate back to system tabs
app.navigationBars.buttons.element(boundBy: 0).tap()
waitForStability()
// Batteries tab
tapTab(.batteriesTab, in: app)
waitForStability()
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "10-AdventureVanBatteries")
// Open first battery Battery Editor
let batteriesList = resolvedBatteriesList(in: app)
if batteriesList.waitForExistence(timeout: 4) {
let firstBattery = batteriesList.cells.element(boundBy: 0)
if firstBattery.waitForExistence(timeout: 2) {
firstBattery.tap()
waitForStability()
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "11-BatteryEditor")
// Navigate back
app.navigationBars.buttons.element(boundBy: 0).tap()
waitForStability()
}
}
// Chargers tab
tapTab(.chargersTab, in: app)
waitForStability()
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "12-AdventureVanChargers")
// Open first charger Charger Editor
let chargersList = resolvedChargersList(in: app)
if chargersList.waitForExistence(timeout: 4) {
let firstCharger = chargersList.cells.element(boundBy: 0)
if firstCharger.waitForExistence(timeout: 2) {
firstCharger.tap()
waitForStability()
dismissNotificationBannersIfNeeded()
takeScreenshot(named: "13-ChargerEditor")
}
}
}
// MARK: - App Launch
private func launchApp(arguments: [String]) -> XCUIApplication {
let app = XCUIApplication()
var launchArguments = ["--uitest-reset-data"]
launchArguments.append(contentsOf: arguments)
app.launchArguments = launchArguments
app.launch()
return app
}
private func launchAppWithSampleData() -> XCUIApplication {
let app = XCUIApplication()
app.launchArguments = ["--uitest-reset-data", "--uitest-sample-data"]
app.launch()
return app
}
let systemsList = resolvedSystemsList(in: app)
XCTAssertTrue(systemsList.waitForExistence(timeout: 4))
takeScreenshot(named: "05-SystemsWithSampleData")
// MARK: - Navigation Helpers
private func openFirstSystem(in app: XCUIApplication, systemsList: XCUIElement) {
let firstSystemCell = systemsList.cells.element(boundBy: 0)
XCTAssertTrue(firstSystemCell.waitForExistence(timeout: 2))
let systemName = firstSystemCell.staticTexts.firstMatch.label
@@ -206,69 +306,41 @@ final class CableUITestsScreenshot: XCTestCase {
detailVisible = waitForSystemDetail(named: systemName, in: app)
}
XCTAssertTrue(detailVisible)
takeScreenshot(named: "06-AdventureVanOverview")
// let overviewTab = app.buttons["overview-tab"]
// XCTAssertTrue(overviewTab.waitForExistence(timeout: 3))
// overviewTab.tap()
waitForStability(long: false)
let bomElement = resolveBillOfMaterialsElement(in: app)
if !bomElement.waitForExistence(timeout: 6) {
bringElementIntoView(bomElement, in: app)
}
XCTAssertTrue(bomElement.exists)
if !bomElement.isHittable {
bringElementIntoView(bomElement, in: app, requireHittable: true)
}
if bomElement.isHittable {
bomElement.tap()
} else {
bomElement.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
}
waitForStability(long: true)
takeScreenshot(named: "08-BillOfMaterials")
let closeButton = app.buttons["system-bom-close-button"]
XCTAssertTrue(closeButton.waitForExistence(timeout: 6))
closeButton.tap()
let componentsTab = componentsTabButton(in: app)
XCTAssertTrue(componentsTab.waitForExistence(timeout: 3))
if componentsTab.isHittable {
componentsTab.tap()
} else {
componentsTab.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
}
let loadsList = resolvedLoadsList(in: app)
XCTAssertTrue(loadsList.waitForExistence(timeout: 4))
takeScreenshot(named: "07-AdventureVanLoads")
waitForStability()
let firstLoad = loadsList.cells.element(boundBy: 0)
XCTAssertTrue(firstLoad.waitForExistence(timeout: 2))
let loadName = firstLoad.staticTexts.firstMatch.label
firstLoad.tap()
let loadNavButton = app.navigationBars.buttons[loadName]
XCTAssertTrue(loadNavButton.waitForExistence(timeout: 3))
takeScreenshot(named: "09-AdventureVanCalculator")
}
private func launchApp(arguments: [String]) -> XCUIApplication {
let app = XCUIApplication()
var launchArguments = ["--uitest-reset-data"]
launchArguments.append(contentsOf: arguments)
app.launchArguments = launchArguments
app.launch()
//dismissSystemOverlays()
return app
private func tapTab(_ key: UIStringKey, in app: XCUIApplication) {
let identifierMap: [UIStringKey: String] = [
.overviewTab: "overview-tab",
.componentsTab: "components-tab",
.batteriesTab: "batteries-tab",
.chargersTab: "chargers-tab",
]
// Use .matching + .firstMatch to avoid "multiple matches" error
// with iOS 26 floating tab bar (which creates duplicate elements)
if let identifier = identifierMap[key] {
let tabButton = app.buttons.matching(identifier: identifier).firstMatch
if tabButton.waitForExistence(timeout: 3) {
tapElement(tabButton)
return
}
}
let tabButton = button(in: app.buttons, for: key)
XCTAssertTrue(tabButton.waitForExistence(timeout: 3))
tapElement(tabButton)
}
private func tapElement(_ element: XCUIElement) {
if element.isHittable {
element.tap()
} else {
element.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
}
}
// MARK: - Element Resolution
private func resolvedSystemsList(in app: XCUIApplication) -> XCUIElement {
let collection = app.collectionViews["systems-list"]
if collection.waitForExistence(timeout: 6) {
@@ -299,6 +371,62 @@ final class CableUITestsScreenshot: XCTestCase {
return table
}
private func resolvedBatteriesList(in app: XCUIApplication) -> XCUIElement {
let collection = app.collectionViews["batteries-list"]
if collection.waitForExistence(timeout: 6) {
return collection
}
let table = app.tables["batteries-list"]
if table.waitForExistence(timeout: 6) {
return table
}
// Fallback: any list on screen
if app.collectionViews.firstMatch.waitForExistence(timeout: 2) {
return app.collectionViews.firstMatch
}
return app.tables.firstMatch
}
private func resolvedChargersList(in app: XCUIApplication) -> XCUIElement {
let collection = app.collectionViews["chargers-list"]
if collection.waitForExistence(timeout: 6) {
return collection
}
let table = app.tables["chargers-list"]
if table.waitForExistence(timeout: 6) {
return table
}
if app.collectionViews.firstMatch.waitForExistence(timeout: 2) {
return app.collectionViews.firstMatch
}
return app.tables.firstMatch
}
private func resolveBillOfMaterialsElement(in app: XCUIApplication) -> XCUIElement {
let identifier = "system-bom-button"
let buttonByIdentifier = app.buttons.matching(identifier: identifier).firstMatch
if buttonByIdentifier.exists { return buttonByIdentifier }
let elementByIdentifier = app.otherElements.matching(identifier: identifier).firstMatch
if elementByIdentifier.exists { return elementByIdentifier }
let candidates = candidateStrings(for: .billOfMaterials)
for candidate in candidates {
let button = app.buttons[candidate]
if button.exists { return button }
let other = app.otherElements[candidate]
if other.exists { return other }
}
return buttonByIdentifier
}
// MARK: - Screenshots & Stability
private func takeScreenshot(named name: String) {
let screenshot = XCUIScreen.main.screenshot()
let attachment = XCTAttachment(screenshot: screenshot)
@@ -308,52 +436,7 @@ final class CableUITestsScreenshot: XCTestCase {
}
private func waitForStability(long: Bool = false) {
RunLoop.current.run(until: Date().addingTimeInterval(long ? 5.0 : 0.5))
}
private func componentsTabButton(in app: XCUIApplication) -> XCUIElement {
let identifierMatch = app.descendants(matching: .any)
.matching(identifier: "components-tab").firstMatch
if identifierMatch.exists {
return identifierMatch
}
let localizedLabels = [
"Components", "Verbraucher", "Componentes", "Composants", "Componenten"
]
for label in localizedLabels {
let button = app.buttons[label]
if button.exists {
return button
}
let tabBarButton = app.tabBars.buttons[label]
if tabBarButton.exists {
return tabBarButton
}
let segmentedButton = app.segmentedControls.buttons[label]
if segmentedButton.exists {
return segmentedButton
}
let segmentedOther = app.segmentedControls.otherElements[label]
if segmentedOther.exists {
return segmentedOther
}
}
let fallbackSegmented = app.segmentedControls.buttons.element(boundBy: 1)
if fallbackSegmented.exists {
return fallbackSegmented
}
let tabBarButton = app.tabBars.buttons.element(boundBy: 1)
if tabBarButton.exists {
return tabBarButton
}
return app.tabBars.descendants(matching: .any).firstMatch
RunLoop.current.run(until: Date().addingTimeInterval(long ? 3.0 : 0.5))
}
private func waitForSystemDetail(named systemName: String, in app: XCUIApplication, timeout: TimeInterval = 6) -> Bool {
@@ -362,15 +445,12 @@ final class CableUITestsScreenshot: XCTestCase {
if app.otherElements["system-overview"].exists {
return true
}
let navBar = app.navigationBars.firstMatch
if navBar.buttons[systemName].exists || navBar.staticTexts[systemName].exists {
return true
}
RunLoop.current.run(until: Date().addingTimeInterval(0.25))
}
return app.otherElements["system-overview"].exists
}
@@ -395,32 +475,13 @@ final class CableUITestsScreenshot: XCTestCase {
}
}
private func resolveBillOfMaterialsElement(in app: XCUIApplication) -> XCUIElement {
let identifier = "system-bom-button"
let buttonByIdentifier = app.buttons.matching(identifier: identifier).firstMatch
if buttonByIdentifier.exists { return buttonByIdentifier }
let elementByIdentifier = app.otherElements.matching(identifier: identifier).firstMatch
if elementByIdentifier.exists { return elementByIdentifier }
let candidates = candidateStrings(for: .billOfMaterials)
for candidate in candidates {
let button = app.buttons[candidate]
if button.exists {
return button
}
let other = app.otherElements[candidate]
if other.exists {
return other
}
}
return buttonByIdentifier
}
// MARK: - Notification Dismissal
private func dismissNotificationBannersIfNeeded() {
let banner = springboard.otherElements.matching(identifier: "NotificationShortLookView").firstMatch
if banner.waitForExistence(timeout: 1) {
// Try multiple times notifications can appear with a delay
for _ in 0..<3 {
let banner = springboard.otherElements.matching(identifier: "NotificationShortLookView").firstMatch
guard banner.waitForExistence(timeout: 1) else { return }
if banner.isHittable {
banner.swipeUp()
} else {
@@ -432,6 +493,8 @@ final class CableUITestsScreenshot: XCTestCase {
}
}
// MARK: - Localized Element Matching
private func candidateStrings(for key: UIStringKey) -> [String] {
var values = Set<String>()
if let languageCode = Locale.preferredLanguages.first.flatMap({ String($0.prefix(2)) }),
@@ -444,9 +507,6 @@ final class CableUITestsScreenshot: XCTestCase {
if let others = translations[key]?.values {
values.formUnion(others)
}
if key == .settings {
values.insert("gearshape")
}
return Array(values)
}
@@ -465,139 +525,4 @@ final class CableUITestsScreenshot: XCTestCase {
)
return query.matching(predicate).firstMatch
}
private func optionalButton(in query: XCUIElementQuery, for key: UIStringKey) -> XCUIElement? {
let element = button(in: query, for: key)
return element.exists ? element : nil
}
private func tabButton(in app: XCUIApplication, key: UIStringKey) -> XCUIElement {
let tabSpecific = button(in: app.tabBars.buttons, for: key)
if tabSpecific.exists {
return tabSpecific
}
return button(in: app.buttons, for: key)
}
private func navigationBar(in app: XCUIApplication, key: UIStringKey) -> XCUIElement {
let candidates = candidateStrings(for: key)
for candidate in candidates {
let bar = app.navigationBars[candidate]
if bar.exists {
return bar
}
}
return app.navigationBars.element(boundBy: 0)
}
private func tapButtonIfPresent(app: XCUIApplication, key: UIStringKey) {
let candidates = candidateStrings(for: key)
for candidate in candidates {
let button = app.buttons[candidate]
if button.waitForExistence(timeout: 2) {
button.tap()
return
}
}
}
private func openBillOfMaterials(app: XCUIApplication) {
let bomButton = button(in: app.buttons, for: .billOfMaterials)
XCTAssertTrue(bomButton.waitForExistence(timeout: 6))
bomButton.tap()
let bomView = app.otherElements["system-bom-view"]
XCTAssertTrue(bomView.waitForExistence(timeout: 8))
waitForStability(long: true)
}
private func closeBillOfMaterials(app: XCUIApplication) {
tapButtonIfPresent(app: app, key: .close)
}
private func navigateBack(app: XCUIApplication) {
let backButton = app.navigationBars.buttons.element(boundBy: 0)
if backButton.exists {
backButton.tap()
} else {
app.swipeRight()
}
}
private func openSettings(app: XCUIApplication) {
let systemsBar = navigationBar(in: app, key: .systemsTitle)
let settingsButton = button(in: systemsBar.buttons, for: .settings)
if settingsButton.exists {
settingsButton.tap()
} else {
systemsBar.buttons.element(boundBy: 0).tap()
}
}
private func ensureDoNotDisturbEnabled() {
springboard.activate()
let pullStart = springboard.coordinate(withNormalizedOffset: CGVector(dx: 0.98, dy: 0.02))
let pullEnd = springboard.coordinate(withNormalizedOffset: CGVector(dx: 0.98, dy: 0.30))
pullStart.press(forDuration: 0.1, thenDragTo: pullEnd)
let focusTile = springboard.otherElements["Focus"]
let focusButton = springboard.buttons["Focus"]
if focusTile.waitForExistence(timeout: 2) {
focusTile.press(forDuration: 1.0)
} else if focusButton.waitForExistence(timeout: 2) {
focusButton.press(forDuration: 1.0)
} else {
return
}
let dndButton = springboard.buttons["Do Not Disturb"]
if dndButton.waitForExistence(timeout: 1) {
if !dndButton.isSelected {
dndButton.tap()
}
} else {
let dndCell = springboard.cells["Do Not Disturb"]
if dndCell.waitForExistence(timeout: 1) && !dndCell.isSelected {
dndCell.tap()
} else {
let dndLabel = springboard.staticTexts["Do Not Disturb"]
if dndLabel.waitForExistence(timeout: 1) {
dndLabel.tap()
}
}
}
let dismissStart = springboard.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.85))
let dismissEnd = springboard.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.15))
dismissStart.press(forDuration: 0.1, thenDragTo: dismissEnd)
}
private func dismissSystemOverlays() {
let app = XCUIApplication()
let alertButtons = [
"OK", "Allow", "Later", "Not Now", "Close",
"Continue", "Remind Me Later", "Maybe Later",
]
if app.alerts.firstMatch.exists {
handleAlerts(in: app, buttons: alertButtons)
}
if springboard.alerts.firstMatch.exists || springboard.scrollViews.firstMatch.exists {
handleAlerts(in: springboard, buttons: alertButtons + ["Enable Later"])
}
}
private func handleAlerts(in application: XCUIApplication, buttons: [String]) {
for buttonLabel in buttons {
let button = application.buttons[buttonLabel]
if button.waitForExistence(timeout: 0.5) {
button.tap()
return
}
}
let closeButton = application.buttons.matching(NSPredicate(format: "identifier CONTAINS[c] %@", "Close")).firstMatch
if closeButton.exists {
closeButton.tap()
}
}
}

View File

@@ -1,166 +1,24 @@
//
// CableUITestsScreenshotLaunchTests.swift
// CableUITestsScreenshot
//
// Created by Stefan Lange-Hegermann on 06.10.25.
//
import XCTest
final class CableUITestsScreenshotLaunchTests: XCTestCase {
private func launchApp(arguments: [String] = []) -> XCUIApplication {
let app = XCUIApplication()
var launchArguments = ["--uitest-reset-data"]
launchArguments.append(contentsOf: arguments)
app.launchArguments = launchArguments
app.launch()
return app
}
private func resolvedSystemsList(in app: XCUIApplication) -> XCUIElement {
let collection = app.collectionViews["systems-list"]
if collection.waitForExistence(timeout: 6) {
return collection
}
let table = app.tables["systems-list"]
XCTAssertTrue(table.waitForExistence(timeout: 6))
return table
}
private func resolvedLoadsList(in app: XCUIApplication) -> XCUIElement {
let collection = app.collectionViews["loads-list"]
if collection.waitForExistence(timeout: 6) {
return collection
}
let table = app.tables["loads-list"]
XCTAssertTrue(table.waitForExistence(timeout: 6))
return table
}
private func takeScreenshot(name: String,
lifetime: XCTAttachment.Lifetime = .keepAlways) {
let screenshot = XCUIScreen.main.screenshot()
let attachment = XCTAttachment(screenshot: screenshot)
attachment.name = name
attachment.lifetime = lifetime
add(attachment)
}
override class var runsForEachTargetApplicationUIConfiguration: Bool {
false
}
override func setUpWithError() throws {
continueAfterFailure = false
}
@MainActor
func testOnboardingLoadsView() throws {
let app = launchApp(arguments: ["--uitest-reset-data"])
takeScreenshot(name: "01-OnboardingSystemsView")
func testLaunch() throws {
let app = XCUIApplication()
app.launchArguments = ["--uitest-reset-data", "--uitest-sample-data"]
app.launch()
let createSystemButton = app.buttons["create-system-button"]
XCTAssertTrue(createSystemButton.waitForExistence(timeout: 5))
createSystemButton.tap()
takeScreenshot(name: "02-OnboardingLoadsView")
let componentsTab = app.buttons["components-tab"]
XCTAssertTrue(componentsTab.waitForExistence(timeout: 3))
componentsTab.tap()
let libraryCloseButton = app.buttons["library-view-close-button"]
let browseLibraryButton = onboardingSecondaryButton(in: app)
XCTAssertTrue(browseLibraryButton.waitForExistence(timeout: 5))
browseLibraryButton.tap()
XCTAssertTrue(libraryCloseButton.waitForExistence(timeout: 5))
Thread.sleep(forTimeInterval: 10)
takeScreenshot(name: "04-ComponentSelectorView")
libraryCloseButton.tap()
let createComponentButton = onboardingPrimaryButton(in: app)
XCTAssertTrue(createComponentButton.waitForExistence(timeout: 5))
createComponentButton.tap()
takeScreenshot(name: "03-LoadEditorView")
}
func testWithSampleData() throws {
let app = launchApp(arguments: ["--uitest-sample-data"])
let systemsList = resolvedSystemsList(in: app)
let firstSystemCell = systemsList.cells.element(boundBy: 0)
XCTAssertTrue(firstSystemCell.waitForExistence(timeout: 3))
let systemName = firstSystemCell.staticTexts.firstMatch.label
takeScreenshot(name: "05-SystemsWithSampleData")
let rowButton = firstSystemCell.buttons.firstMatch
if rowButton.waitForExistence(timeout: 2) {
rowButton.tap()
} else {
firstSystemCell.tap()
}
let navButton = app.navigationBars.buttons[systemName]
if !navButton.waitForExistence(timeout: 3) {
let coordinate = firstSystemCell.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
coordinate.tap()
XCTAssertTrue(navButton.waitForExistence(timeout: 3))
}
tapComponentsTab(in: app)
let loadsElement = resolvedLoadsList(in: app)
XCTAssertTrue(loadsElement.waitForExistence(timeout: 6))
XCTAssertTrue(loadsElement.cells.firstMatch.waitForExistence(timeout: 3))
Thread.sleep(forTimeInterval: 1)
takeScreenshot(name: "06-AdventureVanLoads")
let bomButton = app.buttons["system-bom-button"]
XCTAssertTrue(bomButton.waitForExistence(timeout: 2))
bomButton.tap()
// let bomView = app.otherElements["system-bom-view"]
// XCTAssertTrue(bomView.waitForExistence(timeout: 3))
//
// Thread.sleep(forTimeInterval: 1)
// takeScreenshot(name: "07-AdventureVanBillOfMaterials")
}
private func tapComponentsTab(in app: XCUIApplication) {
let button = componentsTabButton(in: app)
XCTAssertTrue(button.waitForExistence(timeout: 3))
button.tap()
}
private func onboardingPrimaryButton(in app: XCUIApplication) -> XCUIElement {
let button = app.buttons["create-component-button"]
if button.exists { return button }
return app.buttons["onboarding-primary-button"]
}
private func onboardingSecondaryButton(in app: XCUIApplication) -> XCUIElement {
let button = app.buttons["select-component-button"]
if button.exists { return button }
return app.buttons["onboarding-secondary-button"]
}
private func componentsTabButton(in app: XCUIApplication) -> XCUIElement {
let idButton = app.buttons["components-tab"]
if idButton.exists {
return idButton
}
let labels = ["Components", "Verbraucher", "Componentes", "Composants", "Componenten"]
for label in labels {
let button = app.buttons[label]
if button.exists { return button }
}
return app.tabBars.buttons.element(boundBy: 1)
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
}