Skip to content

Commit 4e3fff0

Browse files
committed
fix(runtime): require test context
1 parent aa33ab8 commit 4e3fff0

10 files changed

Lines changed: 80 additions & 46 deletions

File tree

apps/playground/rn-harness.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export default {
7979
}),
8080
applePlatform({
8181
name: 'ios',
82-
device: appleSimulator('iPhone 17 Pro', '26.2'),
82+
device: appleSimulator('iPhone 17 Pro', '26.4'),
8383
bundleId: 'com.harnessplayground',
8484
}),
8585
applePlatform({

apps/playground/src/__tests__/smoke.harness.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,17 @@ describe('Smoke test', () => {
44
test('should run a simple test', () => {
55
expect(1 + 1).toBe(2);
66
});
7+
8+
test('should expose task context to tests', (context) => {
9+
expect(context).toBeDefined();
10+
expect(context.task.type).toBe('test');
11+
expect(context.task.mode).toBe('run');
12+
expect(context.task.file.name).toBe('src/__tests__/smoke.harness.ts');
13+
expect(context.task.suite.name).toBe('Smoke test');
14+
expect(context.task.name).toBe('should expose task context to tests');
15+
});
16+
17+
test('should report dynamic skips as skipped', (context) => {
18+
context.skip('skip from test context');
19+
});
720
});

packages/bridge/src/shared/test-collector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { HarnessTestContext } from './test-context.js';
22

33
export type TestStatus = 'active' | 'skipped' | 'todo';
44

5-
export type TestFn = (context?: HarnessTestContext) => void | Promise<void>;
5+
export type TestFn = (context: HarnessTestContext) => void | Promise<void>;
66

77
export type SuiteHookFn = () => void | Promise<void>;
88

packages/coverage-ios/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": "../config"
8+
},
69
{
710
"path": "./tsconfig.lib.json"
811
}

packages/coverage-ios/tsconfig.lib.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,10 @@
1010
"types": ["node"],
1111
"lib": ["DOM", "ES2022"]
1212
},
13-
"include": ["src/**/*.ts"]
13+
"include": ["src/**/*.ts"],
14+
"references": [
15+
{
16+
"path": "../config/tsconfig.lib.json"
17+
}
18+
]
1419
}

packages/runtime/src/__tests__/runner-context.test.ts

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,11 @@ vi.mock('../symbolicate.js', async () => {
1515
};
1616
});
1717

18-
const getTask = (context?: HarnessTestContext) => {
19-
if (!context) {
20-
throw new Error('Expected test context to be defined');
21-
}
22-
18+
const getTask = (context: HarnessTestContext) => {
2319
return context.task;
2420
};
2521

26-
const getTaskContext = (context?: HarnessTestContext) => {
27-
if (!context) {
28-
throw new Error('Expected test context to be defined');
29-
}
30-
22+
const getTaskContext = (context: HarnessTestContext) => {
3123
return context;
3224
};
3325

@@ -49,15 +41,15 @@ describe('runner task context', () => {
4941
try {
5042
const collection = await collector.collect(() => {
5143
harnessDescribe('Task Context Suite', () => {
52-
beforeEach((context: HarnessTestContext | undefined) => {
44+
beforeEach((context: HarnessTestContext) => {
5345
observedTasks.push({ source: 'beforeEach', task: getTask(context) });
5446
});
5547

56-
afterEach((context: HarnessTestContext | undefined) => {
48+
afterEach((context: HarnessTestContext) => {
5749
observedTasks.push({ source: 'afterEach', task: getTask(context) });
5850
});
5951

60-
harnessIt('exposes task metadata', (context: HarnessTestContext | undefined) => {
52+
harnessIt('exposes task metadata', (context: HarnessTestContext) => {
6153
observedTasks.push({ source: 'test', task: getTask(context) });
6254
});
6355
});
@@ -160,7 +152,7 @@ describe('runner task context', () => {
160152
calls.push('afterEach');
161153
});
162154

163-
harnessIt('skips from context', (context: HarnessTestContext | undefined) => {
155+
harnessIt('skips from context', (context: HarnessTestContext) => {
164156
const { skip } = getTaskContext(context);
165157

166158
calls.push('before-skip');
@@ -204,7 +196,7 @@ describe('runner task context', () => {
204196
try {
205197
const collection = await collector.collect(() => {
206198
harnessDescribe('Conditional Skip Suite', () => {
207-
harnessIt('continues when condition is false', (context: HarnessTestContext | undefined) => {
199+
harnessIt('continues when condition is false', (context: HarnessTestContext) => {
208200
const { skip } = getTaskContext(context);
209201

210202
calls.push('before');
@@ -240,7 +232,7 @@ describe('runner task context', () => {
240232
calls.push('afterEach');
241233
});
242234

243-
harnessIt('runs finished callbacks', (context: HarnessTestContext | undefined) => {
235+
harnessIt('runs finished callbacks', (context: HarnessTestContext) => {
244236
const { onTestFinished } = getTaskContext(context);
245237

246238
onTestFinished(() => {
@@ -288,7 +280,7 @@ describe('runner task context', () => {
288280

289281
harnessIt(
290282
'runs finished callback after skip',
291-
(context: HarnessTestContext | undefined) => {
283+
(context: HarnessTestContext) => {
292284
const { onTestFinished, skip } = getTaskContext(context);
293285

294286
onTestFinished(() => {
@@ -330,7 +322,7 @@ describe('runner task context', () => {
330322

331323
harnessIt(
332324
'runs finished callback after failure',
333-
(context: HarnessTestContext | undefined) => {
325+
(context: HarnessTestContext) => {
334326
const { onTestFinished } = getTaskContext(context);
335327

336328
onTestFinished(() => {
@@ -370,7 +362,7 @@ describe('runner task context', () => {
370362
calls.push('afterEach');
371363
});
372364

373-
harnessIt('runs failed callbacks', (context: HarnessTestContext | undefined) => {
365+
harnessIt('runs failed callbacks', (context: HarnessTestContext) => {
374366
const { onTestFailed } = getTaskContext(context);
375367

376368
onTestFailed(() => {
@@ -419,7 +411,7 @@ describe('runner task context', () => {
419411

420412
harnessIt(
421413
'does not run failed callbacks on skip',
422-
(context: HarnessTestContext | undefined) => {
414+
(context: HarnessTestContext) => {
423415
const { onTestFailed, skip } = getTaskContext(context);
424416

425417
onTestFailed(() => {
@@ -462,7 +454,7 @@ describe('runner task context', () => {
462454

463455
harnessIt(
464456
'runs failed callback after afterEach failure',
465-
(context: HarnessTestContext | undefined) => {
457+
(context: HarnessTestContext) => {
466458
const { onTestFailed } = getTaskContext(context);
467459

468460
onTestFailed(() => {

packages/runtime/src/collector/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
type HarnessTestContext,
66
} from '@react-native-harness/bridge';
77

8-
export type TestFn = (context?: HarnessTestContext) => void | Promise<void>;
8+
export type TestFn = (context: HarnessTestContext) => void | Promise<void>;
99

1010
export type SuiteHookFn = () => void | Promise<void>;
1111

packages/runtime/src/react-native.d.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ declare module 'react-native/Libraries/Core/Devtools/parseErrorStack' {
1717
export default function parseErrorStack(errorStack?: string): StackFrame[];
1818
}
1919

20-
declare module 'react-native-url-polyfill' {
21-
export const URL: typeof globalThis.URL;
22-
}
23-
2420
declare module '*.png' {
2521
import type { ImageSourcePropType } from 'react-native';
2622

packages/runtime/src/runner/hooks.ts

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,29 @@ export type HookType = 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll';
55

66
const collectInheritedHooks = (
77
suite: TestSuite,
8-
hookType: HookType
9-
): Array<TestFn | SuiteHookFn> => {
10-
const hooks: Array<TestFn | SuiteHookFn> = [];
8+
hookType: 'beforeEach' | 'afterEach'
9+
): TestFn[] => {
10+
const hooks: TestFn[] = [];
11+
const suiteChain: TestSuite[] = [];
12+
13+
let current: TestSuite | undefined = suite;
14+
while (current) {
15+
suiteChain.unshift(current);
16+
current = current.parent;
17+
}
18+
19+
for (const currentSuite of suiteChain) {
20+
hooks.push(...currentSuite[hookType]);
21+
}
22+
23+
return hooks;
24+
};
25+
26+
const collectSuiteHooks = (
27+
suite: TestSuite,
28+
hookType: 'beforeAll' | 'afterAll'
29+
): SuiteHookFn[] => {
30+
const hooks: SuiteHookFn[] = [];
1131
const suiteChain: TestSuite[] = [];
1232

1333
// Collect all suites from current to root
@@ -17,23 +37,15 @@ const collectInheritedHooks = (
1737
currentSuite = currentSuite.parent;
1838
}
1939

20-
if (hookType === 'beforeEach' || hookType === 'beforeAll') {
21-
// For beforeEach/beforeAll: run parent hooks first (reverse the chain)
40+
if (hookType === 'beforeAll') {
41+
// Run parent suite hooks before child suite hooks.
2242
for (let i = suiteChain.length - 1; i >= 0; i--) {
23-
if (hookType === 'beforeEach') {
24-
hooks.push(...suiteChain[i].beforeEach);
25-
} else {
26-
hooks.push(...suiteChain[i].beforeAll);
27-
}
43+
hooks.push(...suiteChain[i].beforeAll);
2844
}
2945
} else {
30-
// For afterEach/afterAll: run child hooks first (use chain as-is)
46+
// Run child suite hooks before parent suite hooks.
3147
for (const suiteInChain of suiteChain) {
32-
if (hookType === 'afterEach') {
33-
hooks.push(...suiteInChain.afterEach);
34-
} else {
35-
hooks.push(...suiteInChain.afterAll);
36-
}
48+
hooks.push(...suiteInChain.afterAll);
3749
}
3850
}
3951

@@ -45,9 +57,19 @@ export const runHooks = async (
4557
hookType: HookType,
4658
context?: ActiveTestContext,
4759
): Promise<void> => {
60+
if (hookType === 'beforeAll' || hookType === 'afterAll') {
61+
const hooks = collectSuiteHooks(suite, hookType);
62+
63+
for (const hook of hooks) {
64+
await hook();
65+
}
66+
67+
return;
68+
}
69+
4870
const hooks = collectInheritedHooks(suite, hookType);
4971

5072
for (const hook of hooks) {
51-
await hook(context);
73+
await hook(context as ActiveTestContext);
5274
}
5375
};

tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
},
6363
{
6464
"path": "./packages/coverage-ios"
65+
},
66+
{
67+
"path": "./website"
6568
}
6669
]
6770
}

0 commit comments

Comments
 (0)