Skip to content

chore(🐙): keep e2e runs on the app's socket and fail an image check without a reference on CI - #549

Open
YevheniiKotyrlo wants to merge 4 commits into
wcandillon:mainfrom
YevheniiKotyrlo:chore/e2e-lane
Open

YevheniiKotyrlo wants to merge 4 commits into
wcandillon:mainfrom
YevheniiKotyrlo:chore/e2e-lane

Conversation

@YevheniiKotyrlo

@YevheniiKotyrlo YevheniiKotyrlo commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Two fixes to the e2e harness, so that a red run means a failing test and a green one means every check compared something.

Problem

globalSetup.ts makes every socket the test server accepts global.testClient before the socket says hello. The example app opens a new socket on every retry, and a test file keeps the first value a global had when it read it (jest-environment-node installs a lazy getter per global), so a connection that never says hello takes over every test file that starts after it: each eval is sent to it and waits out its 30 s. Teardown then closes only that last socket and leaves the adb reverse mapping on the device. RemoteTestingClient.handleResponse also reads this.client again when its timeout fires, so a hello arriving while an eval waits takes the listener off the new socket and leaves it on the old one, and an eval with no client arms its timeout before it throws.

checkImage writes a reference it does not find and returns 0, so on CI a check without a stored image compares nothing and passes, and the image it drew only reaches the uploaded artifact. Every check on main has its reference today, so this is about the next check that lands without one.

Solution

The client is the socket that said hello last, held in global.testConnection, an object globalSetup creates before any test file loads, so every file reads the current client through it. Teardown closes every socket the server accepted and both servers, and removes the port mapping setup made. An eval reads its socket once, before arming its timeout.

On CI, a check without a reference fails and writes its image as <reference>.test.png, the name a failed comparison already uses and the workflows already upload. Outside CI it still writes the reference, which is how one is made; overwrite still writes it everywhere. config.ts reads CI with the suite's other flags.

These are wcandillon/react-native-skia#4240 and #4231 for this repo.

Tests

globalSetup.spec.ts drives the real server on a port of its own: an eval goes to the app that said hello rather than to a connection that never did, the client moves to the socket that says hello next, and teardown closes every socket and removes the mapping. RemoteTestingClient.spec.ts drives an eval over fake sockets: it times out on the socket it was sent to when another app says hello meanwhile, and rejects without arming a timer when no app has said hello. checkImage.spec.ts: on CI a check without a reference fails and leaves <reference>.test.png, outside CI it writes the reference, and on CI a present reference is compared and leaves no .test.png.

Against main's harness six of the nine fail: the eval reaches the silent socket, the app's socket stays open, nothing removes the mapping, the listener stays on the first socket, an eval with no client throws a TypeError instead of its own error, and the check without a reference passes on CI. With the change all nine pass.

Verification

yarn lint · yarn tsc · yarn turbo run build --filter='./packages/*' · the Expo template's web build · yarn test:plugin · yarn check-dawn · yarn build:docs. CI's Android e2e step (debug build, Metro and jest with CI=true) on an Android 15 emulator (API 35): 45 of 47 suites and 228 of 230 tests passed, with no check failing for a missing reference and no image written. The two failures, the YUV cases of ExternalTextureColorSpace and NativeBufferWrapper, fail on that emulator with main too (219 of 221 there; they render on the host's GPU). On devices the whole suite passes, 230 of 230: on a Galaxy S23 (Android 14), and on an iPhone 12 Pro Max (iOS 27.0.1) built and run as CI's iOS job does on a simulator (Debug build, Metro with CI=true). main passes 221 of 221 on both. Not run here: the semantic-release dry run, which needs the repository's token.

globalSetup made every socket the test server accepted the tests' client
before it said hello, and a test file keeps the first value a global had
when it read it, so a connection that never says hello takes over every
test file that starts after it: each eval waits out its 30 s. The client is
now the socket that said hello last, test files read it through an object
set up before any of them load, and teardown closes every socket the server
accepted and removes the adb port mapping setup made.

An eval reads the client once, before arming its timeout, so a hello
arriving while it waits no longer moves its timeout to the new socket, and
an eval with no client rejects without leaving a timer behind.
checkImage wrote a missing reference and passed, so on CI a check with no
stored image compared nothing. On CI it now fails and writes the image as
<reference>.test.png, where the workflows already upload failed checks.
Outside CI it still writes the reference, which is how one is made.
@YevheniiKotyrlo

Copy link
Copy Markdown
Contributor Author

The three new specs against main's harness (4f3f7fd7; the two harness specs read global.testClient there, and RemoteTestingClient is exported for the run): 6 failed, 3 passed.

Test On main
e2e test server › talks to the app that said hello, not to a connection that never did Expected: "app", Received: "silent"
e2e test server › closes every socket it accepted when the run ends the app's socket never closes: Exceeded timeout of 5000 ms
e2e test server › removes the port mapping it made when the run ends Expected: ["reverse", "--remove", "tcp:0"], Received: undefined
RemoteTestingClient › times out on the socket it sent the eval to when another app says hello meanwhile the first socket keeps its listener: Expected: 0, Received: 1
RemoteTestingClient › rejects without arming its timeout while no app has said hello Cannot read properties of undefined (reading 'once')
checkImage › fails a check without a reference on CI, keeping its image as a failed check's Received function did not throw

With this change: 9 passed.

The whole suite through CI's Android e2e step on an Android 15 emulator (API 35), Metro and jest with CI=true: Test Suites: 1 failed, 45 passed, 46 total and Tests: 1 failed, 224 passed, 225 total. The failure is ExternalTextureColorSpace's YUV case, which fails with main on that emulator too.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant