also looking at this
fix(mobile): preserve native editing, navigation, and durable media requests
#369
36 files
+864
-185
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -132,11 +132,11 @@ jobs: | ||
| 132 | 132 | strategy: |
| 133 | 133 | fail-fast: false |
| 134 | 134 | matrix: |
| 135 | suite: ${{ fromJSON(inputs.scope == 'ios-render' && '["uikit"]' || inputs.scope == 'ios-navigation' && '["navigation"]' || '["uikit","navigation"]') }} | |
| 135 | suite: ${{ fromJSON(inputs.scope == 'ios-render' && '["uikit"]' || inputs.scope == 'ios-navigation' && inputs.selectors != '' && '["navigation"]' || inputs.scope == 'ios-navigation' && '["navigation-unit","navigation-ui"]' || '["uikit","navigation-unit","navigation-ui"]') }} | |
| 136 | 136 | runs-on: macos-15 |
| 137 | # Compile and run each native fixture on its own fresh runner. Combining | |
| 138 | # both consumed the job budget before navigation finished on slower hosts. | |
| 139 | # Each suite keeps the same bounded backstop and strict XCTest assertions. | |
| 137 | # UIKit reconciliation and both navigation XCTest targets each get a fresh | |
| 138 | # runner and result bundle. Combining unit and UI navigation exceeded the | |
| 139 | # existing job budget; all targets remain required with the same backstop. | |
| 140 | 140 | timeout-minutes: 45 |
| 141 | 141 | env: |
| 142 | 142 | CRAFT_EVIDENCE_LABEL: ios-native |
| @@ -163,7 +163,7 @@ jobs: | ||
| 163 | 163 | # @stacksjs/stx compiler (`stx native compile`) so this job cannot silently change |
| 164 | 164 | # underneath us. Both iOS jobs and Android use the same compiler revision. |
| 165 | 165 | - name: Checkout stx compiler |
| 166 | if: ${{ matrix.suite == 'navigation' }} | |
| 166 | if: ${{ startsWith(matrix.suite, 'navigation') }} | |
| 167 | 167 | uses: actions/checkout@v6 |
| 168 | 168 | with: |
| 169 | 169 | repository: stacksjs/stx |
| @@ -171,7 +171,7 @@ jobs: | ||
| 171 | 171 | path: .stx-compiler |
| 172 | 172 | |
| 173 | 173 | - name: Install stx compiler dependencies |
| 174 | if: ${{ matrix.suite == 'navigation' }} | |
| 174 | if: ${{ startsWith(matrix.suite, 'navigation') }} | |
| 175 | 175 | working-directory: .stx-compiler |
| 176 | 176 | # Pantry ships Bun 1.3.14, which cannot frozen-install the pinned stx |
| 177 | 177 | # lockfile. Resolve the unchanged external manifest without rewriting |
| @@ -191,10 +191,10 @@ jobs: | ||
| 191 | 191 | id: navigation |
| 192 | 192 | # Reconciliation and navigation are independent suites. Keep the |
| 193 | 193 | # navigation evidence when reconciliation fails; the job still fails. |
| 194 | if: ${{ !cancelled() && matrix.suite == 'navigation' }} | |
| 194 | if: ${{ !cancelled() && startsWith(matrix.suite, 'navigation') }} | |
| 195 | 195 | env: |
| 196 | 196 | STX_CLI: ${{ github.workspace }}/.stx-compiler/packages/stx/bin/cli.ts |
| 197 | CRAFT_ONLY_TESTING: ${{ inputs.selectors }} | |
| 197 | CRAFT_ONLY_TESTING: ${{ matrix.suite == 'navigation-unit' && 'NativeNavigationUnitTests' || matrix.suite == 'navigation-ui' && 'NativeNavigationUITests' || inputs.selectors }} | |
| 198 | 198 | CRAFT_NATIVE_NAVIGATION_RESULT: ${{ github.workspace }}/artifacts/mobile-e2e/ios-native/navigation.xcresult |
| 199 | 199 | run: | |
| 200 | 200 | mkdir -p artifacts/mobile-e2e/ios-native |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | import { afterAll, expect, test } from 'bun:test' |
| 2 | 2 | import { chmodSync, existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs' |
| 3 | 3 | import { join } from 'node:path' |
| 4 | import { runInNewContext } from 'node:vm' | |
| 4 | 5 | import { requiredReleaseAssets } from './release-manifest' |
| 5 | 6 | import { createTempDir, removeAllTempDirs } from '../packages/typescript/src/temp-dir' |
| 6 | 7 | |
| @@ -76,6 +77,31 @@ test('native iOS suites and protocol checks have independent required jobs', () | ||
| 76 | 77 | expect(workflow.jobs[name].env?.DEVELOPER_DIR).toBe('/Applications/Xcode_26.2.app/Contents/Developer') |
| 77 | 78 | }) |
| 78 | 79 | |
| 80 | test('navigation shards retain both complete XCTest targets and focused selection', () => { | |
| 81 | const workflow = Bun.YAML.parse(readFileSync(join(import.meta.dir, '../.github/workflows/mobile-e2e.yml'), 'utf8')) as { jobs: Record<string, Job> } | |
| 82 | const native = workflow.jobs['ios-native'] | |
| 83 | const expression = (native.strategy?.matrix as unknown as { suite: string }).suite | |
| 84 | const navigation = native.steps?.find(step => step.name === 'Test native multi-screen navigation') | |
| 85 | const evaluate = (value: string, scope: string, selectors = '', suite = '') => runInNewContext(value.replace(/^\$\{\{\s*|\s*\}\}$/g, ''), { | |
| 86 | inputs: { scope, selectors }, matrix: { suite }, fromJSON: JSON.parse, | |
| 87 | startsWith: (text: string, prefix: string) => text.startsWith(prefix), cancelled: () => false, | |
| 88 | }, { timeout: 100 }) | |
| 89 | for (const scope of ['', 'all']) | |
| 90 | expect(evaluate(expression, scope)).toEqual(['uikit', 'navigation-unit', 'navigation-ui']) | |
| 91 | expect(evaluate(expression, 'ios-navigation')).toEqual(['navigation-unit', 'navigation-ui']) | |
| 92 | expect(evaluate(expression, 'ios-render')).toEqual(['uikit']) | |
| 93 | const focused = 'NativeNavigationUITests/NativeNavigationUITests/testMultilineInputKeepsKeyboardFocusWhenTapped' | |
| 94 | expect(evaluate(expression, 'ios-navigation', focused)).toEqual(['navigation']) | |
| 95 | expect(evaluate(navigation!.env!.CRAFT_ONLY_TESTING, 'all', '', 'navigation-unit')).toBe('NativeNavigationUnitTests') | |
| 96 | expect(evaluate(navigation!.env!.CRAFT_ONLY_TESTING, 'all', '', 'navigation-ui')).toBe('NativeNavigationUITests') | |
| 97 | expect(evaluate(navigation!.env!.CRAFT_ONLY_TESTING, 'ios-navigation', focused, 'navigation')).toBe(focused) | |
| 98 | for (const suite of ['navigation-unit', 'navigation-ui', 'navigation']) | |
| 99 | expect(evaluate(navigation!.if!, 'all', '', suite)).toBe(true) | |
| 100 | expect(evaluate(navigation!.if!, 'all', '', 'uikit')).toBe(false) | |
| 101 | expect(native.strategy?.['fail-fast']).toBe(false) | |
| 102 | expect(workflow.jobs['mobile-summary'].steps?.find(step => step.name === 'Fail if any leg failed')?.if).toContain('needs.ios-native.result') | |
| 103 | }) | |
| 104 | ||
| 79 | 105 | test('mobile device jobs preserve runner diagnostics when the harness fails', () => { |
| 80 | 106 | const workflow = Bun.YAML.parse(readFileSync(join(import.meta.dir, '../.github/workflows/mobile-e2e.yml'), 'utf8')) as { jobs: Record<string, Job> } |
| 81 | 107 | for (const [jobName, evidenceDir, expectedCommand] of [ |