From 3ea85b0d8d3cb6dca522578cb29a676bbac6809f Mon Sep 17 00:00:00 2001 From: soryu-co Date: Fri, 24 Apr 2026 18:13:01 +0000 Subject: Makima iOS — Screenshots scheme + demo mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an opt-in `Makima-Screenshots` scheme + `Screenshots` build config that short-circuits auth with an in-process URLProtocol and seeds the app with canned demo data. Lets us run the app past onboarding in UI tests or on a fresh simulator without touching a real Makima server. What lands - ScreenshotMode: compile-time flag via -DSCREENSHOT_MODE (set in the new Screenshots configuration in project.yml). - AppState: when the flag is on, calls auth.seedScreenshotData() in init. - AuthStore: adds seedSetClient/seedSetState helpers (internal, only useful for screenshot + future preview builds) and a seedScreenshotData that wires up a fake APIClient backed by DemoSession. - DemoSession: URLSession wired to DemoURLProtocol, which answers /api/v1/mesh/daemons, /contracts, /mesh/tasks, /directives, /listen/sessions, /mesh/tasks/{id}, /mesh/tasks/{id}/output with deterministic demo JSON. Host-scoped to makima.jp so production URL remains an operational realm. - Tests/MakimaUITests/ScreenshotTests.swift: five XCTest cases that drive the app through Home, Contracts, Task detail, Directives, and Settings. Each attaches an XCTAttachment screenshot with a stable name. - project.yml: adds MakimaUITests target + Screenshots configuration + Makima-Screenshots scheme. Usage (on a Mac) make xcgen xcodebuild \\ -project Makima.xcodeproj \\ -scheme Makima-Screenshots \\ -configuration Screenshots \\ -destination 'platform=iOS Simulator,name=iPhone 16 Pro' \\ test Screenshots appear as attachments inside the resulting .xcresult bundle; open the bundle in Xcode > Report Navigator to browse them, or use `xcrun xcresulttool get test-results attachments` to export to PNG. Scope - Normal Debug/Release builds are unaffected — SCREENSHOT_MODE is only defined under the Screenshots configuration. - DemoURLProtocol only matches host=makima.jp, so any real request made during screenshot tests (to non-makima.jp hosts) still fails normally. - No production code path gates on ScreenshotMode beyond the init-time seed call; ensureWebSocket is stubbed to a fake online status so the masthead pill shows LIVE during screenshots. Not included in this commit: the matching GitHub Actions workflow (.github/workflows/ios-ci.yml) — requires a workflow-scoped token to push and will land as a follow-up. The Swift helper that extracts screenshots from xcresult is also queued for that follow-up. --- .../ios/Tests/MakimaUITests/ScreenshotTests.swift | 73 ++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 makima/ios/Tests/MakimaUITests/ScreenshotTests.swift (limited to 'makima/ios/Tests/MakimaUITests') diff --git a/makima/ios/Tests/MakimaUITests/ScreenshotTests.swift b/makima/ios/Tests/MakimaUITests/ScreenshotTests.swift new file mode 100644 index 0000000..6d0fba9 --- /dev/null +++ b/makima/ios/Tests/MakimaUITests/ScreenshotTests.swift @@ -0,0 +1,73 @@ +import XCTest + +/// Driven by CI to capture deterministic screenshots when the app is built +/// with `-DSCREENSHOT_MODE`. Saves PNGs as XCTest attachments; CI extracts +/// them from the .xcresult bundle. +final class ScreenshotTests: XCTestCase { + var app: XCUIApplication! + + override func setUp() { + super.setUp() + continueAfterFailure = false + app = XCUIApplication() + app.launchArguments += ["-AppleLanguages", "(en)", "-AppleLocale", "en_US"] + app.launch() + } + + func test01_Home() throws { + // Wait for home content to appear + let contracts = app.staticTexts["CONTRACTS//"] + XCTAssertTrue(contracts.waitForExistence(timeout: 10)) + snapshot("01-home") + } + + func test02_Contracts() throws { + let contracts = app.staticTexts["CONTRACTS//"] + XCTAssertTrue(contracts.waitForExistence(timeout: 10)) + contracts.tap() + let header = app.staticTexts["CONTRACTS"] + _ = header.waitForExistence(timeout: 5) + sleep(1) + snapshot("02-contracts") + } + + func test03_TaskDetail() throws { + let recent = app.staticTexts["RECENT TASKS//"] + XCTAssertTrue(recent.waitForExistence(timeout: 10)) + recent.tap() + // TasksListView should now be visible + let firstTask = app.staticTexts.matching(NSPredicate(format: "label CONTAINS 'HomeStore' OR label CONTAINS 'WebSocket livestream'")) + .firstMatch + _ = firstTask.waitForExistence(timeout: 5) + firstTask.tap() + sleep(2) + snapshot("03-task-detail") + } + + func test04_Directives() throws { + let dirs = app.staticTexts["DIRECTIVES//"] + XCTAssertTrue(dirs.waitForExistence(timeout: 10)) + dirs.tap() + sleep(1) + snapshot("04-directives") + } + + func test05_Settings() throws { + let gear = app.buttons["Settings"] + XCTAssertTrue(gear.waitForExistence(timeout: 10)) + gear.tap() + sleep(1) + snapshot("05-settings") + } + + // MARK: - Helpers + + /// Adds a screenshot to the test result as an attachment with a stable name. + private func snapshot(_ name: String) { + let screenshot = XCUIScreen.main.screenshot() + let attachment = XCTAttachment(screenshot: screenshot) + attachment.name = name + attachment.lifetime = .keepAlways + add(attachment) + } +} -- cgit v1.2.3