dceky 1.0.5-beta.ky-declarations.12 ā 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/setup/genCommandImportFile.js +5 -0
- package/lib/setup/genCommandImportFile.js.map +1 -1
- package/lib/setup/genE2ELaunchFile.js +5 -0
- package/lib/setup/genE2ELaunchFile.js.map +1 -1
- 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/setup/genCommandImportFile.ts +6 -0
- package/setup/genE2ELaunchFile.ts +6 -0
- 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,47 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Get the labels of all the options within a select element
|
|
12
|
+
* @author Allison Zhang
|
|
13
|
+
* @param selector the CSS selector of the select element
|
|
14
|
+
* @returns an array of strings representing the labels of the options
|
|
15
|
+
*/
|
|
16
|
+
listSelectLabels(
|
|
17
|
+
selector: string,
|
|
18
|
+
): Chainable<String[]>;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/*----------------------------------------*/
|
|
24
|
+
/* --------------- Command -------------- */
|
|
25
|
+
/*----------------------------------------*/
|
|
26
|
+
|
|
27
|
+
const listSelectLabels = () => {
|
|
28
|
+
Cypress.Commands.add('listSelectLabels', (selector: string) => {
|
|
29
|
+
const labels: string[] = [];
|
|
30
|
+
return (
|
|
31
|
+
cy
|
|
32
|
+
.get(`${selector} option`)
|
|
33
|
+
.each((option) => {
|
|
34
|
+
labels.push(option.attr('label') || option.text());
|
|
35
|
+
})
|
|
36
|
+
.then(() => {
|
|
37
|
+
return labels;
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/*----------------------------------------*/
|
|
44
|
+
/* --------------- Export --------------- */
|
|
45
|
+
/*----------------------------------------*/
|
|
46
|
+
|
|
47
|
+
export default listSelectLabels;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Get the values of all the options within a select element
|
|
12
|
+
* @author Allison Zhang
|
|
13
|
+
* @param selector the CSS selector of the select element
|
|
14
|
+
* @returns an array of strings representing the values of the options
|
|
15
|
+
*/
|
|
16
|
+
listSelectValues(
|
|
17
|
+
selector: string,
|
|
18
|
+
): Chainable<String[]>;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/*----------------------------------------*/
|
|
24
|
+
/* --------------- Command -------------- */
|
|
25
|
+
/*----------------------------------------*/
|
|
26
|
+
|
|
27
|
+
const listSelectValues = () => {
|
|
28
|
+
Cypress.Commands.add('listSelectValues', (selector: string) => {
|
|
29
|
+
const vals: string[] = [];
|
|
30
|
+
return (
|
|
31
|
+
cy
|
|
32
|
+
.get(`${selector} option`)
|
|
33
|
+
.each((option) => {
|
|
34
|
+
cy.wrap(option).invoke('attr', 'value')
|
|
35
|
+
.then((value) => {
|
|
36
|
+
vals.push(value || option.text());
|
|
37
|
+
});
|
|
38
|
+
})
|
|
39
|
+
.then(() => {
|
|
40
|
+
return vals;
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/*----------------------------------------*/
|
|
47
|
+
/* --------------- Export --------------- */
|
|
48
|
+
/*----------------------------------------*/
|
|
49
|
+
|
|
50
|
+
export default listSelectValues;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Pads a number with leading zeros to a user-defined length.
|
|
12
|
+
* @author Allison Zhang
|
|
13
|
+
* @param opts options object
|
|
14
|
+
* @param opts.length the desired total length of the output string, must be larger than the length of the number
|
|
15
|
+
* @param opts.number the number to be padded with leading zeros
|
|
16
|
+
* @returns padded string with leading zeros
|
|
17
|
+
*/
|
|
18
|
+
padWithZeros(opts: {
|
|
19
|
+
length: number,
|
|
20
|
+
number: number,
|
|
21
|
+
}): Chainable<string>;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/*----------------------------------------*/
|
|
27
|
+
/* --------------- Command -------------- */
|
|
28
|
+
/*----------------------------------------*/
|
|
29
|
+
|
|
30
|
+
const padWithZeros = () => {
|
|
31
|
+
Cypress.Commands.add('padWithZeros', (opts: { length: number, number: number }) => {
|
|
32
|
+
const {
|
|
33
|
+
length,
|
|
34
|
+
number,
|
|
35
|
+
} = opts;
|
|
36
|
+
return (
|
|
37
|
+
cy
|
|
38
|
+
.then(() => {
|
|
39
|
+
let numberString = number.toString();
|
|
40
|
+
while (numberString.length < length) {
|
|
41
|
+
numberString = `0${numberString}`;
|
|
42
|
+
}
|
|
43
|
+
return numberString;
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/*----------------------------------------*/
|
|
50
|
+
/* --------------- Export --------------- */
|
|
51
|
+
/*----------------------------------------*/
|
|
52
|
+
|
|
53
|
+
export default padWithZeros;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Makes a string unique by appending a timestamp and a counter.
|
|
12
|
+
* @author Allison Zhang
|
|
13
|
+
* @param text the original string
|
|
14
|
+
* @returns the uniquified string
|
|
15
|
+
*/
|
|
16
|
+
uniquify(text: string): string;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/*----------------------------------------*/
|
|
22
|
+
/* --------------- Helpers -------------- */
|
|
23
|
+
/*----------------------------------------*/
|
|
24
|
+
|
|
25
|
+
let uniqueCounter = 1;
|
|
26
|
+
|
|
27
|
+
const shorten = (num: number): string => {
|
|
28
|
+
return Number(num).toString(36);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const id = shorten(Math.floor(Math.random() * 10000));
|
|
32
|
+
|
|
33
|
+
/*----------------------------------------*/
|
|
34
|
+
/* --------------- Command -------------- */
|
|
35
|
+
/*----------------------------------------*/
|
|
36
|
+
|
|
37
|
+
const uniquify = () => {
|
|
38
|
+
Cypress.Commands.add('uniquify', (text: string) => {
|
|
39
|
+
const thisCounter = uniqueCounter;
|
|
40
|
+
uniqueCounter += 1;
|
|
41
|
+
return `${text} [${shorten(Number.parseInt(`${Date.now()}${thisCounter}${id}`, 10))}]`;
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/*----------------------------------------*/
|
|
46
|
+
/* --------------- Export --------------- */
|
|
47
|
+
/*----------------------------------------*/
|
|
48
|
+
|
|
49
|
+
export default uniquify;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/*----------------------------------------*/
|
|
4
|
+
/* ---------------- Type ---------------- */
|
|
5
|
+
/*----------------------------------------*/
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Cypress {
|
|
9
|
+
interface Chainable {
|
|
10
|
+
/**
|
|
11
|
+
* Send a request to a Canvas API endpoint.
|
|
12
|
+
* @author Allison Zhang
|
|
13
|
+
* @param opts options object
|
|
14
|
+
* @param opts.method the HTTP method (e.g., 'GET', 'POST', 'DELETE')
|
|
15
|
+
* @param opts.path the API endpoint path excluding 'https://canvas.harvard.edu', (e.g., '/api/v1/courses/123')
|
|
16
|
+
* @param opts.accessToken the access token for authentication
|
|
17
|
+
* @param opts.params the request params (if any)
|
|
18
|
+
* @returns Cypress chainable containing the Canvas API response body
|
|
19
|
+
*/
|
|
20
|
+
visitCanvasEndpoint(
|
|
21
|
+
opts: {
|
|
22
|
+
method: string,
|
|
23
|
+
path: string,
|
|
24
|
+
accessToken: string,
|
|
25
|
+
params?: { [k: string]: any },
|
|
26
|
+
},
|
|
27
|
+
): Chainable<any>;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/*----------------------------------------*/
|
|
33
|
+
/* --------------- Command -------------- */
|
|
34
|
+
/*----------------------------------------*/
|
|
35
|
+
|
|
36
|
+
const visitCanvasEndpoint = () => {
|
|
37
|
+
Cypress.Commands.add('visitCanvasEndpoint', (
|
|
38
|
+
opts: {
|
|
39
|
+
method: string,
|
|
40
|
+
path: string,
|
|
41
|
+
accessToken: string,
|
|
42
|
+
params: {
|
|
43
|
+
[k: string]: any
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
) => {
|
|
47
|
+
const {
|
|
48
|
+
method,
|
|
49
|
+
path,
|
|
50
|
+
accessToken,
|
|
51
|
+
params,
|
|
52
|
+
} = opts;
|
|
53
|
+
cy.log(`Visiting Canvas ${method} endpoint`);
|
|
54
|
+
cy.log(`Canvas API: ${path}`);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
cy
|
|
58
|
+
.request({
|
|
59
|
+
method,
|
|
60
|
+
url: `https://canvas.harvard.edu${path}`,
|
|
61
|
+
body: {
|
|
62
|
+
...(params || {}),
|
|
63
|
+
access_token: accessToken,
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
.its('body')
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/*----------------------------------------*/
|
|
72
|
+
/* --------------- Export --------------- */
|
|
73
|
+
/*----------------------------------------*/
|
|
74
|
+
|
|
75
|
+
export default visitCanvasEndpoint;
|
|
@@ -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,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;
|