Files
gui/pkgs/native-ci/__tests__/detox-classify.test.ts
zeekayandHanzo Dev 69c6e860d7 transplant onto tamagui v2.6.0, brand as a re-runnable transform
This repo could never sync. There was NO merge-base with tamagui — 372 commits
here against 15,791 there, unrelated histories — because it began as a
copy-paste rebrand with a flattened layout. Every "sync" since was a hand
re-application that drifted, which is why the tree sat on 7.3.1 while upstream
shipped fixes we then rediscovered by hand.

This commit's PARENT IS TAMAGUI. A future sync is `git merge tama/main` then
`node scripts/rebrand.mjs` — a real merge, because the history is now shared.

174 packages: 166 upstream, our 8 restored (chrome, shell, telemetry, recipes,
get, font-geist-{sans,mono}, @hanzo/booker), bento-get gained. 170/170 build.

Our patches to files UPSTREAM ALSO OWNS are carried, which the package-level
delta missed and which was where the value actually was:
  - useMedia's server snapshot (`initState ?? getMedia()`). Upstream still
    returns undefined there, so every Tamagui Dialog kills a prerender — the
    exact crash hanzo.app spent a day on.
  - data-slot on web host nodes, absent upstream entirely.
Both applied ONTO upstream's newer files (it rewrote useMedia +150/-63 and
createComponent +160/-55), marked `PATCH (ours, carried across the transplant)`.

Seven traps are encoded in scripts/rebrand.mjs, each found by a gate:
skip itself and lockfiles; special-case the double-branded names in BOTH scoped
and bare form; map path strings, not just paths; drop a level for the flattened
group; check disk rather than a stale index.

Rust build artifacts are NOT committed. An earlier draft of this commit swept
apps/desktop/src-tauri/target/debug in via `git add -A` — 3,859 files including
a 272MB .a, over GitHub's 100MB cap, so the push could never have landed. They
are gitignored now.

Co-Authored-By: Hanzo Dev <[email protected]>
2026-07-30 18:25:06 -07:00

204 lines
8.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Unit tests for classifyDetoxFailures - the connect-flake vs real-failure classifier
* that drives the targeted retry in runDetoxTests.
*
* Fixtures are faithful slices of two real CI failures:
* - run 27021194934 (2026-06-05): noRngh beforeAll hook timeout ("for a hook")
* - run 27175651901 (2026-06-09): PointerEvents safeLaunchApp connect timeout
* ("timed out after 70000ms" thrown from e2e/utils/detox.ts:84)
* plus a synthetic real assertion failure that must NOT be treated as a flake.
*/
import { test, expect } from 'bun:test'
import { classifyDetoxFailures } from '../src/detox'
// June 9: PointerEvents beforeAll launch could not connect; noRngh passed this run.
const JUNE9_CONNECT_TIMEOUT = `
PASS e2e/PressStyleNative.noRngh.test.ts (434.576 s)
PressStyleNative (no RNGH)
✓ should render the test case screen (1200 ms)
FAIL e2e/PointerEvents.test.ts (188.462 s)
PointerEvents
✕ should render the test case (4 ms)
✕ should fire pointerDown and pointerUp on tap
● PointerEvents › should render the test case
timed out after 70000ms
82 | promise,
83 | new Promise<never>((_, reject) => {
> 84 | timeout = setTimeout(() => reject(new Error(\`timed out after \${ms}ms\`)), ms)
| ^
at Timeout.<anonymous> (e2e/utils/detox.ts:84:43)
Test Suites: 1 failed, 1 skipped, 5 passed, 6 of 7 total
Tests: 6 failed, 7 skipped, 19 passed, 32 total
`
// June 5: noRngh beforeAll exceeded the jest hook timeout; other files passed.
const JUNE5_HOOK_TIMEOUT = `
FAIL e2e/PressStyleNative.noRngh.test.ts (573.804 s)
PressStyleNative (no RNGH)
✕ should render the test case screen (50 ms)
● PressStyleNative (no RNGH) › should render the test case screen
thrown: "Exceeded timeout of 180000 ms for a hook.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
PASS e2e/TabsOnInteraction.test.ts (67.079 s)
PASS e2e/ShorthandVariables.test.ts (79.351 s)
PASS e2e/check-rngh-status.test.ts (47 s)
Test Suites: 1 failed, 1 skipped, 5 passed, 6 of 7 total
Tests: 9 failed, 7 skipped, 16 passed, 32 total
`
// A genuine assertion failure - the press count didn't update. Uses detox's REAL
// expectation-timeout wording from Timer.js: "Exceeded timeout of <n>ms while ...".
// Note the prefix collides with jest's hook timeout ("Exceeded timeout of <n> ms for
// a hook"); the flake signature requires " ms for a hook", so detox's "5000ms while"
// (no space, "while") must NOT match.
const REAL_ASSERTION_FAILURE = `
FAIL e2e/PressStyleNative.test.ts (42.1 s)
PressStyleNative
✓ should render the test case screen (900 ms)
✕ should fire pressIn and pressOut events on tap (5300 ms)
● PressStyleNative › should fire pressIn and pressOut events on tap
Exceeded timeout of 5000ms while waiting for element at by.id("simple-press-in-count") to have text "In: 1"
at e2e/PressStyleNative.test.ts:58:8
Test Suites: 1 failed, 6 passed, 7 of 7 total
Tests: 1 failed, 31 passed, 32 total
`
test('classifies a safeLaunchApp connect timeout as a flake', () => {
const { failedFiles, flakeFiles, realFiles } =
classifyDetoxFailures(JUNE9_CONNECT_TIMEOUT)
expect(failedFiles).toEqual(['e2e/PointerEvents.test.ts'])
expect(flakeFiles).toEqual(['e2e/PointerEvents.test.ts'])
expect(realFiles).toEqual([])
})
test('classifies a beforeAll hook timeout as a flake', () => {
const { failedFiles, flakeFiles, realFiles } = classifyDetoxFailures(JUNE5_HOOK_TIMEOUT)
expect(failedFiles).toEqual(['e2e/PressStyleNative.noRngh.test.ts'])
expect(flakeFiles).toEqual(['e2e/PressStyleNative.noRngh.test.ts'])
expect(realFiles).toEqual([])
})
test('does NOT treat a real assertion failure as a flake', () => {
const { failedFiles, flakeFiles, realFiles } =
classifyDetoxFailures(REAL_ASSERTION_FAILURE)
expect(failedFiles).toEqual(['e2e/PressStyleNative.test.ts'])
expect(flakeFiles).toEqual([])
expect(realFiles).toEqual(['e2e/PressStyleNative.test.ts'])
})
test('detox expectation timeout "Exceeded timeout of <n>ms while ..." is NOT a flake', () => {
// the near-collision case: shares the "Exceeded timeout of" prefix with the jest hook
// timeout, but lacks " ms for a hook", so it must be classified real (no retry).
const { flakeFiles, realFiles } = classifyDetoxFailures(REAL_ASSERTION_FAILURE)
expect(flakeFiles).toEqual([])
expect(realFiles).toEqual(['e2e/PressStyleNative.test.ts'])
})
test('mixed flake + real failure: real keeps the file out of the retry set', () => {
const mixed = JUNE9_CONNECT_TIMEOUT + REAL_ASSERTION_FAILURE
const { flakeFiles, realFiles } = classifyDetoxFailures(mixed)
expect(flakeFiles).toEqual(['e2e/PointerEvents.test.ts'])
expect(realFiles).toEqual(['e2e/PressStyleNative.test.ts'])
})
test('strips ANSI color codes before matching', () => {
const colored = `FAIL e2e/PointerEvents.test.ts (188.4 s)\n ● x\n timed out after 70000ms\n`
const { flakeFiles } = classifyDetoxFailures(colored)
expect(flakeFiles).toEqual(['e2e/PointerEvents.test.ts'])
})
test('all-pass output yields no failures', () => {
const passing = `
PASS e2e/PointerEvents.test.ts (120 s)
PASS e2e/TabsOnInteraction.test.ts (67 s)
Test Suites: 7 passed, 7 total
Tests: 32 passed, 32 total
`
const { failedFiles, flakeFiles, realFiles } = classifyDetoxFailures(passing)
expect(failedFiles).toEqual([])
expect(flakeFiles).toEqual([])
expect(realFiles).toEqual([])
})
// June 17: simulator FBSOpenApplicationServiceErrorDomain during test run
const JUNE17_SIMULATOR_LAUNCH_FAILURE = `
FAIL e2e/TabsOnInteraction.test.ts (240.123 s)
TabsOnInteraction
✕ should fire onInteraction with layout for the default selected tab (60000 ms)
● TabsOnInteraction › should fire onInteraction with layout for the default selected tab
An error was encountered processing the command (domain=FBSOpenApplicationServiceErrorDomain, code=4):
Simulator device failed to launch com.hanzogui.hanzoguikitchensink.
Underlying error (domain=FBSOpenApplicationServiceErrorDomain, code=4):
The request to open "com.hanzogui.hanzoguikitchensink" failed.
`
// June 17: teardown timeout causing cascading worker failure
const JUNE17_TEARDOWN_CASCADE = `
FAIL e2e/TabsOnInteraction.test.ts (240.123 s)
TabsOnInteraction
✕ should fire onInteraction with layout for the default selected tab (60000 ms)
● Test suite failed to run
Exceeded timeout of 30000ms while tearing down Detox environment
FAIL e2e/ShorthandVariables.test.ts (180.456 s)
ShorthandVariables
✕ should handle shorthand variables (45000 ms)
● Test suite failed to run
DetoxRuntimeError: Detox worker instance has not been installed in this context (DetoxSecondaryContext).
at get worker (node_modules/detox/src/Detox.js:123:45)
`
test('classifies simulator FBSOpenApplicationServiceErrorDomain as flake', () => {
const { failedFiles, flakeFiles, realFiles } = classifyDetoxFailures(
JUNE17_SIMULATOR_LAUNCH_FAILURE
)
expect(failedFiles).toEqual(['e2e/TabsOnInteraction.test.ts'])
expect(flakeFiles).toEqual(['e2e/TabsOnInteraction.test.ts'])
expect(realFiles).toEqual([])
})
test('classifies teardown timeout + cascading worker failure as flake', () => {
const { failedFiles, flakeFiles, realFiles } = classifyDetoxFailures(
JUNE17_TEARDOWN_CASCADE
)
expect(failedFiles).toEqual([
'e2e/TabsOnInteraction.test.ts',
'e2e/ShorthandVariables.test.ts',
])
expect(flakeFiles).toEqual([
'e2e/TabsOnInteraction.test.ts',
'e2e/ShorthandVariables.test.ts',
])
expect(realFiles).toEqual([])
})
test('real assertion failure with teardown timeout stays real', () => {
// A real failure followed by teardown timeout - the teardown timeout is flake
// but we classify per-file, so this file has both
const mixed = `
FAIL e2e/PressStyleNative.test.ts (42.1 s)
PressStyleNative
✕ should fire pressIn and pressOut events on tap (5300 ms)
● PressStyleNative › should fire pressIn and pressOut events on tap
Exceeded timeout of 5000ms while waiting for element at by.id("simple-press-in-count") to have text "In: 1"
● Test suite failed to run
Exceeded timeout of 30000ms while tearing down Detox environment
`
const { flakeFiles, realFiles } = classifyDetoxFailures(mixed)
// File has both real failure AND teardown timeout - real failure takes priority
expect(flakeFiles).toEqual([])
expect(realFiles).toEqual(['e2e/PressStyleNative.test.ts'])
})