Skip to content

Commit 1a99abd

Browse files
committed
feat: added webPlatform enabling react-native-web testing
1 parent 1d7c811 commit 1a99abd

23 files changed

Lines changed: 1187 additions & 51 deletions

apps/playground/index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@
22
* @format
33
*/
44

5-
import { AppRegistry } from 'react-native';
5+
import { AppRegistry, Platform } from 'react-native';
66
import App from './src/app/App';
77
import { name as appName } from './app.json';
88

99
AppRegistry.registerComponent(appName, () => App);
10+
11+
if (Platform.OS === 'web') {
12+
AppRegistry.runApplication(appName, {
13+
rootTag: document.getElementById('root'),
14+
});
15+
}

apps/playground/metro.config.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const { withNxMetro } = require('@nx/react-native');
22
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
33
const path = require('path');
4+
const fs = require('fs');
45

56
const defaultConfig = getDefaultConfig(__dirname);
67

@@ -17,7 +18,32 @@ const customConfig = {
1718
cacheVersion: '@react-native-harness/playground',
1819
resolver: {
1920
unstable_enablePackageExports: true,
21+
2022
},
23+
server: {
24+
...(defaultConfig.server || {}),
25+
enhanceMiddleware: (middleware, server) => {
26+
return (req, res, next) => {
27+
// Serve our HTML shell at / and /index.html
28+
if (req.url === '/' || req.url === '/index.html') {
29+
const htmlPath = path.join(projectRoot, 'web', 'index.html');
30+
31+
fs.readFile(htmlPath, 'utf8', (err, data) => {
32+
if (err) {
33+
res.writeHead(500, { 'Content-Type': 'text/plain' });
34+
res.end('Error loading index.html: ' + err.message);
35+
return;
36+
}
37+
38+
res.writeHead(200, { 'Content-Type': 'text/html' });
39+
res.end(data);
40+
});
41+
return;
42+
}
43+
return middleware(req, res, next);
44+
};
45+
},
46+
}
2147
};
2248

2349
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
@@ -26,4 +52,30 @@ module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
2652
path.resolve(projectRoot, 'node_modules'),
2753
path.resolve(monorepoRoot, 'node_modules'),
2854
],
55+
}).then((config) => {
56+
const defaultResolveRequest = config.resolver.resolveRequest;
57+
config.resolver.resolveRequest = (context, moduleName, platform) => {
58+
if (platform === 'web') {
59+
60+
if (moduleName.includes('NativeSourceCode') ||
61+
moduleName.includes('NativePlatformConstants') ||
62+
moduleName.includes('NativeDevSettings') ||
63+
moduleName.includes('NativeLogBox') ||
64+
moduleName.includes('NativeRedBox')
65+
) {
66+
return {
67+
type: 'empty',
68+
};
69+
} else if (moduleName === 'react-native') {
70+
return {
71+
type: 'sourceFile',
72+
filePath: require.resolve('react-native-web'),
73+
};
74+
}
75+
}
76+
// Everything else: default behavior
77+
return defaultResolveRequest(context, moduleName, platform);
78+
};
79+
80+
return config;
2981
});

apps/playground/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
},
88
"dependencies": {
99
"react": "19.1.1",
10-
"react-native": "0.82.1"
10+
"react-dom": "19.1.1",
11+
"react-native": "0.82.1",
12+
"react-native-web": "^0.21.2"
1113
},
1214
"devDependencies": {
1315
"react-native-harness": "workspace:*",
@@ -22,6 +24,7 @@
2224
"jest": "^30.2.0",
2325
"@react-native-harness/platform-android": "workspace:*",
2426
"@react-native-harness/platform-apple": "workspace:*",
25-
"@react-native-harness/platform-vega": "workspace:*"
27+
"@react-native-harness/platform-vega": "workspace:*",
28+
"@react-native-harness/platform-web": "workspace:*"
2629
}
2730
}

apps/playground/rn-harness.config.mjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import {
1212
vegaPlatform,
1313
vegaEmulator,
1414
} from '@react-native-harness/platform-vega';
15+
import {
16+
webPlatform,
17+
} from '@react-native-harness/platform-web';
1518

1619
const config = {
1720
entryPoint: './index.js',
@@ -48,6 +51,21 @@ const config = {
4851
device: vegaEmulator('VegaTV_1'),
4952
bundleId: 'com.playground',
5053
}),
54+
webPlatform({
55+
name: 'web:chrome',
56+
browserName: 'chrome',
57+
appUrl: 'http://localhost:8081/index.html',
58+
}),
59+
webPlatform({
60+
name: 'web:firefox',
61+
browserName: 'firefox',
62+
appUrl: 'http://localhost:8081/index.html',
63+
}),
64+
webPlatform({
65+
name: 'web:safari',
66+
browserName: 'safari',
67+
appUrl: 'http://localhost:8081/index.html',
68+
}),
5169
],
5270
defaultRunner: 'android',
5371
bridgeTimeout: 120000,

apps/playground/tsconfig.app.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
"react-native-harness.d.ts"
3434
],
3535
"references": [
36+
{
37+
"path": "../../packages/platform-web/tsconfig.lib.json"
38+
},
3639
{
3740
"path": "../../packages/platform-vega/tsconfig.lib.json"
3841
},

apps/playground/tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
"files": [],
44
"include": [],
55
"references": [
6+
{
7+
"path": "../../packages/platform-web"
8+
},
69
{
710
"path": "../../packages/platform-vega"
811
},

apps/playground/web/index.html

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>react-native-harness</title>
6+
<meta name="viewport" content="width=device-width,initial-scale=1" />
7+
<style>
8+
/* These styles make the body full-height */
9+
html,
10+
body {
11+
height: 100%;
12+
}
13+
/* These styles disable body scrolling if you are using <ScrollView> */
14+
body {
15+
overflow: hidden;
16+
}
17+
/* These styles make the root element full-height */
18+
#root {
19+
display: flex;
20+
height: 100%;
21+
}
22+
</style>
23+
</head>
24+
<body>
25+
<div id="root"></div>
26+
<!-- Load Metro bundle -->
27+
<script src="http://localhost:8081/index.bundle?platform=web&dev=true&entryFile=index.js"></script>
28+
</body>
29+
</html>

packages/bridge/src/shared.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export type {
3939
export { DeviceNotRespondingError } from './errors.js';
4040

4141
export type DeviceDescriptor = {
42-
platform: 'ios' | 'android' | 'vega';
42+
platform: 'ios' | 'android' | 'vega' | 'web';
4343
manufacturer: string;
4444
model: string;
4545
osVersion: string;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import baseConfig from '../../eslint.config.mjs';
2+
3+
export default [
4+
...baseConfig,
5+
{
6+
files: ['**/*.json'],
7+
rules: {
8+
'@nx/dependency-checks': [
9+
'error',
10+
{
11+
ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}'],
12+
},
13+
],
14+
},
15+
languageOptions: {
16+
parser: await import('jsonc-eslint-parser'),
17+
},
18+
},
19+
];

packages/platform-web/package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "@react-native-harness/platform-web",
3+
"description": "Web platform for React Native Harness",
4+
"version": "1.0.0-alpha.19",
5+
"type": "module",
6+
"main": "./dist/index.js",
7+
"module": "./dist/index.js",
8+
"types": "./dist/index.d.ts",
9+
"exports": {
10+
"./package.json": "./package.json",
11+
".": {
12+
"development": "./src/index.ts",
13+
"types": "./dist/index.d.ts",
14+
"import": "./dist/index.js",
15+
"default": "./dist/index.js"
16+
}
17+
},
18+
"dependencies": {
19+
"@react-native-harness/platforms": "workspace:*",
20+
"zod": "^3.25.67",
21+
"tslib": "^2.3.0",
22+
"webdriver": "^9.20.1"
23+
},
24+
"license": "MIT"
25+
}

0 commit comments

Comments
 (0)