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.
Files changed (237) hide show
  1. package/README.md +45 -29
  2. package/lib/commands/assertDoesNotHaveClass.d.ts +18 -0
  3. package/lib/commands/assertDoesNotHaveClass.js +17 -0
  4. package/lib/commands/assertDoesNotHaveClass.js.map +1 -0
  5. package/lib/commands/assertHasClass.d.ts +18 -0
  6. package/lib/commands/assertHasClass.js +17 -0
  7. package/lib/commands/assertHasClass.js.map +1 -0
  8. package/lib/commands/assertNumElements.d.ts +18 -0
  9. package/lib/commands/assertNumElements.js +17 -0
  10. package/lib/commands/assertNumElements.js.map +1 -0
  11. package/lib/commands/clickWithRetry.d.ts +19 -0
  12. package/lib/commands/clickWithRetry.js +29 -0
  13. package/lib/commands/clickWithRetry.js.map +1 -0
  14. package/lib/commands/genTextOfLength.d.ts +20 -0
  15. package/lib/commands/genTextOfLength.js +20 -0
  16. package/lib/commands/genTextOfLength.js.map +1 -0
  17. package/lib/commands/getAttribute.d.ts +20 -0
  18. package/lib/commands/getAttribute.js +22 -0
  19. package/lib/commands/getAttribute.js.map +1 -0
  20. package/lib/commands/getClassName.d.ts +15 -0
  21. package/lib/commands/getClassName.js +21 -0
  22. package/lib/commands/getClassName.js.map +1 -0
  23. package/lib/commands/getCurrentDateInfo.d.ts +21 -0
  24. package/lib/commands/getCurrentDateInfo.js +25 -0
  25. package/lib/commands/getCurrentDateInfo.js.map +1 -0
  26. package/lib/commands/getId.d.ts +15 -0
  27. package/lib/commands/getId.js +21 -0
  28. package/lib/commands/getId.js.map +1 -0
  29. package/lib/commands/getNumElements.d.ts +14 -0
  30. package/lib/commands/getNumElements.js +19 -0
  31. package/lib/commands/getNumElements.js.map +1 -0
  32. package/lib/commands/getSpecialChars.d.ts +14 -0
  33. package/lib/commands/getSpecialChars.js +16 -0
  34. package/lib/commands/getSpecialChars.js.map +1 -0
  35. package/lib/commands/getTitle.d.ts +13 -0
  36. package/lib/commands/getTitle.js +20 -0
  37. package/lib/commands/getTitle.js.map +1 -0
  38. package/lib/commands/handleHarvardKey.d.ts +18 -0
  39. package/lib/commands/handleHarvardKey.js +56 -0
  40. package/lib/commands/handleHarvardKey.js.map +1 -0
  41. package/lib/commands/handleHarvardKey2.d.ts +14 -0
  42. package/lib/commands/handleHarvardKey2.js +88 -0
  43. package/lib/commands/handleHarvardKey2.js.map +1 -0
  44. package/lib/commands/launchAs.d.ts +20 -0
  45. package/lib/commands/launchAs.js +60 -0
  46. package/lib/commands/launchAs.js.map +1 -0
  47. package/lib/commands/launchLTIUsingToken.d.ts +19 -0
  48. package/lib/commands/launchLTIUsingToken.js +74 -0
  49. package/lib/commands/launchLTIUsingToken.js.map +1 -0
  50. package/lib/commands/listSelectLabels.d.ts +15 -0
  51. package/lib/commands/listSelectLabels.js +24 -0
  52. package/lib/commands/listSelectLabels.js.map +1 -0
  53. package/lib/commands/listSelectValues.d.ts +15 -0
  54. package/lib/commands/listSelectValues.js +26 -0
  55. package/lib/commands/listSelectValues.js.map +1 -0
  56. package/lib/commands/navigateToHref.d.ts +19 -0
  57. package/lib/commands/navigateToHref.js +23 -0
  58. package/lib/commands/navigateToHref.js.map +1 -0
  59. package/lib/commands/padWithZeros.d.ts +20 -0
  60. package/lib/commands/padWithZeros.js +24 -0
  61. package/lib/commands/padWithZeros.js.map +1 -0
  62. package/lib/commands/runScript.d.ts +16 -0
  63. package/lib/commands/runScript.js +25 -0
  64. package/lib/commands/runScript.js.map +1 -0
  65. package/lib/commands/typeInto.d.ts +20 -0
  66. package/lib/commands/typeInto.js +28 -0
  67. package/lib/commands/typeInto.js.map +1 -0
  68. package/lib/commands/uniquify.d.ts +15 -0
  69. package/lib/commands/uniquify.js +25 -0
  70. package/lib/commands/uniquify.js.map +1 -0
  71. package/lib/commands/visitCanvasEndpoint.d.ts +27 -0
  72. package/lib/commands/visitCanvasEndpoint.js +35 -0
  73. package/lib/commands/visitCanvasEndpoint.js.map +1 -0
  74. package/lib/commands/visitCanvasGETEndpoint.d.ts +20 -0
  75. package/lib/commands/visitCanvasGETEndpoint.js +26 -0
  76. package/lib/commands/visitCanvasGETEndpoint.js.map +1 -0
  77. package/lib/commands/waitForAtLeastOneElementPresent.d.ts +23 -0
  78. package/lib/commands/waitForAtLeastOneElementPresent.js +27 -0
  79. package/lib/commands/waitForAtLeastOneElementPresent.js.map +1 -0
  80. package/lib/commands/waitForElementVisible.d.ts +14 -0
  81. package/lib/commands/waitForElementVisible.js +20 -0
  82. package/lib/commands/waitForElementVisible.js.map +1 -0
  83. package/lib/index.js.map +1 -0
  84. package/lib/init.d.ts +6 -0
  85. package/lib/init.js +69 -0
  86. package/lib/init.js.map +1 -0
  87. package/lib/src/commands/extractDataFromClass.d.ts +7 -4
  88. package/lib/src/commands/extractDataFromClass.js +2 -1
  89. package/lib/src/commands/extractDataFromClass.js.map +1 -1
  90. package/lib/src/commands/extractDataFromClassByContents.d.ts +9 -5
  91. package/lib/src/commands/extractDataFromClassByContents.js +2 -1
  92. package/lib/src/commands/extractDataFromClassByContents.js.map +1 -1
  93. package/lib/src/commands/genTextOfLength.d.ts +20 -0
  94. package/lib/src/commands/genTextOfLength.js +20 -0
  95. package/lib/src/commands/genTextOfLength.js.map +1 -0
  96. package/lib/src/commands/getAttribute.d.ts +20 -0
  97. package/lib/src/commands/getAttribute.js +22 -0
  98. package/lib/src/commands/getAttribute.js.map +1 -0
  99. package/lib/src/commands/getClassName.d.ts +15 -0
  100. package/lib/src/commands/getClassName.js +21 -0
  101. package/lib/src/commands/getClassName.js.map +1 -0
  102. package/lib/src/commands/getCurrentDateInfo.d.ts +21 -0
  103. package/lib/src/commands/getCurrentDateInfo.js +25 -0
  104. package/lib/src/commands/getCurrentDateInfo.js.map +1 -0
  105. package/lib/src/commands/getId.d.ts +15 -0
  106. package/lib/src/commands/getId.js +21 -0
  107. package/lib/src/commands/getId.js.map +1 -0
  108. package/lib/src/commands/getJSON.d.ts +0 -1
  109. package/lib/src/commands/getJSON.js +1 -4
  110. package/lib/src/commands/getJSON.js.map +1 -1
  111. package/lib/src/commands/getSpecialChars.d.ts +14 -0
  112. package/lib/src/commands/getSpecialChars.js +16 -0
  113. package/lib/src/commands/getSpecialChars.js.map +1 -0
  114. package/lib/src/commands/getTitle.d.ts +13 -0
  115. package/lib/src/commands/getTitle.js +20 -0
  116. package/lib/src/commands/getTitle.js.map +1 -0
  117. package/lib/src/commands/index.js +29 -5
  118. package/lib/src/commands/index.js.map +1 -1
  119. package/lib/src/commands/launchLTIUsingToken.js +12 -4
  120. package/lib/src/commands/launchLTIUsingToken.js.map +1 -1
  121. package/lib/src/commands/listSelectLabels.d.ts +15 -0
  122. package/lib/src/commands/listSelectLabels.js +24 -0
  123. package/lib/src/commands/listSelectLabels.js.map +1 -0
  124. package/lib/src/commands/listSelectValues.d.ts +15 -0
  125. package/lib/src/commands/listSelectValues.js +27 -0
  126. package/lib/src/commands/listSelectValues.js.map +1 -0
  127. package/lib/src/commands/padWithZeros.d.ts +20 -0
  128. package/lib/src/commands/padWithZeros.js +24 -0
  129. package/lib/src/commands/padWithZeros.js.map +1 -0
  130. package/lib/src/commands/uniquify.d.ts +15 -0
  131. package/lib/src/commands/uniquify.js +26 -0
  132. package/lib/src/commands/uniquify.js.map +1 -0
  133. package/lib/src/commands/visitCanvasEndpoint.d.ts +26 -0
  134. package/lib/src/commands/visitCanvasEndpoint.js +36 -0
  135. package/lib/src/commands/visitCanvasEndpoint.js.map +1 -0
  136. package/lib/src/commands/waitForAtLeastOneElementPresent.d.ts +23 -0
  137. package/lib/src/commands/waitForAtLeastOneElementPresent.js +28 -0
  138. package/lib/src/commands/waitForAtLeastOneElementPresent.js.map +1 -0
  139. package/lib/src/init.js +28 -2
  140. package/lib/src/init.js.map +1 -1
  141. package/lib/start/constants/DEFAULT_THREADS_PER_COMBO.d.ts +6 -0
  142. package/lib/start/constants/DEFAULT_THREADS_PER_COMBO.js +9 -0
  143. package/lib/start/constants/DEFAULT_THREADS_PER_COMBO.js.map +1 -0
  144. package/lib/start/helpers/collectPngFiles.d.ts +9 -0
  145. package/lib/start/helpers/collectPngFiles.js +31 -0
  146. package/lib/start/helpers/collectPngFiles.js.map +1 -0
  147. package/lib/start/helpers/executeAllHeadlessCombinations.d.ts +15 -0
  148. package/lib/start/helpers/executeAllHeadlessCombinations.js +116 -0
  149. package/lib/start/helpers/executeAllHeadlessCombinations.js.map +1 -0
  150. package/lib/start/helpers/executeCypress.d.ts +17 -0
  151. package/lib/start/helpers/executeCypress.js +103 -0
  152. package/lib/start/helpers/executeCypress.js.map +1 -0
  153. package/lib/start/helpers/generateHtmlReport.d.ts +14 -0
  154. package/lib/start/helpers/generateHtmlReport.js +54 -0
  155. package/lib/start/helpers/generateHtmlReport.js.map +1 -0
  156. package/lib/start/helpers/generateReportHomepage.d.ts +9 -0
  157. package/lib/start/helpers/generateReportHomepage.js +142 -0
  158. package/lib/start/helpers/generateReportHomepage.js.map +1 -0
  159. package/lib/start/helpers/generateReporterConfig.d.ts +17 -0
  160. package/lib/start/helpers/generateReporterConfig.js +32 -0
  161. package/lib/start/helpers/generateReporterConfig.js.map +1 -0
  162. package/lib/start/helpers/getDateLabeledDir.d.ts +7 -0
  163. package/lib/start/helpers/getDateLabeledDir.js +38 -0
  164. package/lib/start/helpers/getDateLabeledDir.js.map +1 -0
  165. package/lib/start/helpers/mergeAllReportsAndGenerateHtml.d.ts +11 -0
  166. package/lib/start/helpers/mergeAllReportsAndGenerateHtml.js +121 -0
  167. package/lib/start/helpers/mergeAllReportsAndGenerateHtml.js.map +1 -0
  168. package/lib/start/helpers/mergeReports.d.ts +14 -0
  169. package/lib/start/helpers/mergeReports.js +63 -0
  170. package/lib/start/helpers/mergeReports.js.map +1 -0
  171. package/lib/start/helpers/reportHomepage.ejs +272 -0
  172. package/lib/start/helpers/runCypressHeadless.d.ts +18 -0
  173. package/lib/start/helpers/runCypressHeadless.js +138 -0
  174. package/lib/start/helpers/runCypressHeadless.js.map +1 -0
  175. package/lib/start/helpers/runCypressVisible.d.ts +8 -0
  176. package/lib/start/helpers/runCypressVisible.js +53 -0
  177. package/lib/start/helpers/runCypressVisible.js.map +1 -0
  178. package/lib/start/index.js +23 -5
  179. package/lib/start/index.js.map +1 -1
  180. package/lib/start/types/MochawesomeReporterConfig.d.ts +15 -0
  181. package/lib/start/types/MochawesomeReporterConfig.js +3 -0
  182. package/lib/start/types/MochawesomeReporterConfig.js.map +1 -0
  183. package/lib/start/types/Profile.d.ts +9 -0
  184. package/lib/start/types/Profile.js +3 -0
  185. package/lib/start/types/Profile.js.map +1 -0
  186. package/lib/start/types/ReportInfo.d.ts +14 -0
  187. package/lib/start/types/ReportInfo.js +3 -0
  188. package/lib/start/types/ReportInfo.js.map +1 -0
  189. package/lib/start/types/RunResult.d.ts +12 -0
  190. package/lib/start/types/RunResult.js +3 -0
  191. package/lib/start/types/RunResult.js.map +1 -0
  192. package/lib/start/types/ScreenshotInfo.d.ts +10 -0
  193. package/lib/start/types/ScreenshotInfo.js +3 -0
  194. package/lib/start/types/ScreenshotInfo.js.map +1 -0
  195. package/lib/start/types/TemplateReportInfo.d.ts +12 -0
  196. package/lib/start/types/TemplateReportInfo.js +3 -0
  197. package/lib/start/types/TemplateReportInfo.js.map +1 -0
  198. package/package.json +10 -8
  199. package/src/commands/extractDataFromClass.ts +17 -6
  200. package/src/commands/extractDataFromClassByContents.ts +21 -8
  201. package/src/commands/genTextOfLength.ts +54 -0
  202. package/src/commands/getAttribute.ts +58 -0
  203. package/src/commands/getClassName.ts +44 -0
  204. package/src/commands/getCurrentDateInfo.ts +57 -0
  205. package/src/commands/getId.ts +44 -0
  206. package/src/commands/getJSON.ts +0 -4
  207. package/src/commands/getSpecialChars.ts +34 -0
  208. package/src/commands/getTitle.ts +39 -0
  209. package/src/commands/index.ts +29 -5
  210. package/src/commands/launchLTIUsingToken.ts +12 -4
  211. package/src/commands/listSelectLabels.ts +47 -0
  212. package/src/commands/listSelectValues.ts +50 -0
  213. package/src/commands/padWithZeros.ts +53 -0
  214. package/src/commands/uniquify.ts +49 -0
  215. package/src/commands/visitCanvasEndpoint.ts +75 -0
  216. package/src/commands/waitForAtLeastOneElementPresent.ts +64 -0
  217. package/start/constants/DEFAULT_THREADS_PER_COMBO.ts +7 -0
  218. package/start/helpers/collectPngFiles.ts +25 -0
  219. package/start/helpers/executeAllHeadlessCombinations.ts +92 -0
  220. package/start/helpers/executeCypress.ts +66 -0
  221. package/start/helpers/generateHtmlReport.ts +71 -0
  222. package/start/helpers/generateReportHomepage.ts +148 -0
  223. package/start/helpers/generateReporterConfig.ts +39 -0
  224. package/start/helpers/getDateLabeledDir.ts +43 -0
  225. package/start/helpers/mergeAllReportsAndGenerateHtml.ts +150 -0
  226. package/start/helpers/mergeReports.ts +82 -0
  227. package/start/helpers/reportHomepage.ejs +272 -0
  228. package/start/helpers/runCypressHeadless.ts +164 -0
  229. package/start/helpers/runCypressVisible.ts +45 -0
  230. package/start/index.ts +23 -5
  231. package/start/types/MochawesomeReporterConfig.ts +23 -0
  232. package/start/types/Profile.ts +12 -0
  233. package/start/types/ReportInfo.ts +22 -0
  234. package/start/types/RunResult.ts +18 -0
  235. package/start/types/ScreenshotInfo.ts +13 -0
  236. package/start/types/TemplateReportInfo.ts +16 -0
  237. package/src/commands/visitCanvasGETEndpoint.ts +0 -61
@@ -0,0 +1,64 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /*----------------------------------------*/
4
+ /* ---------------- Type ---------------- */
5
+ /*----------------------------------------*/
6
+
7
+ declare global {
8
+ namespace Cypress {
9
+ interface Chainable {
10
+ /**
11
+ * Wait for any element in a list to be present (check every tenth
12
+ * of a second for status)
13
+ * @author Gardenia Liu
14
+ * @param opts object containing all arguments
15
+ * @param opts.items list of CSS selectors of interest
16
+ * @param [opts.timeoutSec] the number of seconds to wait before
17
+ * timing out
18
+ * @returns the selector of the item that was found first, ties
19
+ * broken by which item shows up in the list first
20
+ */
21
+ waitForAtLeastOneElementPresent(
22
+ opts: {
23
+ items: string[],
24
+ timeoutSec?: number,
25
+ }
26
+ ): Chainable<string>;
27
+ }
28
+ }
29
+ }
30
+
31
+ /*----------------------------------------*/
32
+ /* --------------- Command -------------- */
33
+ /*----------------------------------------*/
34
+
35
+ const waitForAtLeastOneElementPresent = () => {
36
+ Cypress.Commands.add(
37
+ 'waitForAtLeastOneElementPresent',
38
+ (opts: { items: string[], timeoutSec?: number }) => {
39
+ const { items, timeoutSec } = opts;
40
+ // Set timeout seconds to 10 if not specified
41
+ const timeoutMs = (timeoutSec || 10) * 1000;
42
+
43
+ // Combined selector for all items
44
+ const combinedSelector = items.join(', ');
45
+
46
+ // Wait for any of the elements to be visible
47
+ return (
48
+ cy
49
+ .get(combinedSelector, { timeout: timeoutMs })
50
+ .should(($els) => { return expect($els.is(':visible'), `at least one of [${combinedSelector}] visible`).to.be.true; })
51
+ .then(($els) => {
52
+ const $firstVisible = $els.filter(':visible').first();
53
+ return items.find((sel) => { return $firstVisible.is(sel); });
54
+ })
55
+ );
56
+ },
57
+ );
58
+ };
59
+
60
+ /*----------------------------------------*/
61
+ /* --------------- Export --------------- */
62
+ /*----------------------------------------*/
63
+
64
+ export default waitForAtLeastOneElementPresent;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Default number of threads per combo
3
+ * @author Yuen Ler Chow
4
+ */
5
+ const DEFAULT_THREADS_PER_COMBO = 2;
6
+
7
+ export default DEFAULT_THREADS_PER_COMBO;
@@ -0,0 +1,25 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Collect all PNG files recursively from a directory
6
+ * @author Yuen Ler Chow
7
+ * @param dir - Directory to search
8
+ * @param prefix - Path prefix for relative paths
9
+ * @returns Array of relative file paths
10
+ */
11
+ const collectPngFiles = (dir: string, prefix = ''): string[] => {
12
+ const files: string[] = [];
13
+ const dirEntries = fs.readdirSync(dir, { withFileTypes: true });
14
+ dirEntries.forEach((entry) => {
15
+ const fullPath = path.join(dir, entry.name);
16
+ if (entry.isDirectory()) {
17
+ files.push(...collectPngFiles(fullPath, path.join(prefix, entry.name)));
18
+ } else if (entry.isFile() && entry.name.toLowerCase().endsWith('.png')) {
19
+ files.push(path.join(prefix, entry.name));
20
+ }
21
+ });
22
+ return files;
23
+ };
24
+
25
+ export default collectPngFiles;
@@ -0,0 +1,92 @@
1
+ /* eslint-disable no-console */
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ // Import helpers
6
+ import runCypressHeadless from './runCypressHeadless';
7
+ import getDateLabeledDir from './getDateLabeledDir';
8
+ import generateReportHomepage from './generateReportHomepage';
9
+ import mergeAllReportsAndGenerateHtml from './mergeAllReportsAndGenerateHtml';
10
+
11
+ // Import shared types
12
+ import Profile from '../types/Profile';
13
+
14
+ /**
15
+ * Execute all headless Cypress test combinations in parallel
16
+ * @author Yuen Ler Chow
17
+ * @param opts - object of arguments
18
+ * @param opts.selectedProfiles - Array of selected profiles
19
+ * @param opts.selectedBrowsers - Array of selected browsers
20
+ * @param [opts.threadsPerCombo] - Number of parallel threads per combo (default: 2)
21
+ */
22
+ const executeAllHeadlessCombinations = async (
23
+ opts: {
24
+ selectedProfiles: Profile[],
25
+ selectedBrowsers: string[],
26
+ threadsPerCombo?: number,
27
+ },
28
+ ): Promise<void> => {
29
+ const { selectedProfiles, selectedBrowsers, threadsPerCombo } = opts;
30
+
31
+ // Create date-labeled output directory
32
+ const outputDir = getDateLabeledDir();
33
+ if (!fs.existsSync(outputDir)) {
34
+ fs.mkdirSync(outputDir, { recursive: true });
35
+ }
36
+
37
+ console.log(`šŸ“ Results will be saved to: ${outputDir}\n`);
38
+
39
+ // Generate all Profile+Browser combinations
40
+ const combinations: { profileName: string; browser: string }[] = [];
41
+ selectedProfiles.forEach((profile) => {
42
+ selectedBrowsers.forEach((browser) => {
43
+ combinations.push({
44
+ profileName: profile.profileName,
45
+ browser,
46
+ });
47
+ });
48
+ });
49
+
50
+ console.log(`šŸ”„ Running ${combinations.length} test combination(s) in parallel...\n`);
51
+
52
+ // Run all combinations in parallel
53
+ const runPromises = combinations.map((combo) => {
54
+ return runCypressHeadless({
55
+ profileName: combo.profileName,
56
+ browser: combo.browser,
57
+ outputDir,
58
+ numThreads: threadsPerCombo,
59
+ });
60
+ });
61
+ const results = await Promise.all(runPromises);
62
+
63
+ // Generate combined HTML report across all profile+browser combinations
64
+ mergeAllReportsAndGenerateHtml(outputDir, results);
65
+
66
+ // Generate report homepage
67
+ const reportInfos = results.map((result) => {
68
+ return {
69
+ profileName: result.profileName,
70
+ browser: result.browser,
71
+ reportPath: result.reportPath || '',
72
+ success: result.success,
73
+ timestamp: new Date().toLocaleString(),
74
+ };
75
+ });
76
+
77
+ generateReportHomepage(reportInfos, outputDir);
78
+
79
+ // Print summary
80
+ const passed = results.filter((r) => { return r.success; }).length;
81
+ const failed = results.filter((r) => { return !r.success; }).length;
82
+
83
+ console.log('\n═══════════════════════════════════════════════════════════');
84
+ console.log('šŸ“Š Execution Summary:');
85
+ console.log(` Total: ${results.length}`);
86
+ console.log(` āœ… Passed: ${passed}`);
87
+ console.log(` āŒ Failed: ${failed}`);
88
+ console.log(` šŸ“„ Report homepage: file://${path.join(outputDir, 'index.html')}`);
89
+ console.log('═══════════════════════════════════════════════════════════\n');
90
+ };
91
+
92
+ export default executeAllHeadlessCombinations;
@@ -0,0 +1,66 @@
1
+ /* eslint-disable no-console */
2
+
3
+ // Import shared types
4
+ import Profile from '../types/Profile';
5
+
6
+ // Import helpers
7
+ import runCypressVisible from './runCypressVisible';
8
+ import executeAllHeadlessCombinations from './executeAllHeadlessCombinations';
9
+
10
+ // Import constants
11
+ import DEFAULT_THREADS_PER_COMBO from '../constants/DEFAULT_THREADS_PER_COMBO';
12
+
13
+ /**
14
+ * Execute Cypress tests in either headless or visible mode
15
+ * @author Yuen Ler Chow
16
+ * @param opts - object of arguments
17
+ * @param opts.selectedProfiles - Array of selected profiles
18
+ * @param opts.selectedBrowsers - Array of selected browsers
19
+ * @param opts.isHeadless - Whether to run in headless mode
20
+ * @param [opts.threadsPerCombo] - Number of parallel threads per combo (default: 2)
21
+ */
22
+ const executeCypress = async (
23
+ opts: {
24
+ selectedProfiles: Profile[],
25
+ selectedBrowsers: string[],
26
+ isHeadless: boolean,
27
+ threadsPerCombo?: number,
28
+ },
29
+ ): Promise<void> => {
30
+ const {
31
+ selectedProfiles,
32
+ selectedBrowsers,
33
+ isHeadless,
34
+ threadsPerCombo,
35
+ } = opts;
36
+
37
+ console.log('\n═══════════════════════════════════════════════════════════');
38
+ console.log('Starting Cypress with the following configuration:');
39
+ console.log(`Profile(s): ${selectedProfiles.map((p) => { return p.profileName; }).join(', ')}`);
40
+ console.log(`Browser(s): ${selectedBrowsers.join(', ')}`);
41
+ console.log(`Headless: ${isHeadless}`);
42
+ if (isHeadless) {
43
+ console.log(`Threads per combo: ${threadsPerCombo ?? DEFAULT_THREADS_PER_COMBO}`);
44
+ }
45
+ console.log('═══════════════════════════════════════════════════════════\n');
46
+
47
+ try {
48
+ if (!isHeadless) {
49
+ // Visible mode: run with single profile
50
+ const { profileName } = selectedProfiles[0];
51
+ await runCypressVisible(profileName);
52
+ } else {
53
+ // Headless mode: run parallel instances for each Profile+Browser combination
54
+ await executeAllHeadlessCombinations({
55
+ selectedProfiles,
56
+ selectedBrowsers,
57
+ threadsPerCombo,
58
+ });
59
+ }
60
+ } catch (error) {
61
+ console.error('\nāŒ Error during execution:', error);
62
+ process.exit(1);
63
+ }
64
+ };
65
+
66
+ export default executeCypress;
@@ -0,0 +1,71 @@
1
+ /* eslint-disable no-console */
2
+ import { execSync } from 'child_process';
3
+ import path from 'path';
4
+ import { existsSync } from 'fs';
5
+ import getRootPath from './getRootPath';
6
+
7
+ /**
8
+ * Generate HTML report from merged JSON report using mochawesome-report-generator (marge)
9
+ * @author Yuen Ler Chow
10
+ * @param opts - object of arguments
11
+ * @param opts.resultsDir - Results directory
12
+ * @param opts.profileName - Profile name
13
+ * @param opts.browserName - Browser name
14
+ */
15
+ const generateHtmlReport = (
16
+ opts: {
17
+ resultsDir: string;
18
+ profileName: string;
19
+ browserName: string;
20
+ },
21
+ ): void => {
22
+ const { resultsDir, profileName, browserName } = opts;
23
+
24
+ const reportDataPath = path.join(
25
+ resultsDir,
26
+ `${profileName}-${browserName}`,
27
+ 'report-data.json',
28
+ );
29
+ const reportDir = path.join(
30
+ resultsDir,
31
+ `${profileName}-${browserName}`,
32
+ 'report',
33
+ );
34
+ const expectedHtmlPath = path.join(reportDir, 'report-data.html');
35
+
36
+ console.log(`\nšŸ“„ Generate HTML Report Debug for ${profileName} + ${browserName}:`);
37
+ console.log(` Report data path: ${reportDataPath}`);
38
+ console.log(` Report output dir: ${reportDir}`);
39
+
40
+ if (!existsSync(reportDataPath)) {
41
+ const errorMsg = `āŒ ERROR: Report data JSON does not exist: ${reportDataPath}`;
42
+ console.error(errorMsg);
43
+ throw new Error(errorMsg);
44
+ }
45
+
46
+ try {
47
+ const command = `npx marge "${reportDataPath}" --reportDir "${reportDir}"`;
48
+
49
+ console.log(` Running: ${command}`);
50
+ console.log(`Generating HTML report for ${profileName} + ${browserName}...`);
51
+
52
+ execSync(command, {
53
+ stdio: 'inherit',
54
+ cwd: getRootPath(),
55
+ });
56
+
57
+ // Verify the HTML report was created
58
+ if (!existsSync(expectedHtmlPath)) {
59
+ const errorMsg = `āŒ ERROR: HTML report was not created at: ${expectedHtmlPath}`;
60
+ console.error(errorMsg);
61
+ throw new Error(errorMsg);
62
+ }
63
+
64
+ console.log(` āœ… HTML report generated successfully at: ${expectedHtmlPath}`);
65
+ } catch (e) {
66
+ console.error(`Error generating HTML report for ${profileName} + ${browserName}:`, e);
67
+ throw e;
68
+ }
69
+ };
70
+
71
+ export default generateHtmlReport;
@@ -0,0 +1,148 @@
1
+ /* eslint-disable no-console */
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import ejs from 'ejs';
5
+
6
+ // Import helpers
7
+ import collectPngFiles from './collectPngFiles';
8
+
9
+ // Import types
10
+ import ReportInfo from '../types/ReportInfo';
11
+ import ScreenshotInfo from '../types/ScreenshotInfo';
12
+ import TemplateReportInfo from '../types/TemplateReportInfo';
13
+
14
+ /**
15
+ * Generate an HTML homepage that links to all reports
16
+ * @author Yuen Ler Chow
17
+ * @param reports - Array of report information
18
+ * @param outputDir - The directory where the homepage should be created
19
+ */
20
+ const generateReportHomepage = (
21
+ reports: ReportInfo[],
22
+ outputDir: string,
23
+ ): void => {
24
+ const homepagePath = path.join(outputDir, 'index.html');
25
+ const combinedReportPath = path.join(outputDir, 'all-runs.html');
26
+ const combinedJsonPath = path.join(outputDir, 'all-report-data.json');
27
+
28
+ const hasCombinedReport = fs.existsSync(combinedReportPath);
29
+
30
+ // Try to read combined stats from all-report-data.json
31
+ let combinedTotalTests: number | null = null;
32
+ let combinedPassedTests: number | null = null;
33
+ if (fs.existsSync(combinedJsonPath)) {
34
+ try {
35
+ const raw = fs.readFileSync(combinedJsonPath, 'utf-8');
36
+ const data = JSON.parse(raw);
37
+ const stats = (data && data.stats) || null;
38
+ if (stats && typeof stats.tests === 'number' && typeof stats.passes === 'number') {
39
+ combinedTotalTests = stats.tests;
40
+ combinedPassedTests = stats.passes;
41
+ }
42
+ } catch (err) {
43
+ // File exists but couldn't be read/parsed - log warning
44
+ console.warn('āš ļø Could not read combined stats:', err);
45
+ }
46
+ }
47
+
48
+ // Try to read per-combination stats from each combo's report-data.json
49
+ reports.forEach((report) => {
50
+ const comboJsonPath = path.join(
51
+ outputDir,
52
+ `${report.profileName}-${report.browser}`,
53
+ 'report-data.json',
54
+ );
55
+ if (!fs.existsSync(comboJsonPath)) {
56
+ return;
57
+ }
58
+ try {
59
+ const raw = fs.readFileSync(comboJsonPath, 'utf-8');
60
+ const data = JSON.parse(raw);
61
+ const stats = (data && data.stats) || null;
62
+ if (stats && typeof stats.tests === 'number' && typeof stats.passes === 'number') {
63
+ // eslint-disable-next-line no-param-reassign
64
+ report.totalTests = stats.tests;
65
+ // eslint-disable-next-line no-param-reassign
66
+ report.passedTests = stats.passes;
67
+ }
68
+ } catch (err) {
69
+ // File exists but couldn't be read/parsed - log warning
70
+ console.warn(`āš ļø Could not read stats for ${report.profileName}-${report.browser}:`, err);
71
+ }
72
+ });
73
+
74
+ // Sort reports by profile name, then browser
75
+ const sortedReports = [...reports].sort((a, b) => {
76
+ if (a.profileName !== b.profileName) {
77
+ return a.profileName.localeCompare(b.profileName);
78
+ }
79
+ return a.browser.localeCompare(b.browser);
80
+ });
81
+
82
+ // Prepare reports with computed fields for the template
83
+ const templateReports: TemplateReportInfo[] = sortedReports.map((report) => {
84
+ // Compute relative report path
85
+ let relativeReportPath: string | null = null;
86
+ if (report.reportPath && fs.existsSync(report.reportPath)) {
87
+ relativeReportPath = path.relative(outputDir, report.reportPath).replace(/\\/g, '/');
88
+ }
89
+
90
+ // Collect screenshots
91
+ const screenshots: ScreenshotInfo[] = [];
92
+ const comboScreenshotsDir = path.join(
93
+ outputDir,
94
+ `${report.profileName}-${report.browser}`,
95
+ 'screenshots',
96
+ );
97
+ if (fs.existsSync(comboScreenshotsDir)) {
98
+ const pngFiles = collectPngFiles(comboScreenshotsDir);
99
+ pngFiles.forEach((rel) => {
100
+ const href = path
101
+ .relative(outputDir, path.join(comboScreenshotsDir, rel))
102
+ .replace(/\\/g, '/');
103
+ screenshots.push({ href, name: rel });
104
+ });
105
+ }
106
+
107
+ return {
108
+ ...report,
109
+ relativeReportPath,
110
+ screenshots,
111
+ };
112
+ });
113
+
114
+ // Group by profile
115
+ const reportsByProfile: { [key: string]: TemplateReportInfo[] } = {};
116
+ templateReports.forEach((report) => {
117
+ if (!reportsByProfile[report.profileName]) {
118
+ reportsByProfile[report.profileName] = [];
119
+ }
120
+ reportsByProfile[report.profileName].push(report);
121
+ });
122
+
123
+ // Calculate summary stats
124
+ const totalRuns = reports.length;
125
+ const passedRuns = reports.filter((r) => { return r.success; }).length;
126
+ const failedRuns = reports.filter((r) => { return !r.success; }).length;
127
+ const profileCount = Object.keys(reportsByProfile).length;
128
+
129
+ // Load and render EJS template
130
+ const templatePath = path.join(__dirname, 'reportHomepage.ejs');
131
+ const templateFile = fs.readFileSync(templatePath, 'utf-8');
132
+ const html = ejs.render(templateFile, {
133
+ hasCombinedReport,
134
+ combinedTotalTests,
135
+ combinedPassedTests,
136
+ totalRuns,
137
+ passedRuns,
138
+ failedRuns,
139
+ profileCount,
140
+ reportsByProfile,
141
+ });
142
+
143
+ // Write the HTML file
144
+ fs.writeFileSync(homepagePath, html, 'utf-8');
145
+ console.log(`\nšŸ“„ Report homepage generated: ${homepagePath}`);
146
+ };
147
+
148
+ export default generateReportHomepage;
@@ -0,0 +1,39 @@
1
+ import path from 'path';
2
+ import MochawesomeReporterConfig from '../types/MochawesomeReporterConfig';
3
+
4
+ /**
5
+ * Generate inline reporter configuration for cypress-multi-reporters
6
+ * that produces granular mochawesome JSON per spec
7
+ * @author Yuen Ler Chow
8
+ * @param opts - object of arguments
9
+ * @param opts.resultsDir - Base directory for results
10
+ * @param opts.profileName - Profile name
11
+ * @param opts.browserName - Browser name
12
+ * @returns Reporter config object
13
+ */
14
+ const generateReporterConfig = (opts: {
15
+ resultsDir: string;
16
+ profileName: string;
17
+ browserName: string;
18
+ }): MochawesomeReporterConfig => {
19
+ const { resultsDir, profileName, browserName } = opts;
20
+
21
+ const granularResultsDir = path.join(
22
+ resultsDir,
23
+ `${profileName}-${browserName}`,
24
+ 'granular-results',
25
+ );
26
+
27
+ return {
28
+ reporterEnabled: 'mochawesome',
29
+ mochawesomeReporterOptions: {
30
+ reportDir: granularResultsDir,
31
+ reportFilename: 'test-[name]',
32
+ overwrite: false,
33
+ html: false,
34
+ json: true,
35
+ },
36
+ };
37
+ };
38
+
39
+ export default generateReporterConfig;
@@ -0,0 +1,43 @@
1
+ import path from 'path';
2
+ import getRootPath from './getRootPath';
3
+
4
+ /**
5
+ * Get a date-labeled directory path for results using Eastern Time
6
+ * @author Yuen Ler Chow
7
+ * @returns path to date-labeled directory (e.g., cypress-results/2024-1-15_3-05pm)
8
+ */
9
+ const getDateLabeledDir = (): string => {
10
+ const root = getRootPath();
11
+ const now = new Date();
12
+
13
+ // Format date in Eastern Time
14
+ const str = now.toLocaleString(
15
+ 'en-US', // Using US encoding (it's the only one installed on containers)
16
+ { timeZone: 'America/New_York' }, // Force EST timezone
17
+ );
18
+
19
+ // Parse the string for the date/time info
20
+ const [dateStr, timeStr] = str.split(', '); // Format: MM/DD/YYYY, HH:MM:SS AM
21
+ const [monthStr, dayStr, yearStr] = dateStr.split('/'); // Format: MM/DD/YYYY
22
+ const [hourStr, minStr, ending] = timeStr.split(':'); // Format: HH:MM:SS AM
23
+
24
+ // Create all time numbers
25
+ const year = Number.parseInt(yearStr, 10);
26
+ const month = Number.parseInt(monthStr, 10);
27
+ const day = Number.parseInt(dayStr, 10);
28
+ const minute = Number.parseInt(minStr, 10);
29
+ const hour12 = Number.parseInt(hourStr, 10);
30
+
31
+ // Determine AM/PM
32
+ const isAM = ending.toLowerCase().includes('am');
33
+
34
+ // Pad minute with leading zero if needed
35
+ const minutePadded = minute.toString().padStart(2, '0');
36
+
37
+ // Build formatted date string: YYYY-M-D_H-MMam/pm (no spaces for shell compatibility)
38
+ const formattedDate = `${year}-${month}-${day}_${hour12}-${minutePadded}${isAM ? 'am' : 'pm'}`;
39
+
40
+ return path.join(root, 'cypress-results', formattedDate);
41
+ };
42
+
43
+ export default getDateLabeledDir;