react-native-harness 1.0.0-alpha.1 → 1.0.0-alpha.11
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/README.md +2 -2
- package/bin.js +1 -1
- package/dist/babel.d.ts +2 -0
- package/dist/babel.d.ts.map +1 -0
- package/dist/babel.js +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -37
- package/dist/metro.d.ts +2 -0
- package/dist/metro.d.ts.map +1 -0
- package/dist/metro.js +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/eslint.config.mjs +1 -1
- package/package.json +31 -12
- package/src/babel.ts +1 -0
- package/src/index.ts +1 -52
- package/src/metro.ts +1 -0
- package/tsconfig.json +9 -3
- package/tsconfig.lib.json +8 -14
- package/types/index.d.ts +1 -0
- package/dist/bundlers/metro.d.ts +0 -5
- package/dist/bundlers/metro.d.ts.map +0 -1
- package/dist/bundlers/metro.js +0 -52
- package/dist/bundlers/webpack.d.ts +0 -2
- package/dist/bundlers/webpack.d.ts.map +0 -1
- package/dist/bundlers/webpack.js +0 -49
- package/dist/commands/test.d.ts +0 -2
- package/dist/commands/test.d.ts.map +0 -1
- package/dist/commands/test.js +0 -111
- package/dist/errors/appNotInstalledError.d.ts +0 -7
- package/dist/errors/appNotInstalledError.d.ts.map +0 -1
- package/dist/errors/appNotInstalledError.js +0 -12
- package/dist/errors/bridgeTimeoutError.d.ts +0 -7
- package/dist/errors/bridgeTimeoutError.d.ts.map +0 -1
- package/dist/errors/bridgeTimeoutError.js +0 -12
- package/dist/errors/errorHandler.d.ts +0 -2
- package/dist/errors/errorHandler.d.ts.map +0 -1
- package/dist/errors/errorHandler.js +0 -138
- package/dist/errors/errors.d.ts +0 -41
- package/dist/errors/errors.d.ts.map +0 -1
- package/dist/errors/errors.js +0 -83
- package/dist/platforms/android/build.d.ts +0 -5
- package/dist/platforms/android/build.d.ts.map +0 -1
- package/dist/platforms/android/build.js +0 -29
- package/dist/platforms/android/device.d.ts +0 -5
- package/dist/platforms/android/device.d.ts.map +0 -1
- package/dist/platforms/android/device.js +0 -36
- package/dist/platforms/android/emulator.d.ts +0 -11
- package/dist/platforms/android/emulator.d.ts.map +0 -1
- package/dist/platforms/android/emulator.js +0 -110
- package/dist/platforms/android/index.d.ts +0 -4
- package/dist/platforms/android/index.d.ts.map +0 -1
- package/dist/platforms/android/index.js +0 -56
- package/dist/platforms/ios/build.d.ts +0 -7
- package/dist/platforms/ios/build.d.ts.map +0 -1
- package/dist/platforms/ios/build.js +0 -51
- package/dist/platforms/ios/device.d.ts +0 -7
- package/dist/platforms/ios/device.d.ts.map +0 -1
- package/dist/platforms/ios/device.js +0 -51
- package/dist/platforms/ios/index.d.ts +0 -4
- package/dist/platforms/ios/index.d.ts.map +0 -1
- package/dist/platforms/ios/index.js +0 -38
- package/dist/platforms/ios/simulator.d.ts +0 -11
- package/dist/platforms/ios/simulator.d.ts.map +0 -1
- package/dist/platforms/ios/simulator.js +0 -133
- package/dist/platforms/platform-adapter.d.ts +0 -10
- package/dist/platforms/platform-adapter.d.ts.map +0 -1
- package/dist/platforms/platform-adapter.js +0 -1
- package/dist/platforms/platform-registry.d.ts +0 -3
- package/dist/platforms/platform-registry.d.ts.map +0 -1
- package/dist/platforms/platform-registry.js +0 -19
- package/dist/platforms/web/index.d.ts +0 -4
- package/dist/platforms/web/index.d.ts.map +0 -1
- package/dist/platforms/web/index.js +0 -73
- package/dist/process.d.ts +0 -3
- package/dist/process.d.ts.map +0 -1
- package/dist/process.js +0 -28
- package/dist/reporters/default-reporter.d.ts +0 -3
- package/dist/reporters/default-reporter.d.ts.map +0 -1
- package/dist/reporters/default-reporter.js +0 -116
- package/dist/reporters/junit-reporter.d.ts +0 -3
- package/dist/reporters/junit-reporter.d.ts.map +0 -1
- package/dist/reporters/junit-reporter.js +0 -119
- package/dist/reporters/live-reporter.d.ts +0 -20
- package/dist/reporters/live-reporter.d.ts.map +0 -1
- package/dist/reporters/live-reporter.js +0 -176
- package/dist/src/reporters/default-reporter.js +0 -135
- package/dist/test-reporter-demo.js +0 -95
- package/dist/utils.d.ts +0 -5
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -11
- package/src/bundlers/metro.ts +0 -79
- package/src/commands/test.ts +0 -189
- package/src/errors/errorHandler.ts +0 -184
- package/src/errors/errors.ts +0 -109
- package/src/platforms/android/build.ts +0 -48
- package/src/platforms/android/device.ts +0 -48
- package/src/platforms/android/emulator.ts +0 -139
- package/src/platforms/android/index.ts +0 -87
- package/src/platforms/ios/build.ts +0 -76
- package/src/platforms/ios/device.ts +0 -76
- package/src/platforms/ios/index.ts +0 -55
- package/src/platforms/ios/simulator.ts +0 -177
- package/src/platforms/platform-adapter.ts +0 -11
- package/src/platforms/platform-registry.ts +0 -24
- package/src/platforms/web/index.ts +0 -95
- package/src/process.ts +0 -33
- package/src/reporters/default-reporter.ts +0 -149
- package/src/reporters/junit-reporter.ts +0 -179
- package/src/utils.ts +0 -12
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { TestRunnerConfig } from '@react-native-harness/config';
|
|
2
|
-
|
|
3
|
-
export type Environment = {
|
|
4
|
-
restart: () => Promise<void>;
|
|
5
|
-
dispose: () => Promise<void>;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export type PlatformAdapter = {
|
|
9
|
-
name: string;
|
|
10
|
-
getEnvironment: (runner: TestRunnerConfig) => Promise<Environment>;
|
|
11
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { PlatformAdapter } from './platform-adapter.js';
|
|
2
|
-
import androidPlatformAdapter from './android/index.js';
|
|
3
|
-
import iosPlatformAdapter from './ios/index.js';
|
|
4
|
-
import webPlatformAdapter from './web/index.js';
|
|
5
|
-
|
|
6
|
-
const platformAdapters = {
|
|
7
|
-
android: androidPlatformAdapter,
|
|
8
|
-
ios: iosPlatformAdapter,
|
|
9
|
-
web: webPlatformAdapter,
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const getPlatformAdapter = async (
|
|
13
|
-
platformName: string
|
|
14
|
-
): Promise<PlatformAdapter> => {
|
|
15
|
-
if (!(platformName in platformAdapters)) {
|
|
16
|
-
throw new Error(`Platform adapter for ${platformName} not found`);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
return platformAdapters[platformName as keyof typeof platformAdapters];
|
|
21
|
-
} catch (error) {
|
|
22
|
-
throw new Error(`Platform adapter for ${platformName} not found`);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { TestRunnerConfig, WebTestRunnerConfig, assertWebRunnerConfig } from '@react-native-harness/config';
|
|
2
|
-
import { logger } from '@react-native-harness/tools';
|
|
3
|
-
import { Browser, chromium, firefox, Page, webkit } from 'playwright';
|
|
4
|
-
import { PlatformAdapter } from '../platform-adapter.js';
|
|
5
|
-
import { runMetro } from '../../bundlers/metro.js';
|
|
6
|
-
|
|
7
|
-
const runBrowser = async (
|
|
8
|
-
url: string,
|
|
9
|
-
browserType: WebTestRunnerConfig['browser']
|
|
10
|
-
): Promise<{ browser: Browser; page: Page }> => {
|
|
11
|
-
// For now, we're only supporting Firefox, but this will be expanded
|
|
12
|
-
// to support other browsers based on the config
|
|
13
|
-
let browser: Browser;
|
|
14
|
-
|
|
15
|
-
switch (browserType) {
|
|
16
|
-
case 'firefox':
|
|
17
|
-
browser = await firefox.launch({
|
|
18
|
-
headless: false,
|
|
19
|
-
devtools: false,
|
|
20
|
-
args: [
|
|
21
|
-
'--no-sandbox',
|
|
22
|
-
'--disable-setuid-sandbox',
|
|
23
|
-
'--disable-dev-shm-usage',
|
|
24
|
-
'--disable-web-security',
|
|
25
|
-
'--allow-insecure-localhost',
|
|
26
|
-
'--ignore-certificate-errors',
|
|
27
|
-
],
|
|
28
|
-
ignoreDefaultArgs: ['--disable-extensions'],
|
|
29
|
-
});
|
|
30
|
-
break;
|
|
31
|
-
case 'chrome':
|
|
32
|
-
browser = await chromium.launch({
|
|
33
|
-
headless: false,
|
|
34
|
-
devtools: false,
|
|
35
|
-
});
|
|
36
|
-
break;
|
|
37
|
-
case 'safari':
|
|
38
|
-
browser = await webkit.launch({
|
|
39
|
-
headless: false,
|
|
40
|
-
devtools: false,
|
|
41
|
-
});
|
|
42
|
-
break;
|
|
43
|
-
default:
|
|
44
|
-
throw new Error(`Unsupported browser type: ${browserType}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const context = await browser.newContext({
|
|
48
|
-
bypassCSP: true,
|
|
49
|
-
ignoreHTTPSErrors: true,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const page = await context.newPage();
|
|
53
|
-
|
|
54
|
-
await page.setViewportSize({ width: 1280, height: 720 });
|
|
55
|
-
|
|
56
|
-
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
|
57
|
-
|
|
58
|
-
return { browser, page };
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export const webPlatformAdapter: PlatformAdapter = {
|
|
62
|
-
name: 'web',
|
|
63
|
-
getEnvironment: async (runner: TestRunnerConfig) => {
|
|
64
|
-
assertWebRunnerConfig(runner);
|
|
65
|
-
|
|
66
|
-
logger.debug('Starting web environment');
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
logger.debug('Running metro');
|
|
71
|
-
const metro = await runMetro(true);
|
|
72
|
-
logger.debug('Metro running');
|
|
73
|
-
|
|
74
|
-
logger.debug(`Running browser: ${runner.browser}`);
|
|
75
|
-
const { browser, page } = await runBrowser('http://localhost:8081', runner.browser);
|
|
76
|
-
logger.debug('Browser running');
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
restart: async () => {
|
|
82
|
-
logger.debug('Reloading page');
|
|
83
|
-
await page.reload({ waitUntil: 'domcontentloaded', timeout: 60000 });
|
|
84
|
-
},
|
|
85
|
-
dispose: async () => {
|
|
86
|
-
logger.debug('Closing browser');
|
|
87
|
-
await browser.close();
|
|
88
|
-
|
|
89
|
-
metro.kill();
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
export default webPlatformAdapter;
|
package/src/process.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { type ChildProcess } from 'node:child_process';
|
|
2
|
-
|
|
3
|
-
export const killWithAwait = (child: ChildProcess): Promise<void> => {
|
|
4
|
-
return new Promise((resolve) => {
|
|
5
|
-
const timeout = setTimeout(() => {
|
|
6
|
-
child.kill('SIGTERM');
|
|
7
|
-
resolve();
|
|
8
|
-
}, 10000);
|
|
9
|
-
|
|
10
|
-
if (child.killed || child.exitCode !== null) {
|
|
11
|
-
clearTimeout(timeout);
|
|
12
|
-
resolve();
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
child.on('exit', () => {
|
|
17
|
-
clearTimeout(timeout);
|
|
18
|
-
resolve();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
child.on('error', () => {
|
|
22
|
-
clearTimeout(timeout);
|
|
23
|
-
resolve();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
child.kill();
|
|
28
|
-
} catch (error) {
|
|
29
|
-
clearTimeout(timeout);
|
|
30
|
-
resolve();
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
};
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { color, spinner } from '@react-native-harness/tools';
|
|
2
|
-
import type { TestSuiteResult, TestResult } from '@react-native-harness/bridge';
|
|
3
|
-
import { Reporter } from '@react-native-harness/config';
|
|
4
|
-
|
|
5
|
-
export const defaultReporter: Reporter = {
|
|
6
|
-
report: async (results) => {
|
|
7
|
-
const resultsSpinner = spinner();
|
|
8
|
-
resultsSpinner.start('Test Results');
|
|
9
|
-
|
|
10
|
-
for (const suite of results) {
|
|
11
|
-
resultsSpinner.message(formatSuiteResult(suite));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Summary
|
|
15
|
-
let totalPassed = 0,
|
|
16
|
-
totalFailed = 0,
|
|
17
|
-
totalSkipped = 0,
|
|
18
|
-
totalTodo = 0;
|
|
19
|
-
let totalDuration = 0;
|
|
20
|
-
|
|
21
|
-
for (const suite of results) {
|
|
22
|
-
const summary = getTestSummary(suite);
|
|
23
|
-
totalPassed += summary.passed;
|
|
24
|
-
totalFailed += summary.failed;
|
|
25
|
-
totalSkipped += summary.skipped;
|
|
26
|
-
totalTodo += summary.todo;
|
|
27
|
-
totalDuration += suite.duration || 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const summaryText = [
|
|
31
|
-
color.green(`${totalPassed} passed`),
|
|
32
|
-
color.red(`${totalFailed} failed`),
|
|
33
|
-
color.yellow(`${totalSkipped} skipped`),
|
|
34
|
-
color.blue(`${totalTodo} todo`),
|
|
35
|
-
].join(', ');
|
|
36
|
-
|
|
37
|
-
resultsSpinner.message(`Summary: ${summaryText}`);
|
|
38
|
-
resultsSpinner.message(`Total time: ${formatDuration(totalDuration)}`);
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const formatDuration = (duration?: number): string => {
|
|
43
|
-
if (duration == null) return '';
|
|
44
|
-
return color.dim(` (${duration}ms)`);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const getStatusIcon = (status: string): string => {
|
|
48
|
-
switch (status) {
|
|
49
|
-
case 'passed':
|
|
50
|
-
return color.green('✓');
|
|
51
|
-
case 'failed':
|
|
52
|
-
return color.red('✗');
|
|
53
|
-
case 'skipped':
|
|
54
|
-
return color.yellow('○');
|
|
55
|
-
case 'todo':
|
|
56
|
-
return color.blue('◐');
|
|
57
|
-
default:
|
|
58
|
-
return '?';
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const formatTestResult = (test: TestResult, indent = ''): string => {
|
|
63
|
-
const icon = getStatusIcon(test.status);
|
|
64
|
-
const name = test.status === 'failed' ? color.red(test.name) : test.name;
|
|
65
|
-
const duration = formatDuration(test.duration);
|
|
66
|
-
|
|
67
|
-
let result = `${indent}${icon} ${name}${duration}`;
|
|
68
|
-
|
|
69
|
-
if (test.error) {
|
|
70
|
-
const errorLines = test.error.message?.split('\n') || [];
|
|
71
|
-
result +=
|
|
72
|
-
'\n' +
|
|
73
|
-
errorLines
|
|
74
|
-
.map((line: string) => `${indent} ${color.red(line)}`)
|
|
75
|
-
.join('\n');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return result;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const formatSuiteResult = (suite: TestSuiteResult, indent = ''): string => {
|
|
82
|
-
const icon = getStatusIcon(suite.status);
|
|
83
|
-
const name =
|
|
84
|
-
suite.status === 'failed' ? color.red(suite.name) : color.bold(suite.name);
|
|
85
|
-
const duration = formatDuration(suite.duration);
|
|
86
|
-
|
|
87
|
-
let result = `${indent}${icon} ${name}${duration}`;
|
|
88
|
-
|
|
89
|
-
if (suite.error) {
|
|
90
|
-
const errorLines = suite.error.message.split('\n');
|
|
91
|
-
result +=
|
|
92
|
-
'\n' +
|
|
93
|
-
errorLines
|
|
94
|
-
.map((line: string) => `${indent} ${color.red(line)}`)
|
|
95
|
-
.join('\n');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const childIndent = indent + ' ';
|
|
99
|
-
|
|
100
|
-
// Format tests
|
|
101
|
-
for (const test of suite.tests) {
|
|
102
|
-
result += '\n' + formatTestResult(test, childIndent);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Format nested suites
|
|
106
|
-
for (const childSuite of suite.suites) {
|
|
107
|
-
result += '\n' + formatSuiteResult(childSuite, childIndent);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return result;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const getTestSummary = (
|
|
114
|
-
suite: TestSuiteResult
|
|
115
|
-
): { passed: number; failed: number; skipped: number; todo: number } => {
|
|
116
|
-
let passed = 0,
|
|
117
|
-
failed = 0,
|
|
118
|
-
skipped = 0,
|
|
119
|
-
todo = 0;
|
|
120
|
-
|
|
121
|
-
// Count tests in current suite
|
|
122
|
-
for (const test of suite.tests) {
|
|
123
|
-
switch (test.status) {
|
|
124
|
-
case 'passed':
|
|
125
|
-
passed++;
|
|
126
|
-
break;
|
|
127
|
-
case 'failed':
|
|
128
|
-
failed++;
|
|
129
|
-
break;
|
|
130
|
-
case 'skipped':
|
|
131
|
-
skipped++;
|
|
132
|
-
break;
|
|
133
|
-
case 'todo':
|
|
134
|
-
todo++;
|
|
135
|
-
break;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Count tests in nested suites
|
|
140
|
-
for (const childSuite of suite.suites) {
|
|
141
|
-
const childSummary = getTestSummary(childSuite);
|
|
142
|
-
passed += childSummary.passed;
|
|
143
|
-
failed += childSummary.failed;
|
|
144
|
-
skipped += childSummary.skipped;
|
|
145
|
-
todo += childSummary.todo;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return { passed, failed, skipped, todo };
|
|
149
|
-
};
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { Reporter } from '@react-native-harness/config';
|
|
2
|
-
import type { TestSuiteResult, TestResult } from '@react-native-harness/bridge';
|
|
3
|
-
import { writeFileSync } from 'fs';
|
|
4
|
-
import { join } from 'path';
|
|
5
|
-
|
|
6
|
-
export const junitReporter: Reporter = {
|
|
7
|
-
report: async (results) => {
|
|
8
|
-
const xml = generateJUnitXML(results);
|
|
9
|
-
|
|
10
|
-
// Write to junit.xml file
|
|
11
|
-
const outputPath = join(process.cwd(), 'junit.xml');
|
|
12
|
-
writeFileSync(outputPath, xml, 'utf8');
|
|
13
|
-
|
|
14
|
-
console.log(`📄 JUnit report written to: ${outputPath}`);
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const generateJUnitXML = (results: TestSuiteResult[]): string => {
|
|
19
|
-
const { totalTests, totalFailures, totalSkipped, totalTime } =
|
|
20
|
-
calculateTotals(results);
|
|
21
|
-
|
|
22
|
-
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
23
|
-
xml += `<testsuites tests="${totalTests}" failures="${totalFailures}" skipped="${totalSkipped}" time="${
|
|
24
|
-
totalTime / 1000
|
|
25
|
-
}">\n`;
|
|
26
|
-
|
|
27
|
-
for (const suite of results) {
|
|
28
|
-
xml += generateTestSuiteXML(suite, ' ');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
xml += '</testsuites>\n';
|
|
32
|
-
|
|
33
|
-
return xml;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const generateTestSuiteXML = (
|
|
37
|
-
suite: TestSuiteResult,
|
|
38
|
-
indent: string
|
|
39
|
-
): string => {
|
|
40
|
-
const { tests, failures, skipped, time } = getSuiteStats(suite);
|
|
41
|
-
|
|
42
|
-
let xml = `${indent}<testsuite name="${escapeXML(
|
|
43
|
-
suite.name
|
|
44
|
-
)}" tests="${tests}" failures="${failures}" skipped="${skipped}" time="${
|
|
45
|
-
time / 1000
|
|
46
|
-
}"`;
|
|
47
|
-
|
|
48
|
-
if (suite.error) {
|
|
49
|
-
xml += ` errors="1"`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
xml += '>\n';
|
|
53
|
-
|
|
54
|
-
// Add suite-level error if present
|
|
55
|
-
if (suite.error) {
|
|
56
|
-
xml += `${indent} <error message="${escapeXML(
|
|
57
|
-
suite.error.message || 'Suite failed'
|
|
58
|
-
)}"`;
|
|
59
|
-
if (suite.error.name) {
|
|
60
|
-
xml += ` type="${escapeXML(suite.error.name)}"`;
|
|
61
|
-
}
|
|
62
|
-
xml += '>';
|
|
63
|
-
if (suite.error.stack) {
|
|
64
|
-
xml += escapeXML(suite.error.stack);
|
|
65
|
-
}
|
|
66
|
-
xml += '</error>\n';
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Add individual test cases
|
|
70
|
-
for (const test of suite.tests) {
|
|
71
|
-
xml += generateTestCaseXML(test, indent + ' ');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Add nested suites
|
|
75
|
-
for (const nestedSuite of suite.suites) {
|
|
76
|
-
xml += generateTestSuiteXML(nestedSuite, indent + ' ');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
xml += `${indent}</testsuite>\n`;
|
|
80
|
-
|
|
81
|
-
return xml;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const generateTestCaseXML = (test: TestResult, indent: string): string => {
|
|
85
|
-
const time = (test.duration || 0) / 1000;
|
|
86
|
-
let xml = `${indent}<testcase name="${escapeXML(test.name)}" time="${time}"`;
|
|
87
|
-
|
|
88
|
-
if (test.status === 'passed') {
|
|
89
|
-
xml += '/>\n';
|
|
90
|
-
} else {
|
|
91
|
-
xml += '>\n';
|
|
92
|
-
|
|
93
|
-
switch (test.status) {
|
|
94
|
-
case 'failed':
|
|
95
|
-
xml += `${indent} <failure message="${escapeXML(
|
|
96
|
-
test.error?.message || 'Test failed'
|
|
97
|
-
)}"`;
|
|
98
|
-
if (test.error?.name) {
|
|
99
|
-
xml += ` type="${escapeXML(test.error.name)}"`;
|
|
100
|
-
}
|
|
101
|
-
xml += '>';
|
|
102
|
-
if (test.error?.stack) {
|
|
103
|
-
xml += escapeXML(test.error.stack);
|
|
104
|
-
}
|
|
105
|
-
xml += '</failure>\n';
|
|
106
|
-
break;
|
|
107
|
-
case 'skipped':
|
|
108
|
-
xml += `${indent} <skipped/>\n`;
|
|
109
|
-
break;
|
|
110
|
-
case 'todo':
|
|
111
|
-
xml += `${indent} <skipped message="TODO: Test not implemented"/>\n`;
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
xml += `${indent}</testcase>\n`;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return xml;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const calculateTotals = (
|
|
122
|
-
results: TestSuiteResult[]
|
|
123
|
-
): {
|
|
124
|
-
totalTests: number;
|
|
125
|
-
totalFailures: number;
|
|
126
|
-
totalSkipped: number;
|
|
127
|
-
totalTime: number;
|
|
128
|
-
} => {
|
|
129
|
-
let totalTests = 0;
|
|
130
|
-
let totalFailures = 0;
|
|
131
|
-
let totalSkipped = 0;
|
|
132
|
-
let totalTime = 0;
|
|
133
|
-
|
|
134
|
-
for (const suite of results) {
|
|
135
|
-
const stats = getSuiteStats(suite);
|
|
136
|
-
totalTests += stats.tests;
|
|
137
|
-
totalFailures += stats.failures;
|
|
138
|
-
totalSkipped += stats.skipped;
|
|
139
|
-
totalTime += stats.time;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return { totalTests, totalFailures, totalSkipped, totalTime };
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const getSuiteStats = (
|
|
146
|
-
suite: TestSuiteResult
|
|
147
|
-
): {
|
|
148
|
-
tests: number;
|
|
149
|
-
failures: number;
|
|
150
|
-
skipped: number;
|
|
151
|
-
time: number;
|
|
152
|
-
} => {
|
|
153
|
-
let tests = suite.tests.length;
|
|
154
|
-
let failures = suite.tests.filter((t) => t.status === 'failed').length;
|
|
155
|
-
let skipped = suite.tests.filter(
|
|
156
|
-
(t) => t.status === 'skipped' || t.status === 'todo'
|
|
157
|
-
).length;
|
|
158
|
-
let time = suite.duration || 0;
|
|
159
|
-
|
|
160
|
-
// Add stats from nested suites
|
|
161
|
-
for (const nestedSuite of suite.suites) {
|
|
162
|
-
const nestedStats = getSuiteStats(nestedSuite);
|
|
163
|
-
tests += nestedStats.tests;
|
|
164
|
-
failures += nestedStats.failures;
|
|
165
|
-
skipped += nestedStats.skipped;
|
|
166
|
-
time += nestedStats.time;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return { tests, failures, skipped, time };
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const escapeXML = (str: string): string => {
|
|
173
|
-
return str
|
|
174
|
-
.replace(/&/g, '&')
|
|
175
|
-
.replace(/</g, '<')
|
|
176
|
-
.replace(/>/g, '>')
|
|
177
|
-
.replace(/"/g, '"')
|
|
178
|
-
.replace(/'/g, ''');
|
|
179
|
-
};
|
package/src/utils.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export function assert(condition: boolean, message: string): asserts condition {
|
|
2
|
-
if (!condition) {
|
|
3
|
-
throw new AssertionError(message);
|
|
4
|
-
}
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export class AssertionError extends Error {
|
|
8
|
-
constructor(message: string) {
|
|
9
|
-
super(message);
|
|
10
|
-
this.name = 'AssertionError';
|
|
11
|
-
}
|
|
12
|
-
}
|
package/tsconfig.tsbuildinfo
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"fileNames":[],"fileInfos":[],"root":[],"options":{"composite":true,"declarationMap":true,"emitDeclarationOnly":true,"importHelpers":true,"module":199,"noEmitOnError":true,"noFallthroughCasesInSwitch":true,"noImplicitOverride":true,"noImplicitReturns":true,"noUnusedLocals":true,"skipLibCheck":true,"strict":true,"target":9},"version":"5.8.3"}
|