@react-native-harness/runtime 1.3.0 → 1.4.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@react-native-harness/runtime",
3
3
  "description": "The core test runtime that executes on React Native devices, providing Jest-compatible APIs (describe, it, expect) and managing test collection, execution, and result reporting in native environments.",
4
- "version": "1.3.0",
4
+ "version": "1.4.0-rc.1",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -47,7 +47,7 @@
47
47
  "react-native-url-polyfill": "^3.0.0",
48
48
  "use-sync-external-store": "^1.6.0",
49
49
  "zustand": "^5.0.5",
50
- "@react-native-harness/bridge": "1.3.0"
50
+ "@react-native-harness/bridge": "1.4.0-rc.1"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/chai": "^5.2.2"
@@ -439,6 +439,55 @@ describe('runner task context', () => {
439
439
  }
440
440
  });
441
441
 
442
+ it('returns skipped descendants for describe.skip()', async () => {
443
+ const collector = getTestCollector();
444
+ const runner = getTestRunner();
445
+
446
+ try {
447
+ const collection = await collector.collect(() => {
448
+ harnessDescribe.skip('Skipped Suite', () => {
449
+ harnessIt('skipped test', () => undefined);
450
+
451
+ harnessDescribe('Nested Suite', () => {
452
+ harnessIt('nested skipped test', () => undefined);
453
+ });
454
+ });
455
+ }, 'runtime/describe-skip-descendants.test.ts');
456
+
457
+ const result = await runner.run({
458
+ testSuite: collection.testSuite,
459
+ testFilePath: 'runtime/describe-skip-descendants.test.ts',
460
+ runner: 'ios',
461
+ });
462
+
463
+ expect(result.suites[0]).toMatchObject({
464
+ name: 'Skipped Suite',
465
+ status: 'skipped',
466
+ tests: [
467
+ {
468
+ name: 'skipped test',
469
+ status: 'skipped',
470
+ },
471
+ ],
472
+ suites: [
473
+ {
474
+ name: 'Nested Suite',
475
+ status: 'skipped',
476
+ tests: [
477
+ {
478
+ name: 'nested skipped test',
479
+ status: 'skipped',
480
+ },
481
+ ],
482
+ },
483
+ ],
484
+ });
485
+ } finally {
486
+ collector.dispose();
487
+ runner.dispose();
488
+ }
489
+ });
490
+
442
491
  it('runs onTestFailed when afterEach fails', async () => {
443
492
  const calls: string[] = [];
444
493
  const collector = getTestCollector();
@@ -76,10 +76,19 @@ const convertRawTestCaseToTestCase = (
76
76
  rawTest: RawTestCase,
77
77
  suiteContext: { hasFocusedTests: boolean }
78
78
  ): TestCase => {
79
+ const declarationMode = rawTest.options.todo
80
+ ? 'todo'
81
+ : rawTest.options.skip
82
+ ? 'skip'
83
+ : rawTest.options.only
84
+ ? 'only'
85
+ : undefined;
86
+
79
87
  return {
80
88
  name: rawTest.name,
81
89
  fn: rawTest.fn,
82
90
  status: computeTestStatus(rawTest, suiteContext),
91
+ declarationMode,
83
92
  };
84
93
  };
85
94
 
@@ -21,6 +21,55 @@ import {
21
21
  runOnTestFinished,
22
22
  } from './test-context.js';
23
23
 
24
+ const getAncestorTitles = (suite: TestSuite): string[] => {
25
+ const ancestorTitles: string[] = [];
26
+ let currentSuite = suite.parent;
27
+
28
+ while (currentSuite) {
29
+ if (currentSuite.name !== 'root') {
30
+ ancestorTitles.unshift(currentSuite.name);
31
+ }
32
+ currentSuite = currentSuite.parent;
33
+ }
34
+
35
+ if (suite.name !== 'root') {
36
+ ancestorTitles.push(suite.name);
37
+ }
38
+
39
+ return ancestorTitles;
40
+ };
41
+
42
+ const getFullName = (ancestorTitles: string[], testName: string): string =>
43
+ [...ancestorTitles, testName].join(' ');
44
+
45
+ const emitTestFinished = (
46
+ context: TestRunnerContext,
47
+ options: {
48
+ test: TestCase;
49
+ suite: TestSuite;
50
+ startedAt: number;
51
+ duration: number;
52
+ status: 'passed' | 'failed' | 'skipped' | 'todo';
53
+ error?: TestResult['error'];
54
+ },
55
+ ) => {
56
+ const ancestorTitles = getAncestorTitles(options.suite);
57
+
58
+ context.events.emit({
59
+ type: 'test-finished',
60
+ file: context.testFilePath,
61
+ suite: options.suite.name,
62
+ name: options.test.name,
63
+ ancestorTitles,
64
+ fullName: getFullName(ancestorTitles, options.test.name),
65
+ startedAt: options.startedAt,
66
+ declarationMode: options.test.declarationMode,
67
+ duration: options.duration,
68
+ error: options.error,
69
+ status: options.status,
70
+ });
71
+ };
72
+
24
73
  declare global {
25
74
  var HARNESS_TEST_PATH: string;
26
75
  }
@@ -30,7 +79,7 @@ const runTest = async (
30
79
  suite: TestSuite,
31
80
  context: TestRunnerContext,
32
81
  ): Promise<TestResult> => {
33
- const startTime = Date.now();
82
+ const startedAt = Date.now();
34
83
  const task: HarnessTaskContext = {
35
84
  name: test.name,
36
85
  type: 'test',
@@ -54,11 +103,16 @@ const runTest = async (
54
103
  );
55
104
 
56
105
  // Emit test-started event
106
+ const ancestorTitles = getAncestorTitles(suite);
57
107
  context.events.emit({
58
108
  type: 'test-started',
59
109
  name: test.name,
60
110
  suite: suite.name,
61
111
  file: context.testFilePath,
112
+ ancestorTitles,
113
+ fullName: getFullName(ancestorTitles, test.name),
114
+ startedAt,
115
+ declarationMode: test.declarationMode,
62
116
  });
63
117
 
64
118
  try {
@@ -67,14 +121,16 @@ const runTest = async (
67
121
  name: test.name,
68
122
  status: 'skipped' as const,
69
123
  duration: 0,
124
+ ancestorTitles,
125
+ fullName: getFullName(ancestorTitles, test.name),
126
+ startedAt,
127
+ declarationMode: test.declarationMode,
70
128
  };
71
129
 
72
- // Emit test-finished event
73
- context.events.emit({
74
- type: 'test-finished',
75
- name: test.name,
76
- suite: suite.name,
77
- file: context.testFilePath,
130
+ emitTestFinished(context, {
131
+ test,
132
+ suite,
133
+ startedAt,
78
134
  duration: 0,
79
135
  status: 'skipped',
80
136
  });
@@ -88,14 +144,16 @@ const runTest = async (
88
144
  name: test.name,
89
145
  status: 'todo' as const,
90
146
  duration: 0,
147
+ ancestorTitles,
148
+ fullName: getFullName(ancestorTitles, test.name),
149
+ startedAt,
150
+ declarationMode: test.declarationMode,
91
151
  };
92
152
 
93
- // Emit test-finished event
94
- context.events.emit({
95
- type: 'test-finished',
96
- name: test.name,
97
- suite: suite.name,
98
- file: context.testFilePath,
153
+ emitTestFinished(context, {
154
+ test,
155
+ suite,
156
+ startedAt,
99
157
  duration: 0,
100
158
  status: 'todo',
101
159
  });
@@ -127,7 +185,7 @@ const runTest = async (
127
185
  }
128
186
 
129
187
  if (didSkip) {
130
- const duration = Date.now() - startTime;
188
+ const duration = Date.now() - startedAt;
131
189
 
132
190
  await runOnTestFinished(lifecycleState);
133
191
 
@@ -135,13 +193,16 @@ const runTest = async (
135
193
  name: test.name,
136
194
  status: 'skipped' as const,
137
195
  duration,
196
+ ancestorTitles,
197
+ fullName: getFullName(ancestorTitles, test.name),
198
+ startedAt,
199
+ declarationMode: test.declarationMode,
138
200
  };
139
201
 
140
- context.events.emit({
141
- type: 'test-finished',
142
- file: context.testFilePath,
143
- suite: suite.name,
144
- name: test.name,
202
+ emitTestFinished(context, {
203
+ test,
204
+ suite,
205
+ startedAt,
145
206
  duration,
146
207
  status: 'skipped',
147
208
  });
@@ -155,20 +216,22 @@ const runTest = async (
155
216
  setCurrentExpectTestState(undefined);
156
217
  }
157
218
 
158
- const duration = Date.now() - startTime;
219
+ const duration = Date.now() - startedAt;
159
220
 
160
221
  const result = {
161
222
  name: test.name,
162
223
  status: 'passed' as const,
163
224
  duration,
225
+ ancestorTitles,
226
+ fullName: getFullName(ancestorTitles, test.name),
227
+ startedAt,
228
+ declarationMode: test.declarationMode,
164
229
  };
165
230
 
166
- // Emit test-finished event
167
- context.events.emit({
168
- type: 'test-finished',
169
- file: context.testFilePath,
170
- suite: suite.name,
171
- name: test.name,
231
+ emitTestFinished(context, {
232
+ test,
233
+ suite,
234
+ startedAt,
172
235
  duration,
173
236
  status: 'passed',
174
237
  });
@@ -184,21 +247,23 @@ const runTest = async (
184
247
  suite.name,
185
248
  test.name,
186
249
  );
187
- const duration = Date.now() - startTime;
250
+ const duration = Date.now() - startedAt;
188
251
 
189
252
  const result = {
190
253
  name: test.name,
191
254
  status: 'failed' as const,
192
255
  error: testError.toSerializedJSON(),
193
256
  duration,
257
+ ancestorTitles,
258
+ fullName: getFullName(ancestorTitles, test.name),
259
+ startedAt,
260
+ declarationMode: test.declarationMode,
194
261
  };
195
262
 
196
- // Emit test-finished event
197
- context.events.emit({
198
- type: 'test-finished',
199
- file: context.testFilePath,
200
- suite: suite.name,
201
- name: test.name,
263
+ emitTestFinished(context, {
264
+ test,
265
+ suite,
266
+ startedAt,
202
267
  duration,
203
268
  error: testError.toSerializedJSON(),
204
269
  status: 'failed',
@@ -223,10 +288,19 @@ export const runSuite = async (
223
288
 
224
289
  // Check if suite should be skipped or is todo
225
290
  if (suite.status === 'skipped') {
291
+ const testResults = await Promise.all(
292
+ suite.tests.map((test) => runTest({ ...test, status: 'skipped' }, suite, context)),
293
+ );
294
+ const suiteResults = await Promise.all(
295
+ suite.suites.map((childSuite) =>
296
+ runSuite({ ...childSuite, status: 'skipped' }, context),
297
+ ),
298
+ );
299
+
226
300
  const result = {
227
301
  name: suite.name,
228
- tests: [],
229
- suites: [],
302
+ tests: testResults,
303
+ suites: suiteResults,
230
304
  status: 'skipped' as const,
231
305
  duration: 0,
232
306
  };