dceky 1.0.5-beta.ky-declarations.13 → 1.0.5
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 +45 -29
- package/lib/commands/assertDoesNotHaveClass.d.ts +18 -0
- package/lib/commands/assertDoesNotHaveClass.js +17 -0
- package/lib/commands/assertDoesNotHaveClass.js.map +1 -0
- package/lib/commands/assertHasClass.d.ts +18 -0
- package/lib/commands/assertHasClass.js +17 -0
- package/lib/commands/assertHasClass.js.map +1 -0
- package/lib/commands/assertNumElements.d.ts +18 -0
- package/lib/commands/assertNumElements.js +17 -0
- package/lib/commands/assertNumElements.js.map +1 -0
- package/lib/commands/clickWithRetry.d.ts +19 -0
- package/lib/commands/clickWithRetry.js +29 -0
- package/lib/commands/clickWithRetry.js.map +1 -0
- package/lib/commands/genTextOfLength.d.ts +20 -0
- package/lib/commands/genTextOfLength.js +20 -0
- package/lib/commands/genTextOfLength.js.map +1 -0
- package/lib/commands/getAttribute.d.ts +20 -0
- package/lib/commands/getAttribute.js +22 -0
- package/lib/commands/getAttribute.js.map +1 -0
- package/lib/commands/getClassName.d.ts +15 -0
- package/lib/commands/getClassName.js +21 -0
- package/lib/commands/getClassName.js.map +1 -0
- package/lib/commands/getCurrentDateInfo.d.ts +21 -0
- package/lib/commands/getCurrentDateInfo.js +25 -0
- package/lib/commands/getCurrentDateInfo.js.map +1 -0
- package/lib/commands/getId.d.ts +15 -0
- package/lib/commands/getId.js +21 -0
- package/lib/commands/getId.js.map +1 -0
- package/lib/commands/getNumElements.d.ts +14 -0
- package/lib/commands/getNumElements.js +19 -0
- package/lib/commands/getNumElements.js.map +1 -0
- package/lib/commands/getSpecialChars.d.ts +14 -0
- package/lib/commands/getSpecialChars.js +16 -0
- package/lib/commands/getSpecialChars.js.map +1 -0
- package/lib/commands/getTitle.d.ts +13 -0
- package/lib/commands/getTitle.js +20 -0
- package/lib/commands/getTitle.js.map +1 -0
- package/lib/commands/handleHarvardKey.d.ts +18 -0
- package/lib/commands/handleHarvardKey.js +56 -0
- package/lib/commands/handleHarvardKey.js.map +1 -0
- package/lib/commands/handleHarvardKey2.d.ts +14 -0
- package/lib/commands/handleHarvardKey2.js +88 -0
- package/lib/commands/handleHarvardKey2.js.map +1 -0
- package/lib/commands/launchAs.d.ts +20 -0
- package/lib/commands/launchAs.js +60 -0
- package/lib/commands/launchAs.js.map +1 -0
- package/lib/commands/launchLTIUsingToken.d.ts +19 -0
- package/lib/commands/launchLTIUsingToken.js +74 -0
- package/lib/commands/launchLTIUsingToken.js.map +1 -0
- package/lib/commands/listSelectLabels.d.ts +15 -0
- package/lib/commands/listSelectLabels.js +24 -0
- package/lib/commands/listSelectLabels.js.map +1 -0
- package/lib/commands/listSelectValues.d.ts +15 -0
- package/lib/commands/listSelectValues.js +26 -0
- package/lib/commands/listSelectValues.js.map +1 -0
- package/lib/commands/navigateToHref.d.ts +19 -0
- package/lib/commands/navigateToHref.js +23 -0
- package/lib/commands/navigateToHref.js.map +1 -0
- package/lib/commands/padWithZeros.d.ts +20 -0
- package/lib/commands/padWithZeros.js +24 -0
- package/lib/commands/padWithZeros.js.map +1 -0
- package/lib/commands/runScript.d.ts +16 -0
- package/lib/commands/runScript.js +25 -0
- package/lib/commands/runScript.js.map +1 -0
- package/lib/commands/typeInto.d.ts +20 -0
- package/lib/commands/typeInto.js +28 -0
- package/lib/commands/typeInto.js.map +1 -0
- package/lib/commands/uniquify.d.ts +15 -0
- package/lib/commands/uniquify.js +25 -0
- package/lib/commands/uniquify.js.map +1 -0
- package/lib/commands/visitCanvasEndpoint.d.ts +27 -0
- package/lib/commands/visitCanvasEndpoint.js +35 -0
- package/lib/commands/visitCanvasEndpoint.js.map +1 -0
- package/lib/commands/visitCanvasGETEndpoint.d.ts +20 -0
- package/lib/commands/visitCanvasGETEndpoint.js +26 -0
- package/lib/commands/visitCanvasGETEndpoint.js.map +1 -0
- package/lib/commands/waitForAtLeastOneElementPresent.d.ts +23 -0
- package/lib/commands/waitForAtLeastOneElementPresent.js +27 -0
- package/lib/commands/waitForAtLeastOneElementPresent.js.map +1 -0
- package/lib/commands/waitForElementVisible.d.ts +14 -0
- package/lib/commands/waitForElementVisible.js +20 -0
- package/lib/commands/waitForElementVisible.js.map +1 -0
- package/lib/index.js.map +1 -0
- package/lib/init.d.ts +6 -0
- package/lib/init.js +69 -0
- package/lib/init.js.map +1 -0
- package/lib/src/commands/extractDataFromClass.d.ts +7 -4
- package/lib/src/commands/extractDataFromClass.js +2 -1
- package/lib/src/commands/extractDataFromClass.js.map +1 -1
- package/lib/src/commands/extractDataFromClassByContents.d.ts +9 -5
- package/lib/src/commands/extractDataFromClassByContents.js +2 -1
- package/lib/src/commands/extractDataFromClassByContents.js.map +1 -1
- package/lib/src/commands/genTextOfLength.d.ts +20 -0
- package/lib/src/commands/genTextOfLength.js +20 -0
- package/lib/src/commands/genTextOfLength.js.map +1 -0
- package/lib/src/commands/getAttribute.d.ts +20 -0
- package/lib/src/commands/getAttribute.js +22 -0
- package/lib/src/commands/getAttribute.js.map +1 -0
- package/lib/src/commands/getClassName.d.ts +15 -0
- package/lib/src/commands/getClassName.js +21 -0
- package/lib/src/commands/getClassName.js.map +1 -0
- package/lib/src/commands/getCurrentDateInfo.d.ts +21 -0
- package/lib/src/commands/getCurrentDateInfo.js +25 -0
- package/lib/src/commands/getCurrentDateInfo.js.map +1 -0
- package/lib/src/commands/getId.d.ts +15 -0
- package/lib/src/commands/getId.js +21 -0
- package/lib/src/commands/getId.js.map +1 -0
- package/lib/src/commands/getJSON.d.ts +0 -1
- package/lib/src/commands/getJSON.js +1 -4
- package/lib/src/commands/getJSON.js.map +1 -1
- package/lib/src/commands/getSpecialChars.d.ts +14 -0
- package/lib/src/commands/getSpecialChars.js +16 -0
- package/lib/src/commands/getSpecialChars.js.map +1 -0
- package/lib/src/commands/getTitle.d.ts +13 -0
- package/lib/src/commands/getTitle.js +20 -0
- package/lib/src/commands/getTitle.js.map +1 -0
- package/lib/src/commands/index.js +29 -5
- package/lib/src/commands/index.js.map +1 -1
- package/lib/src/commands/launchLTIUsingToken.js +12 -4
- package/lib/src/commands/launchLTIUsingToken.js.map +1 -1
- package/lib/src/commands/listSelectLabels.d.ts +15 -0
- package/lib/src/commands/listSelectLabels.js +24 -0
- package/lib/src/commands/listSelectLabels.js.map +1 -0
- package/lib/src/commands/listSelectValues.d.ts +15 -0
- package/lib/src/commands/listSelectValues.js +27 -0
- package/lib/src/commands/listSelectValues.js.map +1 -0
- package/lib/src/commands/padWithZeros.d.ts +20 -0
- package/lib/src/commands/padWithZeros.js +24 -0
- package/lib/src/commands/padWithZeros.js.map +1 -0
- package/lib/src/commands/uniquify.d.ts +15 -0
- package/lib/src/commands/uniquify.js +26 -0
- package/lib/src/commands/uniquify.js.map +1 -0
- package/lib/src/commands/visitCanvasEndpoint.d.ts +26 -0
- package/lib/src/commands/visitCanvasEndpoint.js +36 -0
- package/lib/src/commands/visitCanvasEndpoint.js.map +1 -0
- package/lib/src/commands/waitForAtLeastOneElementPresent.d.ts +23 -0
- package/lib/src/commands/waitForAtLeastOneElementPresent.js +28 -0
- package/lib/src/commands/waitForAtLeastOneElementPresent.js.map +1 -0
- package/lib/src/init.js +28 -2
- package/lib/src/init.js.map +1 -1
- package/lib/start/constants/DEFAULT_THREADS_PER_COMBO.d.ts +6 -0
- package/lib/start/constants/DEFAULT_THREADS_PER_COMBO.js +9 -0
- package/lib/start/constants/DEFAULT_THREADS_PER_COMBO.js.map +1 -0
- package/lib/start/helpers/collectPngFiles.d.ts +9 -0
- package/lib/start/helpers/collectPngFiles.js +31 -0
- package/lib/start/helpers/collectPngFiles.js.map +1 -0
- package/lib/start/helpers/executeAllHeadlessCombinations.d.ts +15 -0
- package/lib/start/helpers/executeAllHeadlessCombinations.js +116 -0
- package/lib/start/helpers/executeAllHeadlessCombinations.js.map +1 -0
- package/lib/start/helpers/executeCypress.d.ts +17 -0
- package/lib/start/helpers/executeCypress.js +103 -0
- package/lib/start/helpers/executeCypress.js.map +1 -0
- package/lib/start/helpers/generateHtmlReport.d.ts +14 -0
- package/lib/start/helpers/generateHtmlReport.js +54 -0
- package/lib/start/helpers/generateHtmlReport.js.map +1 -0
- package/lib/start/helpers/generateReportHomepage.d.ts +9 -0
- package/lib/start/helpers/generateReportHomepage.js +142 -0
- package/lib/start/helpers/generateReportHomepage.js.map +1 -0
- package/lib/start/helpers/generateReporterConfig.d.ts +17 -0
- package/lib/start/helpers/generateReporterConfig.js +32 -0
- package/lib/start/helpers/generateReporterConfig.js.map +1 -0
- package/lib/start/helpers/getDateLabeledDir.d.ts +7 -0
- package/lib/start/helpers/getDateLabeledDir.js +38 -0
- package/lib/start/helpers/getDateLabeledDir.js.map +1 -0
- package/lib/start/helpers/mergeAllReportsAndGenerateHtml.d.ts +11 -0
- package/lib/start/helpers/mergeAllReportsAndGenerateHtml.js +121 -0
- package/lib/start/helpers/mergeAllReportsAndGenerateHtml.js.map +1 -0
- package/lib/start/helpers/mergeReports.d.ts +14 -0
- package/lib/start/helpers/mergeReports.js +63 -0
- package/lib/start/helpers/mergeReports.js.map +1 -0
- package/lib/start/helpers/reportHomepage.ejs +272 -0
- package/lib/start/helpers/runCypressHeadless.d.ts +18 -0
- package/lib/start/helpers/runCypressHeadless.js +138 -0
- package/lib/start/helpers/runCypressHeadless.js.map +1 -0
- package/lib/start/helpers/runCypressVisible.d.ts +8 -0
- package/lib/start/helpers/runCypressVisible.js +53 -0
- package/lib/start/helpers/runCypressVisible.js.map +1 -0
- package/lib/start/index.js +23 -5
- package/lib/start/index.js.map +1 -1
- package/lib/start/types/MochawesomeReporterConfig.d.ts +15 -0
- package/lib/start/types/MochawesomeReporterConfig.js +3 -0
- package/lib/start/types/MochawesomeReporterConfig.js.map +1 -0
- package/lib/start/types/Profile.d.ts +9 -0
- package/lib/start/types/Profile.js +3 -0
- package/lib/start/types/Profile.js.map +1 -0
- package/lib/start/types/ReportInfo.d.ts +14 -0
- package/lib/start/types/ReportInfo.js +3 -0
- package/lib/start/types/ReportInfo.js.map +1 -0
- package/lib/start/types/RunResult.d.ts +12 -0
- package/lib/start/types/RunResult.js +3 -0
- package/lib/start/types/RunResult.js.map +1 -0
- package/lib/start/types/ScreenshotInfo.d.ts +10 -0
- package/lib/start/types/ScreenshotInfo.js +3 -0
- package/lib/start/types/ScreenshotInfo.js.map +1 -0
- package/lib/start/types/TemplateReportInfo.d.ts +12 -0
- package/lib/start/types/TemplateReportInfo.js +3 -0
- package/lib/start/types/TemplateReportInfo.js.map +1 -0
- package/package.json +10 -8
- package/src/commands/extractDataFromClass.ts +17 -6
- package/src/commands/extractDataFromClassByContents.ts +21 -8
- package/src/commands/genTextOfLength.ts +54 -0
- package/src/commands/getAttribute.ts +58 -0
- package/src/commands/getClassName.ts +44 -0
- package/src/commands/getCurrentDateInfo.ts +57 -0
- package/src/commands/getId.ts +44 -0
- package/src/commands/getJSON.ts +0 -4
- package/src/commands/getSpecialChars.ts +34 -0
- package/src/commands/getTitle.ts +39 -0
- package/src/commands/index.ts +29 -5
- package/src/commands/launchLTIUsingToken.ts +12 -4
- package/src/commands/listSelectLabels.ts +47 -0
- package/src/commands/listSelectValues.ts +50 -0
- package/src/commands/padWithZeros.ts +53 -0
- package/src/commands/uniquify.ts +49 -0
- package/src/commands/visitCanvasEndpoint.ts +75 -0
- package/src/commands/waitForAtLeastOneElementPresent.ts +64 -0
- package/start/constants/DEFAULT_THREADS_PER_COMBO.ts +7 -0
- package/start/helpers/collectPngFiles.ts +25 -0
- package/start/helpers/executeAllHeadlessCombinations.ts +92 -0
- package/start/helpers/executeCypress.ts +66 -0
- package/start/helpers/generateHtmlReport.ts +71 -0
- package/start/helpers/generateReportHomepage.ts +148 -0
- package/start/helpers/generateReporterConfig.ts +39 -0
- package/start/helpers/getDateLabeledDir.ts +43 -0
- package/start/helpers/mergeAllReportsAndGenerateHtml.ts +150 -0
- package/start/helpers/mergeReports.ts +82 -0
- package/start/helpers/reportHomepage.ejs +272 -0
- package/start/helpers/runCypressHeadless.ts +164 -0
- package/start/helpers/runCypressVisible.ts +45 -0
- package/start/index.ts +23 -5
- package/start/types/MochawesomeReporterConfig.ts +23 -0
- package/start/types/Profile.ts +12 -0
- package/start/types/ReportInfo.ts +22 -0
- package/start/types/RunResult.ts +18 -0
- package/start/types/ScreenshotInfo.ts +13 -0
- package/start/types/TemplateReportInfo.ts +16 -0
- package/src/commands/visitCanvasGETEndpoint.ts +0 -61
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
// Import libs
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
// Import helpers
|
|
9
|
+
import getRootPath from './getRootPath';
|
|
10
|
+
import RunResult from '../types/RunResult';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Recursively annotate mochawesome suites (not individual tests) with a label
|
|
14
|
+
* so the combined report shows profile+browser for each suite
|
|
15
|
+
* @author Yuen Ler Chow
|
|
16
|
+
* @param node - The mochawesome node to annotate
|
|
17
|
+
* @param label - The label to prepend to suite titles
|
|
18
|
+
*/
|
|
19
|
+
const annotateMochawesomeSuites = (node: any, label: string): void => {
|
|
20
|
+
if (!node || typeof node !== 'object') {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// If this is an array of nodes, annotate each element
|
|
25
|
+
if (Array.isArray(node)) {
|
|
26
|
+
node.forEach((child: any) => {
|
|
27
|
+
annotateMochawesomeSuites(child, label);
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Only annotate if this is a suite (has suites or tests arrays)
|
|
33
|
+
const isSuite = Array.isArray(node.suites) || Array.isArray(node.tests);
|
|
34
|
+
if (isSuite) {
|
|
35
|
+
if (typeof node.title === 'string') {
|
|
36
|
+
// eslint-disable-next-line no-param-reassign
|
|
37
|
+
node.title = `${label}${node.title}`;
|
|
38
|
+
}
|
|
39
|
+
if (typeof node.fullTitle === 'string') {
|
|
40
|
+
// eslint-disable-next-line no-param-reassign
|
|
41
|
+
node.fullTitle = `${label}${node.fullTitle}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Recurse into child suites (but not tests)
|
|
46
|
+
if (Array.isArray(node.suites)) {
|
|
47
|
+
node.suites.forEach((suite: any) => {
|
|
48
|
+
annotateMochawesomeSuites(suite, label);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Merge all per-combination mochawesome JSON reports into a single JSON
|
|
55
|
+
* and generate one combined HTML report for all runs.
|
|
56
|
+
* Each suite/test is annotated with [profile][browser] in its title.
|
|
57
|
+
* @author Yuen Ler Chow
|
|
58
|
+
* @param outputDir - Date-labeled results directory
|
|
59
|
+
* @param results - Array of run results
|
|
60
|
+
*/
|
|
61
|
+
const mergeAllReportsAndGenerateHtml = (
|
|
62
|
+
outputDir: string,
|
|
63
|
+
results: RunResult[],
|
|
64
|
+
): void => {
|
|
65
|
+
// Collect annotated copies of report-data.json files from successful runs
|
|
66
|
+
const annotatedJsonPaths: string[] = [];
|
|
67
|
+
|
|
68
|
+
results.forEach((result) => {
|
|
69
|
+
const jsonPath = path.join(
|
|
70
|
+
outputDir,
|
|
71
|
+
`${result.profileName}-${result.browser}`,
|
|
72
|
+
'report-data.json',
|
|
73
|
+
);
|
|
74
|
+
if (!fs.existsSync(jsonPath)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
// Import the json report
|
|
80
|
+
const raw = fs.readFileSync(jsonPath, 'utf-8');
|
|
81
|
+
const data = JSON.parse(raw);
|
|
82
|
+
const label = `[${result.profileName}][${result.browser}] `;
|
|
83
|
+
|
|
84
|
+
if (Array.isArray(data.results)) {
|
|
85
|
+
data.results.forEach((res: any) => {
|
|
86
|
+
if (res && res.suites) {
|
|
87
|
+
annotateMochawesomeSuites(res.suites, label);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Annotate tests with their profile+browser
|
|
93
|
+
const annotatedPath = path.join(
|
|
94
|
+
outputDir,
|
|
95
|
+
`${result.profileName}-${result.browser}-labeled.json`,
|
|
96
|
+
);
|
|
97
|
+
fs.writeFileSync(annotatedPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
98
|
+
annotatedJsonPaths.push(annotatedPath);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.error(`Error annotating report for ${result.profileName} + ${result.browser}:`, e);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (annotatedJsonPaths.length === 0) {
|
|
105
|
+
console.warn('No annotated report-data.json files found. Skipping global merge.');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const allReportDataPath = path.join(outputDir, 'all-report-data.json');
|
|
110
|
+
const allReportHtmlDir = outputDir;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
// Build mochawesome-merge command with all annotated JSON paths
|
|
114
|
+
const mergedArgs = (
|
|
115
|
+
annotatedJsonPaths
|
|
116
|
+
.map((p) => {
|
|
117
|
+
return `"${p}"`;
|
|
118
|
+
})
|
|
119
|
+
.join(' ')
|
|
120
|
+
);
|
|
121
|
+
const mergeCommand = `npx mochawesome-merge ${mergedArgs} > "${allReportDataPath}"`;
|
|
122
|
+
|
|
123
|
+
console.log('Merging all annotated report-data.json files into a single JSON for all runs...');
|
|
124
|
+
|
|
125
|
+
execSync(mergeCommand, {
|
|
126
|
+
stdio: 'inherit',
|
|
127
|
+
cwd: getRootPath(),
|
|
128
|
+
shell: '/bin/bash',
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Generate combined HTML using marge
|
|
132
|
+
const margeCommand = `npx marge "${allReportDataPath}" --reportDir "${allReportHtmlDir}" --reportFilename "all-runs"`;
|
|
133
|
+
|
|
134
|
+
console.log('Generating combined HTML report for all runs...');
|
|
135
|
+
|
|
136
|
+
execSync(
|
|
137
|
+
margeCommand,
|
|
138
|
+
{
|
|
139
|
+
stdio: 'inherit',
|
|
140
|
+
cwd: getRootPath(),
|
|
141
|
+
},
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
console.log(`Combined HTML report generated at: ${path.join(allReportHtmlDir, 'all-runs.html')}`);
|
|
145
|
+
} catch (e) {
|
|
146
|
+
console.error('Error generating combined HTML report for all runs:', e);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default mergeAllReportsAndGenerateHtml;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { existsSync, readdirSync } from 'fs';
|
|
5
|
+
import getRootPath from './getRootPath';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Merge mochawesome JSON reports into a single JSON file using mochawesome-merge
|
|
9
|
+
* @author Yuen Ler Chow
|
|
10
|
+
* @param opts - object of arguments
|
|
11
|
+
* @param opts.resultsDir - Results directory containing the reports
|
|
12
|
+
* @param opts.profileName - Profile name
|
|
13
|
+
* @param opts.browserName - Browser name
|
|
14
|
+
*/
|
|
15
|
+
const mergeReports = (
|
|
16
|
+
opts: {
|
|
17
|
+
resultsDir: string;
|
|
18
|
+
profileName: string;
|
|
19
|
+
browserName: string;
|
|
20
|
+
},
|
|
21
|
+
): void => {
|
|
22
|
+
const { resultsDir, profileName, browserName } = opts;
|
|
23
|
+
|
|
24
|
+
const granularResultsDir = path.join(
|
|
25
|
+
resultsDir,
|
|
26
|
+
`${profileName}-${browserName}`,
|
|
27
|
+
'granular-results',
|
|
28
|
+
);
|
|
29
|
+
const reportDataPath = path.join(
|
|
30
|
+
resultsDir,
|
|
31
|
+
`${profileName}-${browserName}`,
|
|
32
|
+
'report-data.json',
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
console.log(`\n📊 Merge Reports Debug for ${profileName} + ${browserName}:`);
|
|
36
|
+
console.log(` Granular results dir: ${granularResultsDir}`);
|
|
37
|
+
console.log(` Report data path: ${reportDataPath}`);
|
|
38
|
+
|
|
39
|
+
if (!existsSync(granularResultsDir)) {
|
|
40
|
+
const errorMsg = `❌ ERROR: Granular results directory does not exist: ${granularResultsDir}`;
|
|
41
|
+
console.error(errorMsg);
|
|
42
|
+
throw new Error(errorMsg);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// List files in granular results directory
|
|
46
|
+
const files = readdirSync(granularResultsDir);
|
|
47
|
+
const jsonFiles = files.filter((f) => { return f.endsWith('.json'); });
|
|
48
|
+
console.log(` Found ${jsonFiles.length} JSON files: ${jsonFiles.join(', ') || '(none)'}`);
|
|
49
|
+
|
|
50
|
+
if (jsonFiles.length === 0) {
|
|
51
|
+
const errorMsg = `❌ ERROR: No JSON files found in granular results directory: ${granularResultsDir}`;
|
|
52
|
+
console.error(errorMsg);
|
|
53
|
+
throw new Error(errorMsg);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const command = `npx mochawesome-merge "${path.join(granularResultsDir, '*.json')}" > "${reportDataPath}"`;
|
|
58
|
+
|
|
59
|
+
console.log(` Running: ${command}`);
|
|
60
|
+
console.log(`Merging reports for ${profileName} + ${browserName}...`);
|
|
61
|
+
|
|
62
|
+
execSync(command, {
|
|
63
|
+
stdio: 'inherit',
|
|
64
|
+
cwd: getRootPath(),
|
|
65
|
+
shell: '/bin/bash',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Verify the report was created
|
|
69
|
+
if (!existsSync(reportDataPath)) {
|
|
70
|
+
const errorMsg = `❌ ERROR: Report data was not created at: ${reportDataPath}`;
|
|
71
|
+
console.error(errorMsg);
|
|
72
|
+
throw new Error(errorMsg);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(` ✅ Report data created successfully at: ${reportDataPath}`);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
console.error(`Error merging reports for ${profileName} + ${browserName}:`, e);
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default mergeReports;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Cypress Test Reports</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
body {
|
|
14
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
15
|
+
background: #f5f5f5;
|
|
16
|
+
padding: 2rem 1rem;
|
|
17
|
+
color: #222;
|
|
18
|
+
}
|
|
19
|
+
.container {
|
|
20
|
+
max-width: 75rem;
|
|
21
|
+
margin: 0 auto;
|
|
22
|
+
background: #ffffff;
|
|
23
|
+
border-radius: 0.5rem;
|
|
24
|
+
border: 0.0625rem solid #e0e0e0;
|
|
25
|
+
box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.04);
|
|
26
|
+
}
|
|
27
|
+
header {
|
|
28
|
+
border-bottom: 0.0625rem solid #e0e0e0;
|
|
29
|
+
padding: 1.25rem 1.5rem;
|
|
30
|
+
text-align: center;
|
|
31
|
+
}
|
|
32
|
+
h1 {
|
|
33
|
+
font-size: 1.75rem;
|
|
34
|
+
margin-bottom: 0.625rem;
|
|
35
|
+
}
|
|
36
|
+
.subtitle {
|
|
37
|
+
font-size: 0.95rem;
|
|
38
|
+
color: #666666;
|
|
39
|
+
}
|
|
40
|
+
.content {
|
|
41
|
+
padding: 1.5rem;
|
|
42
|
+
}
|
|
43
|
+
.profile-section {
|
|
44
|
+
margin-bottom: 2.5rem;
|
|
45
|
+
}
|
|
46
|
+
.profile-title {
|
|
47
|
+
font-size: 1.1rem;
|
|
48
|
+
color: #111111;
|
|
49
|
+
margin-bottom: 0.75rem;
|
|
50
|
+
border-bottom: 0.0625rem solid #e0e0e0;
|
|
51
|
+
padding-bottom: 0.375rem;
|
|
52
|
+
}
|
|
53
|
+
.report-grid {
|
|
54
|
+
display: grid;
|
|
55
|
+
grid-template-columns: repeat(auto-fill, minmax(15.625rem, 1fr));
|
|
56
|
+
gap: 1.25rem;
|
|
57
|
+
margin-bottom: 1.875rem;
|
|
58
|
+
}
|
|
59
|
+
.report-card {
|
|
60
|
+
border: 0.0625rem solid #e0e0e0;
|
|
61
|
+
border-radius: 0.5rem;
|
|
62
|
+
padding: 1rem;
|
|
63
|
+
background: #fafafa;
|
|
64
|
+
}
|
|
65
|
+
.report-card.success {
|
|
66
|
+
border-color: #c8e6c9;
|
|
67
|
+
background: #f4f9f4;
|
|
68
|
+
}
|
|
69
|
+
.report-card.failed {
|
|
70
|
+
border-color: #ffcdd2;
|
|
71
|
+
background: #fdf5f6;
|
|
72
|
+
}
|
|
73
|
+
.browser-name {
|
|
74
|
+
font-size: 1rem;
|
|
75
|
+
font-weight: bold;
|
|
76
|
+
color: #222222;
|
|
77
|
+
margin-bottom: 0.625rem;
|
|
78
|
+
text-transform: capitalize;
|
|
79
|
+
}
|
|
80
|
+
.status {
|
|
81
|
+
display: inline-block;
|
|
82
|
+
padding: 0.25rem 0.75rem;
|
|
83
|
+
border-radius: 0.75rem;
|
|
84
|
+
font-size: 0.85em;
|
|
85
|
+
font-weight: bold;
|
|
86
|
+
margin-bottom: 0.625rem;
|
|
87
|
+
}
|
|
88
|
+
.status.success {
|
|
89
|
+
background: #2e7d32;
|
|
90
|
+
color: white;
|
|
91
|
+
}
|
|
92
|
+
.status.failed {
|
|
93
|
+
background: #c62828;
|
|
94
|
+
color: white;
|
|
95
|
+
}
|
|
96
|
+
.report-link {
|
|
97
|
+
display: inline-block;
|
|
98
|
+
margin-top: 0.625rem;
|
|
99
|
+
padding: 0.5rem 1rem;
|
|
100
|
+
background: #1976d2;
|
|
101
|
+
color: white;
|
|
102
|
+
text-decoration: none;
|
|
103
|
+
border-radius: 0.375rem;
|
|
104
|
+
transition: background 0.3s ease;
|
|
105
|
+
}
|
|
106
|
+
.report-link:hover {
|
|
107
|
+
background: #115293;
|
|
108
|
+
}
|
|
109
|
+
.timestamp {
|
|
110
|
+
font-size: 0.85em;
|
|
111
|
+
color: #777777;
|
|
112
|
+
margin-top: 0.5rem;
|
|
113
|
+
}
|
|
114
|
+
.summary {
|
|
115
|
+
background: #fafafa;
|
|
116
|
+
padding: 1rem 1.125rem;
|
|
117
|
+
border-radius: 0.375rem;
|
|
118
|
+
margin-bottom: 1.875rem;
|
|
119
|
+
border: 0.0625rem solid #e0e0e0;
|
|
120
|
+
}
|
|
121
|
+
.summary h2 {
|
|
122
|
+
color: #111111;
|
|
123
|
+
margin-bottom: 0.625rem;
|
|
124
|
+
font-size: 1rem;
|
|
125
|
+
}
|
|
126
|
+
.summary-stats {
|
|
127
|
+
display: flex;
|
|
128
|
+
gap: 1.25rem;
|
|
129
|
+
flex-wrap: wrap;
|
|
130
|
+
}
|
|
131
|
+
.stat {
|
|
132
|
+
display: flex;
|
|
133
|
+
flex-direction: column;
|
|
134
|
+
}
|
|
135
|
+
.stat-value {
|
|
136
|
+
font-size: 1.4rem;
|
|
137
|
+
font-weight: bold;
|
|
138
|
+
color: #1976d2;
|
|
139
|
+
}
|
|
140
|
+
.stat-label {
|
|
141
|
+
font-size: 0.9em;
|
|
142
|
+
color: #777777;
|
|
143
|
+
text-transform: uppercase;
|
|
144
|
+
letter-spacing: 0.0625rem;
|
|
145
|
+
}
|
|
146
|
+
.combined-report {
|
|
147
|
+
margin-bottom: 1.5rem;
|
|
148
|
+
padding: 0.875rem 1rem;
|
|
149
|
+
border-radius: 0.375rem;
|
|
150
|
+
border: 0.0625rem solid #d0d0d0;
|
|
151
|
+
background: #f9fafb;
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: space-between;
|
|
155
|
+
gap: 0.75rem;
|
|
156
|
+
}
|
|
157
|
+
.combined-report-title {
|
|
158
|
+
font-size: 0.95rem;
|
|
159
|
+
font-weight: 500;
|
|
160
|
+
color: #111111;
|
|
161
|
+
}
|
|
162
|
+
.combined-report-subtitle {
|
|
163
|
+
font-size: 0.85rem;
|
|
164
|
+
color: #777777;
|
|
165
|
+
margin-top: 0.125rem;
|
|
166
|
+
}
|
|
167
|
+
.combined-report-button {
|
|
168
|
+
padding: 0.5rem 1rem;
|
|
169
|
+
border-radius: 0.25rem;
|
|
170
|
+
border: 0.0625rem solid #1976d2;
|
|
171
|
+
background: #1976d2;
|
|
172
|
+
color: #ffffff;
|
|
173
|
+
text-decoration: none;
|
|
174
|
+
font-size: 0.9rem;
|
|
175
|
+
font-weight: 500;
|
|
176
|
+
white-space: nowrap;
|
|
177
|
+
}
|
|
178
|
+
.combined-report-button:hover {
|
|
179
|
+
background: #115293;
|
|
180
|
+
border-color: #115293;
|
|
181
|
+
}
|
|
182
|
+
.screenshot-list {
|
|
183
|
+
list-style: none;
|
|
184
|
+
padding-left: 0;
|
|
185
|
+
margin-top: 0.5rem;
|
|
186
|
+
}
|
|
187
|
+
.no-report {
|
|
188
|
+
color: #999;
|
|
189
|
+
font-size: 0.9em;
|
|
190
|
+
}
|
|
191
|
+
</style>
|
|
192
|
+
</head>
|
|
193
|
+
<body>
|
|
194
|
+
<div class="container">
|
|
195
|
+
<header>
|
|
196
|
+
<h1>Cypress Test Reports</h1>
|
|
197
|
+
<p class="subtitle">Profile and browser run summary</p>
|
|
198
|
+
</header>
|
|
199
|
+
<div class="content">
|
|
200
|
+
<% if (hasCombinedReport) { %>
|
|
201
|
+
<div class="combined-report">
|
|
202
|
+
<div>
|
|
203
|
+
<div class="combined-report-title">Combined report for all profiles and browsers</div>
|
|
204
|
+
<div class="combined-report-subtitle">
|
|
205
|
+
<% if (combinedTotalTests !== null && combinedPassedTests !== null) { %>
|
|
206
|
+
<%= combinedPassedTests %>/<%= combinedTotalTests %> tests passing in this run.
|
|
207
|
+
<% } else { %>
|
|
208
|
+
View a single consolidated Mochawesome report for this run.
|
|
209
|
+
<% } %>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
<a href="all-runs.html" class="combined-report-button" target="_blank">Open combined report</a>
|
|
213
|
+
</div>
|
|
214
|
+
<% } %>
|
|
215
|
+
|
|
216
|
+
<div class="summary">
|
|
217
|
+
<h2>Summary</h2>
|
|
218
|
+
<div class="summary-stats">
|
|
219
|
+
<div class="stat">
|
|
220
|
+
<div class="stat-value"><%= totalRuns %></div>
|
|
221
|
+
<div class="stat-label">Total Runs</div>
|
|
222
|
+
</div>
|
|
223
|
+
<div class="stat">
|
|
224
|
+
<div class="stat-value"><%= passedRuns %></div>
|
|
225
|
+
<div class="stat-label">Passed</div>
|
|
226
|
+
</div>
|
|
227
|
+
<div class="stat">
|
|
228
|
+
<div class="stat-value"><%= failedRuns %></div>
|
|
229
|
+
<div class="stat-label">Failed</div>
|
|
230
|
+
</div>
|
|
231
|
+
<div class="stat">
|
|
232
|
+
<div class="stat-value"><%= profileCount %></div>
|
|
233
|
+
<div class="stat-label">Profiles</div>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<% Object.entries(reportsByProfile).forEach(([profileName, profileReports]) => { %>
|
|
239
|
+
<div class="profile-section">
|
|
240
|
+
<h2 class="profile-title">📋 Profile: <%= profileName %></h2>
|
|
241
|
+
<div class="report-grid">
|
|
242
|
+
<% profileReports.forEach((report) => { %>
|
|
243
|
+
<div class="report-card <%= report.success ? 'success' : 'failed' %>">
|
|
244
|
+
<div class="browser-name">🌐 <%= report.browser %></div>
|
|
245
|
+
<span class="status <%= report.success ? 'success' : 'failed' %>">
|
|
246
|
+
<%= report.success ? '✅ Passed' : '❌ Failed' %>
|
|
247
|
+
</span>
|
|
248
|
+
<% if (report.totalTests !== undefined && report.passedTests !== undefined) { %>
|
|
249
|
+
<div class="timestamp"><%= report.passedTests %>/<%= report.totalTests %> tests passing</div>
|
|
250
|
+
<% } %>
|
|
251
|
+
<div class="timestamp"><%= report.timestamp %></div>
|
|
252
|
+
<% if (report.relativeReportPath) { %>
|
|
253
|
+
<a href="<%= report.relativeReportPath %>" class="report-link" target="_blank">View Report</a>
|
|
254
|
+
<% } else { %>
|
|
255
|
+
<p class="no-report">Report not available</p>
|
|
256
|
+
<% } %>
|
|
257
|
+
<% if (report.screenshots && report.screenshots.length > 0) { %>
|
|
258
|
+
<ul class="screenshot-list">
|
|
259
|
+
<% report.screenshots.forEach((screenshot) => { %>
|
|
260
|
+
<li><a href="<%= screenshot.href %>" class="report-link" target="_blank"><%= screenshot.name %></a></li>
|
|
261
|
+
<% }) %>
|
|
262
|
+
</ul>
|
|
263
|
+
<% } %>
|
|
264
|
+
</div>
|
|
265
|
+
<% }) %>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
<% }) %>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
</body>
|
|
272
|
+
</html>
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
// Import libs
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
|
|
8
|
+
// Import helpers
|
|
9
|
+
import getRootPath from './getRootPath';
|
|
10
|
+
import generateReporterConfig from './generateReporterConfig';
|
|
11
|
+
import mergeReports from './mergeReports';
|
|
12
|
+
import generateHtmlReport from './generateHtmlReport';
|
|
13
|
+
|
|
14
|
+
// Import shared types
|
|
15
|
+
import RunResult from '../types/RunResult';
|
|
16
|
+
|
|
17
|
+
// Import constants
|
|
18
|
+
import DEFAULT_THREADS_PER_COMBO from '../constants/DEFAULT_THREADS_PER_COMBO';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Run Cypress in headless mode for a single profile and browser using cypress-parallel
|
|
22
|
+
* @author Yuen Ler Chow
|
|
23
|
+
* @param opts - object of arguments
|
|
24
|
+
* @param opts.profileName - The profile name to use
|
|
25
|
+
* @param opts.browser - The browser to use
|
|
26
|
+
* @param opts.outputDir - The output directory for results
|
|
27
|
+
* @param [opts.numThreads] - The number of parallel threads to use (default: 2)
|
|
28
|
+
* @returns Promise that resolves with run result
|
|
29
|
+
*/
|
|
30
|
+
const runCypressHeadless = (opts: {
|
|
31
|
+
profileName: string,
|
|
32
|
+
browser: string,
|
|
33
|
+
outputDir: string,
|
|
34
|
+
numThreads?: number,
|
|
35
|
+
}): Promise<RunResult> => {
|
|
36
|
+
const {
|
|
37
|
+
profileName,
|
|
38
|
+
browser,
|
|
39
|
+
outputDir,
|
|
40
|
+
numThreads = DEFAULT_THREADS_PER_COMBO,
|
|
41
|
+
} = opts;
|
|
42
|
+
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const root = getRootPath();
|
|
45
|
+
|
|
46
|
+
// Ensure output directory exists
|
|
47
|
+
if (!fs.existsSync(outputDir)) {
|
|
48
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Create subdirectory for this profile+browser combination
|
|
52
|
+
const comboDir = path.join(outputDir, `${profileName}-${browser}`);
|
|
53
|
+
if (!fs.existsSync(comboDir)) {
|
|
54
|
+
fs.mkdirSync(comboDir, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Create per-combo screenshots directory
|
|
58
|
+
const comboScreenshotsDir = path.join(comboDir, 'screenshots');
|
|
59
|
+
if (!fs.existsSync(comboScreenshotsDir)) {
|
|
60
|
+
fs.mkdirSync(comboScreenshotsDir, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const env = {
|
|
64
|
+
...process.env,
|
|
65
|
+
CYPRESS_PROFILE: profileName,
|
|
66
|
+
BROWSER: browser,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
console.log(`\n🚀 Starting Cypress headless: ${profileName} + ${browser}`);
|
|
70
|
+
console.log(`Output: ${comboDir}\n`);
|
|
71
|
+
|
|
72
|
+
// Create reporter config for cypress-multi-reporters
|
|
73
|
+
const reporterConfigPath = path.join(comboDir, 'reporter-config.json');
|
|
74
|
+
const reporterConfig = generateReporterConfig({
|
|
75
|
+
resultsDir: outputDir,
|
|
76
|
+
profileName,
|
|
77
|
+
browserName: browser,
|
|
78
|
+
});
|
|
79
|
+
fs.writeFileSync(reporterConfigPath, JSON.stringify(reporterConfig, null, 2));
|
|
80
|
+
|
|
81
|
+
// Cypress parallel run command with browser, threads, and reporter config
|
|
82
|
+
const cypressArgsString = `--headless --browser ${browser} --config screenshotsFolder=${comboScreenshotsDir} --reporter cypress-multi-reporters --reporter-options configFile=${reporterConfigPath}`;
|
|
83
|
+
|
|
84
|
+
console.log(`Reporter config path: ${reporterConfigPath}`);
|
|
85
|
+
console.log(`Granular results dir: ${path.join(comboDir, 'granular-results')}`);
|
|
86
|
+
|
|
87
|
+
const args = [
|
|
88
|
+
'cypress-parallel',
|
|
89
|
+
'-s',
|
|
90
|
+
'cy:run',
|
|
91
|
+
'--threads',
|
|
92
|
+
numThreads.toString(),
|
|
93
|
+
'-d',
|
|
94
|
+
'cypress/e2e/**/*',
|
|
95
|
+
'-a',
|
|
96
|
+
// eslint-disable-next-line quotes
|
|
97
|
+
`'"${cypressArgsString}"'`,
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const cypressProcess = spawn('npx', args, {
|
|
101
|
+
cwd: root,
|
|
102
|
+
env,
|
|
103
|
+
stdio: 'inherit',
|
|
104
|
+
shell: true,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
cypressProcess.on('close', (code) => {
|
|
108
|
+
const success = code === 0;
|
|
109
|
+
|
|
110
|
+
// Merge granular mochawesome JSON and generate a single HTML report
|
|
111
|
+
let reportGenerated = true;
|
|
112
|
+
try {
|
|
113
|
+
// Merge all json test suites (test files) into one report
|
|
114
|
+
mergeReports({
|
|
115
|
+
resultsDir: outputDir,
|
|
116
|
+
profileName,
|
|
117
|
+
browserName: browser,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Generate combined HTML report for all test suites for this single browser+profile combo
|
|
121
|
+
generateHtmlReport({
|
|
122
|
+
resultsDir: outputDir,
|
|
123
|
+
profileName,
|
|
124
|
+
browserName: browser,
|
|
125
|
+
});
|
|
126
|
+
} catch (reportError) {
|
|
127
|
+
console.error(`\n⚠️ Report generation failed for ${profileName} + ${browser}:`, reportError);
|
|
128
|
+
reportGenerated = false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// By default, marge names the HTML after the JSON basename: report-data.html
|
|
132
|
+
const reportPath = path.join(
|
|
133
|
+
outputDir,
|
|
134
|
+
`${profileName}-${browser}`,
|
|
135
|
+
'report',
|
|
136
|
+
'report-data.html',
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const result: RunResult = {
|
|
140
|
+
profileName,
|
|
141
|
+
browser,
|
|
142
|
+
success: success && reportGenerated,
|
|
143
|
+
outputDir: comboDir,
|
|
144
|
+
reportPath: reportGenerated ? reportPath : undefined,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
if (success && reportGenerated) {
|
|
148
|
+
console.log(`\n✅ Completed: ${profileName} + ${browser}`);
|
|
149
|
+
} else if (!reportGenerated) {
|
|
150
|
+
console.log(`\n⚠️ Tests ran but report generation failed: ${profileName} + ${browser}`);
|
|
151
|
+
} else {
|
|
152
|
+
console.log(`\n❌ Failed: ${profileName} + ${browser} (exit code: ${code})`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
resolve(result);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
cypressProcess.on('error', (error) => {
|
|
159
|
+
reject(error);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export default runCypressHeadless;
|