eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1import XCTest
2
3// The long-press message menu, driven with REAL synthesized touches in the
4// simulator (2026-07-21): every code-level harness passed while the device
5// press stayed dead, so the gesture itself must be exercised. Runs against
6// the local dev server β€” pass TEST_RUNNER_DEV_LINK (the dev sign-in link
7// from .lchat-dev.json) on the xcodebuild invocation; without it the test
8// skips.
9final class FeedMenuUITests: XCTestCase {
10
11 override func setUpWithError() throws {
12 continueAfterFailure = false
13 }
14
15 func testLongPressOpensMenuAndReacts() throws {
16 let env = ProcessInfo.processInfo.environment
17 guard let link = env["DEV_LINK"], !link.isEmpty else {
18 throw XCTSkip("Set TEST_RUNNER_DEV_LINK to the dev server sign-in link.")
19 }
20
21 let app = XCUIApplication()
22 // The DEBUG dev-join door (AppState.init): an empty install joins
23 // the dev space natively from this link before first paint.
24 app.launchEnvironment["LCHAT_DEV_JOIN"] = link
25 app.launch()
26
27 // Open the dev-seeded group ("Dev chat" β€” lchat-dev-up's fixture).
28 let row = app.staticTexts["Dev chat"].firstMatch
29 XCTAssertTrue(row.waitForExistence(timeout: 25), "Dev chat not in thread list")
30 shot(app, "menu-01-threads")
31 row.tap()
32
33 // The dev seed plants a welcome message β€” press that bubble (no
34 // composer/keyboard in the loop; sim keyboards are flaky).
35 let bubble = app.staticTexts
36 .containing(NSPredicate(format: "label CONTAINS %@", "seeded by lchat-dev-up"))
37 .firstMatch
38 XCTAssertTrue(bubble.waitForExistence(timeout: 20), "seeded bubble never rendered")
39 shot(app, "menu-02-feed")
40
41 // THE press. The owned menu's reaction palette is the verdict.
42 bubble.press(forDuration: 0.8)
43 let react = app.buttons["React πŸ‘"].firstMatch
44 let appeared = react.waitForExistence(timeout: 5)
45 shot(app, appeared ? "menu-03-open" : "menu-03-DEAD")
46 XCTAssertTrue(appeared, "long-press did not open the menu")
47
48 // React, and the overlay must dismiss.
49 react.tap()
50 Thread.sleep(forTimeInterval: 0.6)
51 shot(app, "menu-04-after-react")
52 XCTAssertFalse(app.buttons["React ❀️"].firstMatch.exists,
53 "menu overlay did not dismiss after reacting")
54
55 // A second press on the same bubble reopens it (repeatability).
56 bubble.press(forDuration: 0.8)
57 XCTAssertTrue(app.buttons["React πŸ˜‚"].firstMatch.waitForExistence(timeout: 5),
58 "second long-press did not reopen the menu")
59 // Tap outside (the dimmed area above) dismisses without acting.
60 app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.88)).tap()
61 Thread.sleep(forTimeInterval: 0.6)
62 XCTAssertFalse(app.buttons["React πŸ˜‚"].firstMatch.exists,
63 "outside tap did not dismiss the menu")
64 shot(app, "menu-05-dismissed")
65 }
66
67 private func shot(_ app: XCUIApplication, _ name: String) {
68 let a = XCTAttachment(screenshot: app.screenshot())
69 a.name = name
70 a.lifetime = .keepAlways
71 add(a)
72 }
73}
74