@spscommerce/e2e-core 1.0.4 → 1.1.2
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/.dockerignore +1 -1
- package/Dockerfile +2 -1
- package/README.md +1 -1
- package/helpers/apiCalls.ts +83 -0
- package/helpers/authSetup.ts +22 -3
- package/helpers/loggerSetup.ts +2 -2
- package/jest-environment.config.js +8 -8
- package/jest-playwright.config.js +18 -9
- package/jest.auth.js +2 -33
- package/jest.config.js +2 -1
- package/package.json +4 -2
package/.dockerignore
CHANGED
package/Dockerfile
CHANGED
|
@@ -3,7 +3,8 @@ FROM mcr.microsoft.com/playwright:bionic
|
|
|
3
3
|
# We need to install Edge manually since it does not exist in the official PW image
|
|
4
4
|
# https://github.com/microsoft/playwright/blob/07fb81a4efdcc4c2169a61a16b513b9d91be6c49/src/server/chromium/findChromiumChannel.ts#L38
|
|
5
5
|
|
|
6
|
-
RUN apt-get
|
|
6
|
+
RUN apt-get update \
|
|
7
|
+
&& apt-get install --reinstall wget \
|
|
7
8
|
&& wget -q -O - https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \
|
|
8
9
|
&& echo "deb https://packages.microsoft.com/repos/edge stable main" >> /etc/apt/sources.list.d/microsoft-edge.list \
|
|
9
10
|
&& apt-get update -qqy \
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ HEADLESS=false BROWSER=chromium npm run test
|
|
|
39
39
|
| ------------------: | -------------------------------------------------- | ------------------------------ | ------------------------------------ |
|
|
40
40
|
| HEADLESS | launch browser in headless/headful mode | true | true, false |
|
|
41
41
|
| BROWSER | use + as a separator, e.g edge+firefox | chromium | chromium, firefox, webkit, edge |
|
|
42
|
-
|
|
|
42
|
+
| URL | auth state is invoked for the defined env | test | test, prod etc. |
|
|
43
43
|
| SLOWMO | slow down an execution for debugging | 0 | [number_in_ms] |
|
|
44
44
|
| TIMEOUT | global timeout for each test | 30000 | [number_in_ms] |
|
|
45
45
|
| WORKERS | number of workers (test threads) | number of CPUs minus 1 | [number] |
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
|
|
3
|
+
let authEndpoint = 'https://test.id.spsc.io/';
|
|
4
|
+
|
|
5
|
+
if (process.env.URL && process.env.URL.toLowerCase().includes('prod')) {
|
|
6
|
+
authEndpoint = 'https://id.spsc.io/';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function getAuthToken(): Promise<string> {
|
|
10
|
+
if (process.env.USER_EMAIL && process.env.USER_PASSWORD) {
|
|
11
|
+
const requestOptions = {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
body: JSON.stringify({
|
|
14
|
+
username: process.env.USER_EMAIL,
|
|
15
|
+
password: process.env.USER_PASSWORD,
|
|
16
|
+
client_id: "595",
|
|
17
|
+
grant_type: "password"
|
|
18
|
+
}),
|
|
19
|
+
headers: {
|
|
20
|
+
'Accept': 'application/json',
|
|
21
|
+
'Content-Type': 'application/json'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const fetchResponse = await fetch(authEndpoint + 'identity/token/', requestOptions);
|
|
26
|
+
const data = await fetchResponse.json();
|
|
27
|
+
return data.access_token;
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.log(e);
|
|
30
|
+
return process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
throw new Error(`Set up process environment variables: USER_EMAIL and USER_PASSWORD.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function getUserInfo(): Promise<any> {
|
|
38
|
+
const requestOptions = {
|
|
39
|
+
method: 'GET',
|
|
40
|
+
headers: {
|
|
41
|
+
'Authorization': `Bearer ${process.env.TOKEN}`,
|
|
42
|
+
'Accept': 'application/json',
|
|
43
|
+
'Content-Type': 'application/json'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const fetchResponse = await fetch(authEndpoint + `identity/users/me/`, requestOptions);
|
|
48
|
+
const data = await fetchResponse.json();
|
|
49
|
+
return data;
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.log(e);
|
|
52
|
+
return process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function changePreferredOrg(orgID: string): Promise<void> {
|
|
57
|
+
const userData = await getUserInfo();
|
|
58
|
+
const requestOptions = {
|
|
59
|
+
method: 'PUT',
|
|
60
|
+
body: JSON.stringify({
|
|
61
|
+
email: userData.email,
|
|
62
|
+
first_name: userData.first_name,
|
|
63
|
+
last_name: userData.last_name,
|
|
64
|
+
preferred_org_id: orgID,
|
|
65
|
+
organization: {
|
|
66
|
+
id: orgID
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
headers: {
|
|
70
|
+
'Authorization': `Bearer ${process.env.TOKEN}`,
|
|
71
|
+
'Accept': 'application/json',
|
|
72
|
+
'Content-Type': 'application/json'
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const fetchResponse = await fetch(authEndpoint + `identity/users/${userData.id}/`, requestOptions);
|
|
77
|
+
const data = await fetchResponse.json();
|
|
78
|
+
return data;
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.log(e);
|
|
81
|
+
return process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
package/helpers/authSetup.ts
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { changePreferredOrg, getAuthToken, getUserInfo } from '../helpers/apiCalls';
|
|
2
|
+
|
|
3
|
+
export async function authenticate(orgID: string): Promise<void> {
|
|
4
|
+
const testSuiteName = expect.getState().testPath.split('/').pop().split('.').reverse()[2];
|
|
5
|
+
process.env.TOKEN = await getAuthToken();
|
|
6
|
+
let userData = await getUserInfo();
|
|
7
|
+
console.log(`${testSuiteName} suite authentication flow:
|
|
8
|
+
Current preferred org: ${userData.preferred_org_id}.
|
|
9
|
+
Current org: ${userData.organization.organization_name}.`);
|
|
10
|
+
while (userData.preferred_org_id !== orgID || userData.organization.id !== orgID) {
|
|
11
|
+
await changePreferredOrg(orgID);
|
|
12
|
+
process.env.TOKEN = await getAuthToken();
|
|
13
|
+
userData = await getUserInfo();
|
|
14
|
+
console.log(`${testSuiteName} suite authentication flow:
|
|
15
|
+
Preferred org was changed to: ${userData.preferred_org_id}.
|
|
16
|
+
Current org was changed to: ${userData.organization.organization_name}.`);
|
|
17
|
+
}
|
|
18
|
+
console.log(process.env.TOKEN);
|
|
19
|
+
await page.addInitScript(params => {
|
|
3
20
|
window.localStorage.setItem(params.name, params.value);
|
|
4
|
-
}, { name: "access_token", value:
|
|
21
|
+
}, { name: "access_token", value: process.env.TOKEN });
|
|
5
22
|
return;
|
|
6
23
|
}
|
|
7
24
|
|
|
@@ -9,5 +26,7 @@ export async function unauthenticate(): Promise<void> {
|
|
|
9
26
|
await context.clearCookies();
|
|
10
27
|
await context.addInitScript(() => window.localStorage.clear());
|
|
11
28
|
await context.addInitScript(() => window.sessionStorage.clear());
|
|
29
|
+
await page.goto(process.env.BASEURL);
|
|
30
|
+
await page.waitForURL(/https:\/\/auth.*/);
|
|
12
31
|
return;
|
|
13
32
|
}
|
package/helpers/loggerSetup.ts
CHANGED
|
@@ -13,7 +13,7 @@ export async function enableTracer(): Promise<void> {
|
|
|
13
13
|
|
|
14
14
|
export async function disableTracer(): Promise<void> {
|
|
15
15
|
const testName = expect.getState().currentTestName.replace(/ /g, "");
|
|
16
|
-
const tracePathPerSuite = path.join(process.cwd(), 'results/traces');
|
|
16
|
+
const tracePathPerSuite = path.join(process.cwd(), 'results-e2e/traces');
|
|
17
17
|
|
|
18
18
|
// Stop tracing and write it to file per test
|
|
19
19
|
try {
|
|
@@ -28,7 +28,7 @@ export async function disableTracer(): Promise<void> {
|
|
|
28
28
|
|
|
29
29
|
export async function enableLogger(): Promise<void> {
|
|
30
30
|
const testName = expect.getState().currentTestName.replace(/ /g, "");
|
|
31
|
-
const logPathPerSuite = path.join(process.cwd(), 'results/logs');
|
|
31
|
+
const logPathPerSuite = path.join(process.cwd(), 'results-e2e/logs');
|
|
32
32
|
|
|
33
33
|
// Create folder per suite for error files
|
|
34
34
|
await fs.mkdirSync(logPathPerSuite, { recursive: true });
|
|
@@ -18,7 +18,6 @@ class JestEnvironmentFailFast extends ParentEnvironment {
|
|
|
18
18
|
await super.teardown();
|
|
19
19
|
if (this.videos.length !== 0) {
|
|
20
20
|
const { recordVideo } = pwConfig.contextOptions;
|
|
21
|
-
console.log(this.videos)
|
|
22
21
|
const anyTestFailed = this.videos.find(element => element.success !== true);
|
|
23
22
|
if (anyTestFailed) {
|
|
24
23
|
const newVideoName = `${recordVideo.dir}/${anyTestFailed.testSuiteName}.webm`;
|
|
@@ -33,12 +32,13 @@ class JestEnvironmentFailFast extends ParentEnvironment {
|
|
|
33
32
|
// https://github.com/facebook/jest/issues/10059
|
|
34
33
|
// https://github.com/facebook/jest/blob/main/packages/jest-circus/src/eventHandler.ts
|
|
35
34
|
async handleTestEvent(event, state) {
|
|
36
|
-
|
|
35
|
+
await super.handleTestEvent(event);
|
|
36
|
+
|
|
37
37
|
// TEST STARTED
|
|
38
38
|
// Delete already existing screenshot/trace file before test starts if it ran twice due to jest.retryTimes()
|
|
39
39
|
if (event.name === 'test_start') {
|
|
40
|
-
const traceFilePerTest = path.join(process.cwd(), `results/traces/${event.test.parent.name}${event.test.name}.zip`);
|
|
41
|
-
const screenFilePerTest = path.join(process.cwd(), `results/screenshots/${event.test.parent.name}${event.test.name}.png`);
|
|
40
|
+
const traceFilePerTest = path.join(process.cwd(), `results-e2e/traces/${event.test.parent.name}${event.test.name}.zip`);
|
|
41
|
+
const screenFilePerTest = path.join(process.cwd(), `results-e2e/screenshots/${event.test.parent.name}${event.test.name}.png`);
|
|
42
42
|
if (fs.existsSync(traceFilePerTest)) {
|
|
43
43
|
fs.unlinkSync(traceFilePerTest);
|
|
44
44
|
}
|
|
@@ -55,7 +55,7 @@ class JestEnvironmentFailFast extends ParentEnvironment {
|
|
|
55
55
|
if (state.currentlyRunningTest) {
|
|
56
56
|
await this.global.page.screenshot({
|
|
57
57
|
fullPage: true,
|
|
58
|
-
path: process.cwd() + `/results/screenshots/${event.test.parent.name}${event.test.name}.png`,
|
|
58
|
+
path: process.cwd() + `/results-e2e/screenshots/${event.test.parent.name}${event.test.name}.png`,
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -63,7 +63,7 @@ class JestEnvironmentFailFast extends ParentEnvironment {
|
|
|
63
63
|
// TEST FINISHED SUCCESSFULLY
|
|
64
64
|
// Delete trace file for each succeeded test
|
|
65
65
|
if (event.name === 'test_done' && event.test.errors.length === 0) {
|
|
66
|
-
const traceFilePerTest = path.join(process.cwd(), `results/traces/${event.test.parent.name}${event.test.name}.zip`);
|
|
66
|
+
const traceFilePerTest = path.join(process.cwd(), `results-e2e/traces/${event.test.parent.name}${event.test.name}.zip`);
|
|
67
67
|
if (fs.existsSync(traceFilePerTest)) {
|
|
68
68
|
fs.unlinkSync(traceFilePerTest);
|
|
69
69
|
}
|
|
@@ -81,8 +81,8 @@ class JestEnvironmentFailFast extends ParentEnvironment {
|
|
|
81
81
|
|
|
82
82
|
// TEST FINISHES AND FAILS
|
|
83
83
|
// Skip all further tests in the suite (all tests are related to each other) if
|
|
84
|
-
// - any test is failed AND suite name includes '
|
|
85
|
-
if (this.failedTest && event.name === 'test_start' && event.test.parent.name.includes('
|
|
84
|
+
// - any test is failed AND suite name includes 'FailFast'
|
|
85
|
+
if (this.failedTest && event.name === 'test_start' && event.test.parent.name.includes('FailFast')) {
|
|
86
86
|
event.test.mode = 'skip';
|
|
87
87
|
}
|
|
88
88
|
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
const customViewport = {
|
|
2
|
+
width: 1920,
|
|
3
|
+
height: 1080,
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
let runModeBrowser = [];
|
|
2
7
|
|
|
3
8
|
// Run in chromium browser by default (if process.env.BROWSER is not specified)
|
|
@@ -19,13 +24,10 @@ if (!process.env.BROWSER) {
|
|
|
19
24
|
case 'webkit':
|
|
20
25
|
runModeBrowser.push('webkit');
|
|
21
26
|
break;
|
|
22
|
-
// Pay attention: browserName is 'chromium' for Edge browser (mentioned in describe)
|
|
23
|
-
// https://support.microsoft.com/en-us/microsoft-edge/download-the-new-microsoft-edge-based-on-chromium-0f4a3dd7-55df-60f5-739f-00010dba52cf
|
|
24
27
|
case 'edge':
|
|
25
28
|
case 'msedge':
|
|
26
|
-
// Channel is 'msedge-dev' since only it is available in linux
|
|
27
|
-
// https://github.com/microsoft/playwright/blob/07fb81a4efdcc4c2169a61a16b513b9d91be6c49/src/server/chromium/findChromiumChannel.ts#L38
|
|
28
29
|
runModeBrowser.push({ displayName: 'edge', name: 'chromium', launchOptions: { channel: 'msedge-dev' } });
|
|
30
|
+
// https://github.com/microsoft/playwright/blob/07fb81a4efdcc4c2169a61a16b513b9d91be6c49/src/server/chromium/findChromiumChannel.ts#L38
|
|
29
31
|
break;
|
|
30
32
|
default:
|
|
31
33
|
runModeBrowser.push('chromium');
|
|
@@ -38,20 +40,27 @@ if (!process.env.BROWSER) {
|
|
|
38
40
|
// Run in headless mode by default (if process.env.HEADLESS is not specified)
|
|
39
41
|
const runModeHead = !process.env.HEADLESS || process.env.HEADLESS === 'true' ? true : false;
|
|
40
42
|
|
|
43
|
+
// Apply customViewport while running in headless mode
|
|
44
|
+
const runModeViewport = runModeHead ? customViewport : null;
|
|
45
|
+
|
|
46
|
+
let runModeArgs = [];
|
|
47
|
+
|
|
48
|
+
if (runModeBrowser === ['chromium'] && runModeHead === false) {
|
|
49
|
+
runModeArgs = ['--window-size=1920,1080'];
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
module.exports = {
|
|
42
53
|
exitOnPageError: false,
|
|
43
54
|
launchOptions: {
|
|
55
|
+
args: runModeArgs,
|
|
44
56
|
headless: runModeHead,
|
|
45
57
|
slowMo: process.env.SLOWMO ? +process.env.SLOWMO : 0,
|
|
46
58
|
},
|
|
47
59
|
launchType: "LAUNCH",
|
|
48
60
|
contextOptions: {
|
|
49
61
|
acceptDownloads: true,
|
|
50
|
-
viewport:
|
|
51
|
-
|
|
52
|
-
height: 1080
|
|
53
|
-
},
|
|
54
|
-
recordVideo: process.env.VIDEO ? { dir: process.cwd() + '/results/video' } : undefined
|
|
62
|
+
viewport: runModeViewport,
|
|
63
|
+
...(process.env.VIDEO && {recordVideo: { dir: process.cwd() + '/results/video' } }),
|
|
55
64
|
},
|
|
56
65
|
browsers: runModeBrowser
|
|
57
66
|
}
|
package/jest.auth.js
CHANGED
|
@@ -1,36 +1,5 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
let authEndpoint = 'https://test.id.spsc.io/identity/token/';
|
|
4
|
-
|
|
5
|
-
if (process.env.ENV && process.env.ENV.toLowerCase() === 'prod') {
|
|
6
|
-
authEndpoint = 'https://id.spsc.io/identity/token/';
|
|
7
|
-
}
|
|
1
|
+
const { getAuthToken } = require('./helpers/apiCalls');
|
|
8
2
|
|
|
9
3
|
module.exports = async () => {
|
|
10
|
-
|
|
11
|
-
const requestOptions = {
|
|
12
|
-
method: 'POST',
|
|
13
|
-
body: JSON.stringify({
|
|
14
|
-
username: process.env.USER_EMAIL,
|
|
15
|
-
password: process.env.USER_PASSWORD,
|
|
16
|
-
client_id: "595",
|
|
17
|
-
grant_type: "password"
|
|
18
|
-
}),
|
|
19
|
-
headers: {
|
|
20
|
-
'Accept': 'application/json',
|
|
21
|
-
'Content-Type': 'application/json'
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
try {
|
|
25
|
-
const fetchResponse = await fetch(authEndpoint, requestOptions);
|
|
26
|
-
const data = await fetchResponse.json();
|
|
27
|
-
process.env.TOKEN = data.access_token;
|
|
28
|
-
return;
|
|
29
|
-
} catch (e) {
|
|
30
|
-
console.log(e);
|
|
31
|
-
return process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
throw new Error(`Set up process environment variables: USER_EMAIL and USER_PASSWORD.`);
|
|
35
|
-
}
|
|
4
|
+
process.env.TOKEN = await getAuthToken();
|
|
36
5
|
}
|
package/jest.config.js
CHANGED
|
@@ -10,13 +10,14 @@ module.exports = {
|
|
|
10
10
|
includeShortConsoleOutput: true,
|
|
11
11
|
classNameTemplate: "{classname}",
|
|
12
12
|
titleTemplate: "{classname}{title}",
|
|
13
|
-
outputDirectory: "results/jest-junit-report",
|
|
13
|
+
outputDirectory: "results-e2e/jest-junit-report",
|
|
14
14
|
outputName: process.env.SUITE ? `TEST-${process.env.SUITE}.xml` : `TEST-result.xml`
|
|
15
15
|
},
|
|
16
16
|
],
|
|
17
17
|
],
|
|
18
18
|
setupFilesAfterEnv: [
|
|
19
19
|
"./jest.setup.js",
|
|
20
|
+
"jest-extended/all",
|
|
20
21
|
// https://github.com/mattphillips/jest-expect-message/issues/39
|
|
21
22
|
// "jest-expect-message",
|
|
22
23
|
"@alex_neo/jest-expect-message"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spscommerce/e2e-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "An automation test framework which is based on playwright, jest and ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "jest",
|
|
@@ -34,10 +34,12 @@
|
|
|
34
34
|
"jest": "^27.3.1",
|
|
35
35
|
"jest-circus": "^27.3.1",
|
|
36
36
|
"jest-expect-message": "^1.0.2",
|
|
37
|
+
"jest-extended": "^1.2.0",
|
|
37
38
|
"jest-junit": "^13.0.0",
|
|
38
39
|
"jest-playwright-preset": "^1.7.0",
|
|
39
|
-
"node-fetch": "^2.6.
|
|
40
|
+
"node-fetch": "^2.6.6",
|
|
40
41
|
"playwright": "^1.16.1",
|
|
42
|
+
"playwright-core": "^1.17.1",
|
|
41
43
|
"ts-jest": "^27.0.7",
|
|
42
44
|
"typescript": "^4.4.4"
|
|
43
45
|
}
|