Files
fusion/docs/ios-acceptance.md
gsxdsm deb1717812 FN-6667: add real iOS Safari acceptance harness
Add a real iOS Safari acceptance harness and documentation for terminal verification gates.

- Add an `ios:acceptance` script with check, dry-run, and real WebDriver session modes.
- Support BrowserStack, Sauce Labs, and LambdaTest credential discovery with redacted capability planning.
- Document the real-iOS provisioning workflow and link it from testing docs.
- Cover credential selection, URL validation, capability shaping, and CLI behavior with node tests.

Files changed:
 docs/README.md                            |   1 +
 docs/ios-acceptance.md                    | 144 ++++++++++++++++
 docs/testing.md                           |   3 +
 package.json                              |   1 +
 scripts/__tests__/ios-acceptance.test.mjs | 131 +++++++++++++++
 scripts/ios-acceptance.mjs                | 264 ++++++++++++++++++++++++++++++
 scripts/lib/ios-acceptance.mjs            | 243 +++++++++++++++++++++++++++
 7 files changed, 787 insertions(+)

Fusion-Task-Id: FN-6667

Fusion-Task-Lineage: 9eee6dbe-646e-4fff-a1da-fd59cdd2cb59
2026-06-18 16:47:09 -07:00

8.4 KiB

Real iOS Safari acceptance surface

← Docs index

Purpose

The mobile xterm wide-glyph defect recurred across FN-6390 → FN-6424 → FN-6603 → FN-6638 → FN-6659. Each fix shipped without a real-iOS reproduction because the execution environment had no BrowserStack, Sauce Labs, LambdaTest, or physical-device surface. FN-6641 and FN-6662 therefore had to treat the real-device gate as unavailable instead of verified.

scripts/ios-acceptance.mjs is the reachable-surface plumbing for future terminal acceptance gates:

  • --check reports whether real-iOS cloud credentials are present and exits 0 only when a provider is usable.
  • --dry-run prints the redacted provider/capability plan without opening a network session.
  • Session mode opens a real iOS Safari W3C WebDriver session, navigates to a served Fusion dashboard URL, captures a PNG screenshot, and always deletes the cloud session in finally.

The harness is intentionally dependency-light: it uses built-in fetch and does not install Selenium, WebdriverIO, Appium, or provider CLIs.

Real-device options

Option A — physical iPhone or iPad

Use a current iPhone or iPad running Safari with macOS Safari remote Web Inspector:

  1. Serve the built dashboard on a reachable free port. Use --port 0 or another free port; never use port 4040, which is reserved for the production dashboard.
  2. Open the URL on the physical device.
  3. In macOS Safari, enable Develop menu and choose Develop → device → page.
  4. Capture screenshots and measure terminal cell widths through Web Inspector.

This path does not use scripts/ios-acceptance.mjs session mode, but the --check probe should still return non-zero unless cloud credentials are also present. A human verifier records the physical-device evidence in the task document.

Option B — real-iOS cloud WebDriver

Supply exactly one complete credential pair for a supported provider. If multiple pairs are present, the harness chooses BrowserStack → Sauce Labs → LambdaTest.

Provider Credential keys Default hub URL
BrowserStack BROWSERSTACK_USERNAME, BROWSERSTACK_ACCESS_KEY https://hub-cloud.browserstack.com/wd/hub (upstream-pending-verification)
Sauce Labs SAUCE_USERNAME, SAUCE_ACCESS_KEY https://ondemand.us-west-1.saucelabs.com/wd/hub (upstream-pending-verification)
LambdaTest LT_USERNAME, LT_ACCESS_KEY https://mobile-hub.lambdatest.com/wd/hub (upstream-pending-verification)

Hub base URLs are region-configurable:

  • BROWSERSTACK_HUB_URL
  • SAUCE_HUB_URL
  • LT_HUB_URL

Device defaults are intentionally conservative and may be overridden without code changes:

  • BrowserStack: BROWSERSTACK_IOS_DEVICE, BROWSERSTACK_IOS_VERSION
  • Sauce Labs: SAUCE_IOS_DEVICE, SAUCE_IOS_VERSION
  • LambdaTest: LT_IOS_DEVICE, LT_IOS_VERSION

The default capability target is real iOS Safari on iPhone 15 / iOS 17; provider-specific options set real-device flags (realMobile, realDevice, or isRealMobile). Do not replace this with Playwright, desktop WebKit, jsdom, or an iOS simulator for terminal acceptance.

Storing credentials safely

Secret values must never be committed, logged, attached, or written into task documents.

Recommended Fusion setup:

  1. Store each provider credential as a project secret with access policy appropriate for the operator (auto for unattended gates, prompt for manual approval, deny when not exportable).
  2. Mark gate credentials env_exportable=true and set env_export_key to the exact env var name, for example BROWSERSTACK_USERNAME.
  3. Enable project secretsEnv.enabled=true so task worktrees receive a gitignored .env file with the materialized keys.
  4. Keep secretsEnv.requireGitignored=true so plaintext is never written to a tracked path.

If environment materialization is unavailable, an operator or agent can fall back to fn_secret_get for these exact keys (project scope first, then global) and export them only for the acceptance command. The harness prints key names and missing-key lists, never plaintext values.

Harness usage

Probe availability for FN-6662-style gates:

pnpm ios:acceptance -- --check
# or
node scripts/ios-acceptance.mjs --check
  • Exit 0: at least one complete cloud credential pair is present; run the real-iOS gate.
  • Non-zero: no cloud provider is complete. Record the missing keys and close the observational gate with:
NO-OP: real-iOS surface unavailable — credentials missing, cannot run acceptance gate

Inspect a redacted plan without network access:

BROWSERSTACK_USERNAME=... BROWSERSTACK_ACCESS_KEY=... \
  pnpm ios:acceptance -- --dry-run --provider browserstack

Run a real session and capture evidence:

# Serve the dashboard on a free, reachable, non-4040 port first.
DASHBOARD_URL="https://reachable.example.test" \
  pnpm ios:acceptance -- --url "$DASHBOARD_URL" --out screenshots/ios-acceptance.png

The JSON result includes provider, device, platformVersion, sessionId, and screenshotPath. The screenshot is a PNG decoded from the WebDriver /screenshot response. Authenticated hub URLs and Authorization headers are never printed.

Serving the dashboard for cloud access

Build and serve the dashboard from the verification worktree, then make it reachable to the selected real-iOS surface:

pnpm build
# Use the project serve/dev command appropriate for the gate and choose --port 0 or a known free non-4040 port.

For cloud devices, use the provider's documented tunnel, a public preview URL, or another approved remote-access path. The harness does not start tunnels or download provider binaries; it only talks to the hosted WebDriver hub over HTTPS.

External Integration Evidence

This harness integrates hosted SaaS WebDriver hubs over W3C WebDriver using built-in fetch; no provider binary is downloaded or executed locally, so checksums are not applicable.