Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/actions/run-fantom-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,28 @@ runs:
uses: ./.github/actions/setup-node
- name: Install node dependencies
uses: ./.github/actions/yarn-install
- name: Read WPT revision
id: wpt-revision
shell: bash
run: |
echo "revision=$(node -e 'process.stdout.write(require("./scripts/web-platform-tests/sync").WPT_REVISION)')" >> "$GITHUB_OUTPUT"
- name: Checkout WPT
uses: actions/checkout@v6
with:
repository: web-platform-tests/wpt
ref: ${{ steps.wpt-revision.outputs.revision }}
path: .wpt
sparse-checkout: |
fetch
resources
streams
tools
wpt
- name: Generate WPT fixtures
shell: bash
run: |
./.wpt/wpt manifest --no-download --rebuild fetch streams
yarn sync-wpt --wpt-root="$GITHUB_WORKSPACE/.wpt"
- name: Download Fantom Runner binary
uses: actions/download-artifact@v7
with:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,11 @@ fix_*.patch
.metro-health-check*

# Jest Integration
/.wpt/
/private/react-native-fantom/build/
/private/react-native-fantom/.out/
/private/react-native-fantom/tester/build/
/packages/react-native/src/private/webapis/__tests__/wpt/generated/

# [Experimental] Generated TS type definitions
/packages/**/types_generated/
Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ vendor
packages/**/types_generated/

packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeEnumTurboModule.js
packages/react-native/src/private/webapis/__tests__/wpt/generated/wpt-fixtures.json
packages/react-native/src/private/webapis/__tests__/wpt/wpt-baseline.json
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@
"test-ios": "./scripts/objc-test.sh test",
"test-typescript-legacy": "tsc -p packages/react-native/__typetests__/tsconfig.legacy.json",
"test-generated-typescript": "tsc -p packages/react-native/__typetests__/tsconfig.json",
"test-wpt": "yarn fantom 'WPT(Fetch|Streams)-itest'",
"test": "jest",
"fantom": "./scripts/fantom.sh",
"fantom-cli": "./scripts/fantom-cli.sh",
"sync-wpt": "node ./scripts/web-platform-tests/sync.js",
"update-wpt-baseline": "node ./scripts/web-platform-tests/update-baseline.js",
"trigger-react-native-release": "node ./scripts/releases-local/trigger-react-native-release.js",
"update-lock": "npx yarn-deduplicate"
},
Expand Down
103 changes: 103 additions & 0 deletions packages/react-native/src/private/webapis/__tests__/wpt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Web Platform Tests for Fetch and Streams

These tests run the shell-compatible portions of the upstream Web Platform
Tests (WPT) `fetch/` and `streams/` suites inside React Native's Fantom runtime.
The committed `wpt-baseline.json` results are an expectation baseline: both an
unexpected failure and an unexpected pass fail CI until the baseline is
deliberately updated.

The fixtures are generated on demand and are not committed. They are pinned to
the WPT revision named by `WPT_REVISION` in
`scripts/web-platform-tests/sync.js` and contain byte-for-byte copies of the
upstream test sources, their `// META: script` dependencies, and WPT's
`testharness.js`. WPT is distributed under the
[3-Clause BSD License](https://github.com/web-platform-tests/wpt/blob/master/LICENSE.md).
The runner recognizes a `test(() => {}, ...)` registration without changing
its source so that assertion-free markers remain excluded from conformance
counts. The current expected results live in `wpt-baseline.json`; the generated
fixture also records every test file excluded by the environment boundary and
its reason.

Selection is derived from WPT's `MANIFEST.json`, not from a file allowlist. A
source is runnable when the manifest classifies it as a `testharness` `.any.js`
test with a dedicated-worker variant and its declared scripts and reachable
host APIs are available in Fantom. Unsupported records include the manifest
type, generated URLs, declared globals/scripts, and the detected requirement.

## Running the suites

From the React Native repository root:

```shell
yarn test-wpt
```

Fantom `-itest.js` files are part of the existing Fantom CI job, so the GitHub
Actions job checks out the pinned WPT revision, generates the fixture, and then
runs these suites. The CI tests execute the files in a deterministic shuffled
order and compare them with a baseline captured in manifest order. This makes
an order-dependent result fail the suite.

An expected result can be changed to `FLAKY` in the baseline when it is known to
be intermittent. A flaky result accepts either a pass or a failure in CI, and
the baseline updater carries the `FLAKY` status forward by file, test name, and
duplicate-name occurrence. It must be removed manually once the test is stable.

When a suite has no conformance passes and every ordinary subtest failure names
a runtime global that is actually absent, the result is reported as `BLOCKED`.
The baseline records the dominant missing global, all missing globals, and the
number of gated files and subtests instead of retaining hundreds of equivalent
failure rows. Harness-generated failures such as file-evaluation errors remain
visible separately.

## Updating WPT and the baseline

1. Obtain a checkout of
[web-platform-tests/wpt](https://github.com/web-platform-tests/wpt) at the
revision named by `WPT_REVISION` in
`scripts/web-platform-tests/sync.js`. To move to a newer upstream revision,
update that constant first.
2. Generate WPT's manifest at that revision:

```shell
./wpt manifest --no-download --rebuild fetch streams
```

3. Regenerate the local fixture:

```shell
yarn sync-wpt --wpt-root=/absolute/path/to/wpt
```

The fixture is ignored by Git. The generator verifies the checkout revision
and every selected or excluded test source against its manifest Git-blob
hash.
4. Review the manifest-derived runnable and unsupported inventory in the
generated fixture. If the environment capability rules have changed, update
the classifier in `sync.js`.
5. Record current React Native behavior and verify the new baseline:

```shell
yarn update-wpt-baseline
```

6. Review every changed result before accepting the new baseline. The update
command preserves existing `FLAKY` statuses and reruns `yarn test-wpt`
against the newly written baseline.

## Environment limitations

Fantom supplies React Native's real JavaScript globals and Hermes runtime, but
its HTTP client is intentionally a non-completing stub. Fetch tests that require
the WPT HTTP(S) server therefore cannot run without changing the Fantom native
test environment. Browser-only tests also cannot run because Fantom has no DOM,
navigation, Window, service worker, or shared worker environment.

The Streams suite runs `.any.js` tests that are compatible with a JavaScript
shell. WebIDL harness tests, explicit garbage-collection tests, and tests that
require browser transferables such as `MessagePort` or `VideoFrame` are recorded
as unsupported in the generated fixture.

The adapter processes direct `// META: script=` dependencies. Other WPT
execution metadata, nested dependency metadata, and browser or worker realm
creation are not implemented.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

import type {WPTSuiteResult} from './WPTTestHarness';

export type WPTBaseline = {
fetch: WPTSuiteResult,
streams: WPTSuiteResult,
};

// $FlowExpectedError[untyped-import] JSON cannot carry a Flow declaration.
const baseline = require('./wpt-baseline.json') as WPTBaseline;

export default baseline;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';

import baseline from './WPTBaseline';
import fixtures from './WPTFixtures';
import {applyWPTFlakyStatuses, runWPTSuite} from './WPTTestHarness';

const BASELINE_MARKER = '__RN_WPT_BASELINE__';
describe('Web Platform Tests baseline capture', () => {
it('captures fetch', () => {
const result = applyWPTFlakyStatuses(
runWPTSuite(fixtures, 'fetch'),
baseline.fetch,
);
console.log(`${BASELINE_MARKER}fetch:${JSON.stringify(result)}`);
});

it('captures streams', () => {
const result = applyWPTFlakyStatuses(
runWPTSuite(fixtures, 'streams'),
baseline.streams,
);
console.log(`${BASELINE_MARKER}streams:${JSON.stringify(result)}`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

import type {WPTSuiteResult} from './WPTTestHarness';

import baseline from './WPTBaseline';
import fixtures from './WPTFixtures';
import {
applyWPTFlakyStatuses,
getWPTExecutionPaths,
runWPTSuite,
} from './WPTTestHarness';

import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';

function singleTestResult(status: string): WPTSuiteResult {
return {
files: [
{
harnessStatus: 'OK',
path: 'example.any.js',
tests: [{name: 'intermittent result', status}],
},
],
manifest: {sha256: 'test', version: 0},
revision: 'test',
source: 'test',
suite: 'fetch',
summary: {
BLOCKED: 0,
FAIL: status === 'FAIL' ? 1 : 0,
FLAKY: status === 'FLAKY' ? 1 : 0,
NOTRUN: 0,
PASS: status === 'PASS' ? 1 : 0,
PRECONDITION_FAILED: 0,
TIMEOUT: 0,
noOpTests: 0,
total: 1,
unsupportedFiles: 0,
},
};
}

describe('Web Platform Tests: fetch', () => {
it('uses a non-manifest deterministic order', () => {
expect(
getWPTExecutionPaths(fixtures, 'fetch', 'deterministic-shuffle'),
).not.toEqual(getWPTExecutionPaths(fixtures, 'fetch', 'manifest'));
});

it('matches the manifest-order baseline when shuffled', () => {
const result = runWPTSuite(fixtures, 'fetch', 'deterministic-shuffle');
expect(applyWPTFlakyStatuses(result, baseline.fetch)).toEqual(
baseline.fetch,
);
});

it('preserves a flaky baseline result for either outcome', () => {
const flakyBaseline = singleTestResult('FLAKY');
expect(
applyWPTFlakyStatuses(singleTestResult('PASS'), flakyBaseline),
).toEqual(flakyBaseline);
expect(
applyWPTFlakyStatuses(singleTestResult('FAIL'), flakyBaseline),
).toEqual(flakyBaseline);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

import type {WPTFixtures} from './WPTTestHarness';

let fixtures: WPTFixtures;
try {
// $FlowExpectedError[untyped-import] JSON cannot carry a Flow declaration.
// $FlowExpectedError[cannot-resolve-module] Generated by sync.js, not committed.
fixtures = require('./generated/wpt-fixtures.json') as WPTFixtures;
} catch {
throw new Error(
'wpt-fixtures.json not found. Run: node scripts/web-platform-tests/sync.js --wpt-root=/path/to/wpt',
);
}

export default fixtures;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';

import baseline from './WPTBaseline';
import fixtures from './WPTFixtures';
import {
applyWPTFlakyStatuses,
getWPTExecutionPaths,
runWPTSuite,
} from './WPTTestHarness';

describe('Web Platform Tests: streams', () => {
it('uses a non-manifest deterministic order', () => {
expect(
getWPTExecutionPaths(fixtures, 'streams', 'deterministic-shuffle'),
).not.toEqual(getWPTExecutionPaths(fixtures, 'streams', 'manifest'));
});

it('matches the manifest-order baseline when shuffled', () => {
const result = runWPTSuite(fixtures, 'streams', 'deterministic-shuffle');
expect(applyWPTFlakyStatuses(result, baseline.streams)).toEqual(
baseline.streams,
);
});
});
Loading
Loading