@spscommerce/e2e-core 1.1.2 → 1.1.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/.dockerignore +1 -6
- package/Dockerfile +3 -12
- package/helpers/globalSetup.ts +42 -0
- package/helpers/globalTeardown.ts +20 -0
- package/helpers/loggerSetup.ts +20 -29
- package/helpers/matchersSetup.ts +9 -0
- package/package.json +4 -17
- package/playwright.config.ts +97 -0
- package/tsconfig.json +6 -8
- package/helpers/apiCalls.ts +0 -83
- package/helpers/authSetup.ts +0 -32
- package/jest-environment.config.js +0 -91
- package/jest-playwright.config.js +0 -66
- package/jest.auth.js +0 -5
- package/jest.config.js +0 -35
- package/jest.setup.js +0 -20
package/.dockerignore
CHANGED
|
@@ -2,12 +2,7 @@
|
|
|
2
2
|
*
|
|
3
3
|
# White list the required files
|
|
4
4
|
!helpers/
|
|
5
|
-
!credentials.sh
|
|
6
|
-
!jest-environment.config.js
|
|
7
|
-
!jest-playwright.config.js
|
|
8
|
-
!jest.auth.js
|
|
9
|
-
!jest.config.js
|
|
10
|
-
!jest.setup.js
|
|
11
5
|
!package-lock.json
|
|
12
6
|
!package.json
|
|
13
7
|
!tsconfig.json
|
|
8
|
+
!playwright.config.ts
|
package/Dockerfile
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
FROM mcr.microsoft.com/playwright:
|
|
1
|
+
FROM mcr.microsoft.com/playwright:v1.18.0-focal
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
# https://github.com/microsoft/playwright/blob/07fb81a4efdcc4c2169a61a16b513b9d91be6c49/src/server/chromium/findChromiumChannel.ts#L38
|
|
5
|
-
|
|
6
|
-
RUN apt-get update \
|
|
7
|
-
&& apt-get install --reinstall wget \
|
|
8
|
-
&& wget -q -O - https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \
|
|
9
|
-
&& echo "deb https://packages.microsoft.com/repos/edge stable main" >> /etc/apt/sources.list.d/microsoft-edge.list \
|
|
10
|
-
&& apt-get update -qqy \
|
|
11
|
-
&& apt-get -qqy install microsoft-edge-dev \
|
|
12
|
-
&& rm /etc/apt/sources.list.d/microsoft-edge.list \
|
|
13
|
-
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
|
3
|
+
ARG BROWSER
|
|
14
4
|
|
|
15
5
|
RUN mkdir e2e
|
|
16
6
|
WORKDIR /e2e
|
|
17
7
|
COPY . .
|
|
18
8
|
RUN npm ci
|
|
9
|
+
RUN if [ "$BROWSER" = "msedge" ]; then npx playwright install msedge; fi
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { chromium } from '@playwright/test';
|
|
4
|
+
|
|
5
|
+
async function globalSetup() {
|
|
6
|
+
// Remove custom sub-folders for test artifacts
|
|
7
|
+
const resultsFolder = path.join(process.cwd(), 'results-e2e/');
|
|
8
|
+
if (fs.existsSync(resultsFolder)) {
|
|
9
|
+
for (const subFolder of fs.readdirSync(resultsFolder)) {
|
|
10
|
+
if (!subFolder.includes('auth')) { fs.rmSync(resultsFolder + subFolder, { recursive: true, force: true }); }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
// Launch browser and log in if storageState.json does not exist
|
|
14
|
+
const storageFile = path.join(process.cwd(), 'results-e2e/auth/storageState.json');
|
|
15
|
+
const traceFile = path.join(process.cwd(), 'results-e2e/auth/globalSetup.zip');
|
|
16
|
+
if (!fs.existsSync(storageFile)) {
|
|
17
|
+
const browser = await chromium.launch({ headless: false }); // { headless: false }
|
|
18
|
+
const context = await browser.newContext();
|
|
19
|
+
await context.tracing.start({ screenshots: true, snapshots: true });
|
|
20
|
+
const page = await context.newPage();
|
|
21
|
+
try {
|
|
22
|
+
await page.goto(process.env.BASEURL);
|
|
23
|
+
await page.waitForURL(/https:\/\/auth.*/);
|
|
24
|
+
await page.fill('input[type="email"]', process.env.USER_EMAIL);
|
|
25
|
+
await page.click('button#btn-next');
|
|
26
|
+
await page.fill('input[type="password"]', process.env.USER_PASSWORD);
|
|
27
|
+
await page.click('button#btn-login');
|
|
28
|
+
await page.waitForURL(/launchpad/);
|
|
29
|
+
await page.context().storageState({ path: storageFile });
|
|
30
|
+
} catch (e) {
|
|
31
|
+
console.log(e);
|
|
32
|
+
await context.tracing.stop({ path: traceFile });
|
|
33
|
+
throw new Error('Error occurred while running globalSetup.ts file.');
|
|
34
|
+
}
|
|
35
|
+
await browser.close();
|
|
36
|
+
} else {
|
|
37
|
+
const stat = fs.statSync(storageFile);
|
|
38
|
+
console.log(stat);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default globalSetup;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
async function globalTeardown() {
|
|
5
|
+
const oldFolder = path.join(process.cwd(), 'results-e2e-raw/');
|
|
6
|
+
const newFolder = path.join(process.cwd(), 'results-e2e/screenshots/');
|
|
7
|
+
fs.mkdirSync(newFolder, { recursive: true });
|
|
8
|
+
if (fs.existsSync(oldFolder)) {
|
|
9
|
+
for (const oldSubFolder of fs.readdirSync(oldFolder)) {
|
|
10
|
+
for (const oldFile of fs.readdirSync(path.join(oldFolder, oldSubFolder))) {
|
|
11
|
+
const oldFilePath = `${oldFolder}/${oldSubFolder}/${oldFile}`;
|
|
12
|
+
const newFile = "FFUI_" + oldSubFolder.substring(oldSubFolder.indexOf("FFUI-")).split('-').slice(1, -1).join('_').replace('_', ' ');
|
|
13
|
+
const newFilePath = `${newFolder}/${newFile}.${oldFile.split('.').pop()}`;
|
|
14
|
+
fs.copyFileSync(oldFilePath, newFilePath);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default globalTeardown;
|
package/helpers/loggerSetup.ts
CHANGED
|
@@ -1,42 +1,33 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { Page } from '@playwright/test';
|
|
3
4
|
|
|
4
5
|
let access: any;
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
// Stop tracing and write it to file per test
|
|
19
|
-
try {
|
|
20
|
-
await context.tracing.stop({ path: `${tracePathPerSuite}/${testName}.zip` });
|
|
21
|
-
} catch (e) {
|
|
22
|
-
console.log(e);
|
|
23
|
-
}
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
//------------------------LOGGER------------------------
|
|
28
|
-
|
|
29
|
-
export async function enableLogger(): Promise<void> {
|
|
30
|
-
const testName = expect.getState().currentTestName.replace(/ /g, "");
|
|
7
|
+
export async function enableLogger(file: string[], page: Page): Promise<void> {
|
|
8
|
+
// file:
|
|
9
|
+
// [
|
|
10
|
+
// '1.Acceptance/HealthCheckFlow.test.ts',
|
|
11
|
+
// 'Preconditions_block', // optional
|
|
12
|
+
// 'HealthCheckFlow',
|
|
13
|
+
// 'Transactions_tab_is_opened_with_loaded_list_content'
|
|
14
|
+
// ]
|
|
15
|
+
const suite = file[1];
|
|
16
|
+
const test = file.reverse()[0];
|
|
17
|
+
const testName = `${suite} ${test}`;
|
|
31
18
|
const logPathPerSuite = path.join(process.cwd(), 'results-e2e/logs');
|
|
32
19
|
|
|
33
|
-
// Create folder per suite for error files
|
|
34
|
-
|
|
20
|
+
// Create folder per suite for error files
|
|
21
|
+
fs.mkdirSync(logPathPerSuite, { recursive: true });
|
|
35
22
|
|
|
36
23
|
// Create a writable stream for each file per test
|
|
37
24
|
access = fs.createWriteStream(`${logPathPerSuite}/${testName}.log`)
|
|
38
25
|
process.stdout.write = process.stderr.write = access.write.bind(access);
|
|
39
26
|
|
|
27
|
+
// Write the GitHub link of the currently running test to the beginning of each log file
|
|
28
|
+
console.log(`
|
|
29
|
+
https://github.com/SPSCommerce/${process.env.REPONAME}/search?q=${test}`);
|
|
30
|
+
|
|
40
31
|
// Listen for errors
|
|
41
32
|
page
|
|
42
33
|
.on('console', async message => {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { test } from '@playwright/test';
|
|
2
|
+
import withMessage from '@alex_neo/jest-expect-message/dist/withMessage.js';
|
|
3
|
+
|
|
4
|
+
const expect = withMessage(test.expect);
|
|
5
|
+
|
|
6
|
+
const matchers = require('jest-extended');
|
|
7
|
+
expect.extend(matchers);
|
|
8
|
+
|
|
9
|
+
export default expect;
|
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spscommerce/e2e-core",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "An automation test framework which is based on
|
|
3
|
+
"version": "1.1.5",
|
|
4
|
+
"description": "An automation test framework which is based on Playwright.",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"test": "jest",
|
|
7
6
|
"lint": "eslint ."
|
|
8
7
|
},
|
|
9
8
|
"repository": {
|
|
@@ -23,24 +22,12 @@
|
|
|
23
22
|
"license": "UNLICENSED",
|
|
24
23
|
"dependencies": {
|
|
25
24
|
"@alex_neo/jest-expect-message": "^1.0.5",
|
|
26
|
-
"@
|
|
27
|
-
"@types/jest-expect-message": "^1.0.3",
|
|
28
|
-
"@types/node-fetch": "^3.0.3",
|
|
25
|
+
"@playwright/test": "^1.20.1",
|
|
29
26
|
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
|
30
27
|
"@typescript-eslint/parser": "^4.33.0",
|
|
28
|
+
"dotenv": "^16.0.0",
|
|
31
29
|
"eslint": "^7.32.0",
|
|
32
|
-
"eslint-plugin-jest": "^24.7.0",
|
|
33
|
-
"eslint-plugin-jest-playwright": "^0.2.1",
|
|
34
|
-
"jest": "^27.3.1",
|
|
35
|
-
"jest-circus": "^27.3.1",
|
|
36
|
-
"jest-expect-message": "^1.0.2",
|
|
37
30
|
"jest-extended": "^1.2.0",
|
|
38
|
-
"jest-junit": "^13.0.0",
|
|
39
|
-
"jest-playwright-preset": "^1.7.0",
|
|
40
|
-
"node-fetch": "^2.6.6",
|
|
41
|
-
"playwright": "^1.16.1",
|
|
42
|
-
"playwright-core": "^1.17.1",
|
|
43
|
-
"ts-jest": "^27.0.7",
|
|
44
31
|
"typescript": "^4.4.4"
|
|
45
32
|
}
|
|
46
33
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { PlaywrightTestConfig } from '@playwright/test';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Read environment variables from file.
|
|
6
|
+
* https://github.com/motdotla/dotenv
|
|
7
|
+
*/
|
|
8
|
+
require('dotenv').config();
|
|
9
|
+
|
|
10
|
+
const reportName = process.env.SUITE ? `${process.env.SUITE}.xml` : `TEST-result.xml`;
|
|
11
|
+
|
|
12
|
+
let browserProject = [];
|
|
13
|
+
switch (process.env.BROWSER) {
|
|
14
|
+
case 'chromium':
|
|
15
|
+
case 'firefox':
|
|
16
|
+
case 'webkit':
|
|
17
|
+
browserProject = [{
|
|
18
|
+
name: process.env.BROWSER,
|
|
19
|
+
use: {
|
|
20
|
+
browserName: process.env.BROWSER,
|
|
21
|
+
},
|
|
22
|
+
}];
|
|
23
|
+
break;
|
|
24
|
+
case 'msedge':
|
|
25
|
+
case 'chrome':
|
|
26
|
+
browserProject = [{
|
|
27
|
+
name: process.env.BROWSER,
|
|
28
|
+
use: {
|
|
29
|
+
channel: process.env.BROWSER,
|
|
30
|
+
},
|
|
31
|
+
}];
|
|
32
|
+
break;
|
|
33
|
+
default:
|
|
34
|
+
browserProject = [{
|
|
35
|
+
name: 'chromium',
|
|
36
|
+
use: {
|
|
37
|
+
browserName: 'chromium',
|
|
38
|
+
},
|
|
39
|
+
}];
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const domain = 'commerce.spscommerce.com';
|
|
44
|
+
process.env.BASEURL = process.env.URL && process.env.URL === 'prod' ? 'https://' + domain : 'https://test.' + domain;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* See https://playwright.dev/docs/test-configuration.
|
|
48
|
+
*/
|
|
49
|
+
const config: PlaywrightTestConfig = {
|
|
50
|
+
testDir: '../tests-e2e',
|
|
51
|
+
//testDir: './tests-e2e',
|
|
52
|
+
/* Maximum time one test can run for. */
|
|
53
|
+
timeout: +process.env.TIMEOUT || 30000,
|
|
54
|
+
expect: {
|
|
55
|
+
/**
|
|
56
|
+
* Maximum time expect() should wait for the condition to be met.
|
|
57
|
+
* For example in `await expect(locator).toHaveText();`
|
|
58
|
+
*/
|
|
59
|
+
timeout: 5000
|
|
60
|
+
},
|
|
61
|
+
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
|
62
|
+
forbidOnly: !!process.env.CI,
|
|
63
|
+
/* Retry on CI only */
|
|
64
|
+
retries: +process.env.RETRIES || 0,
|
|
65
|
+
/* Opt out of parallel tests on CI. */
|
|
66
|
+
workers: +process.env.WORKERS || 1,
|
|
67
|
+
/* Stop test suite execution after reaching this number of failed tests and skip any tests that were not executed yet. */
|
|
68
|
+
maxFailures: process.env.CI ? 5 : 0,
|
|
69
|
+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
|
70
|
+
reporter: [
|
|
71
|
+
['junit', { outputFile: path.join(process.cwd(), `results-e2e/reports/${reportName}`) }],
|
|
72
|
+
['list']
|
|
73
|
+
],
|
|
74
|
+
globalSetup: './helpers/globalSetup',
|
|
75
|
+
globalTeardown: './helpers/globalTeardown',
|
|
76
|
+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
|
77
|
+
use: {
|
|
78
|
+
storageState: path.join(process.cwd(), 'results-e2e/auth/storageState.json'),
|
|
79
|
+
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
|
80
|
+
actionTimeout: 30000,
|
|
81
|
+
/* Collect screenshot after each test failure. See https://playwright.dev/docs/test-configuration#automatic-screenshots */
|
|
82
|
+
screenshot: 'only-on-failure',
|
|
83
|
+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
|
84
|
+
trace: process.env.TRACER ? 'retain-on-failure' : 'off',
|
|
85
|
+
// Run in headless mode by default (if process.env.HEADLESS is not specified)
|
|
86
|
+
headless: !process.env.HEADLESS || process.env.HEADLESS === 'true' ? true : false,
|
|
87
|
+
ignoreHTTPSErrors: true,
|
|
88
|
+
/* Base URL to use in actions like `await page.goto('/')`. */
|
|
89
|
+
// baseURL: 'http://localhost:3000',
|
|
90
|
+
},
|
|
91
|
+
/* Configure projects for major browsers */
|
|
92
|
+
projects: browserProject,
|
|
93
|
+
/* Folder for test artifacts such as screenshots, videos, traces, etc. (cleaned at the start) */
|
|
94
|
+
outputDir: path.join(process.cwd(), 'results-e2e-raw/'),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export default config;
|
package/tsconfig.json
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
4
|
-
"module": "
|
|
3
|
+
"target": "es2017",
|
|
4
|
+
"module": "esnext",
|
|
5
5
|
"esModuleInterop": true,
|
|
6
|
+
"resolveJsonModule": true,
|
|
7
|
+
"moduleResolution": "node",
|
|
6
8
|
"forceConsistentCasingInFileNames": true,
|
|
7
|
-
"types": [
|
|
8
|
-
|
|
9
|
-
"jest-expect-message",
|
|
10
|
-
"jest-playwright-preset",
|
|
11
|
-
"playwright"
|
|
12
|
-
]
|
|
9
|
+
// "types": [
|
|
10
|
+
// ]
|
|
13
11
|
}
|
|
14
12
|
}
|
package/helpers/apiCalls.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
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 => {
|
|
20
|
-
window.localStorage.setItem(params.name, params.value);
|
|
21
|
-
}, { name: "access_token", value: process.env.TOKEN });
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export async function unauthenticate(): Promise<void> {
|
|
26
|
-
await context.clearCookies();
|
|
27
|
-
await context.addInitScript(() => window.localStorage.clear());
|
|
28
|
-
await context.addInitScript(() => window.sessionStorage.clear());
|
|
29
|
-
await page.goto(process.env.BASEURL);
|
|
30
|
-
await page.waitForURL(/https:\/\/auth.*/);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
const ParentEnvironment = require('jest-playwright-preset/lib/PlaywrightEnvironment').default
|
|
2
|
-
const pwConfig = require("./jest-playwright.config");
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
|
|
6
|
-
class JestEnvironmentFailFast extends ParentEnvironment {
|
|
7
|
-
failedTest = false;
|
|
8
|
-
|
|
9
|
-
constructor(config, context) {
|
|
10
|
-
super(config, context);
|
|
11
|
-
this.videos = [];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// DESCRIBE FINISHED
|
|
15
|
-
// Rename suite video file if any test is failed
|
|
16
|
-
// Delete suite video file if no test is failed
|
|
17
|
-
async teardown() {
|
|
18
|
-
await super.teardown();
|
|
19
|
-
if (this.videos.length !== 0) {
|
|
20
|
-
const { recordVideo } = pwConfig.contextOptions;
|
|
21
|
-
const anyTestFailed = this.videos.find(element => element.success !== true);
|
|
22
|
-
if (anyTestFailed) {
|
|
23
|
-
const newVideoName = `${recordVideo.dir}/${anyTestFailed.testSuiteName}.webm`;
|
|
24
|
-
fs.renameSync(anyTestFailed.videoName, newVideoName);
|
|
25
|
-
} else {
|
|
26
|
-
fs.unlinkSync(this.videos[0].videoName);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Jest events:
|
|
32
|
-
// https://github.com/facebook/jest/issues/10059
|
|
33
|
-
// https://github.com/facebook/jest/blob/main/packages/jest-circus/src/eventHandler.ts
|
|
34
|
-
async handleTestEvent(event, state) {
|
|
35
|
-
await super.handleTestEvent(event);
|
|
36
|
-
|
|
37
|
-
// TEST STARTED
|
|
38
|
-
// Delete already existing screenshot/trace file before test starts if it ran twice due to jest.retryTimes()
|
|
39
|
-
if (event.name === 'test_start') {
|
|
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
|
-
if (fs.existsSync(traceFilePerTest)) {
|
|
43
|
-
fs.unlinkSync(traceFilePerTest);
|
|
44
|
-
}
|
|
45
|
-
if (fs.existsSync(screenFilePerTest)) {
|
|
46
|
-
fs.unlinkSync(screenFilePerTest);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// TEST FAILED
|
|
51
|
-
// Take a screenshot for each failed test while running in CI
|
|
52
|
-
if (event.name === 'test_fn_failure' && !event.test.parent.name.includes('visual')) {
|
|
53
|
-
this.failedTest = true;
|
|
54
|
-
this.failedTestName = event.test.name;
|
|
55
|
-
if (state.currentlyRunningTest) {
|
|
56
|
-
await this.global.page.screenshot({
|
|
57
|
-
fullPage: true,
|
|
58
|
-
path: process.cwd() + `/results-e2e/screenshots/${event.test.parent.name}${event.test.name}.png`,
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// TEST FINISHED SUCCESSFULLY
|
|
64
|
-
// Delete trace file for each succeeded test
|
|
65
|
-
if (event.name === 'test_done' && event.test.errors.length === 0) {
|
|
66
|
-
const traceFilePerTest = path.join(process.cwd(), `results-e2e/traces/${event.test.parent.name}${event.test.name}.zip`);
|
|
67
|
-
if (fs.existsSync(traceFilePerTest)) {
|
|
68
|
-
fs.unlinkSync(traceFilePerTest);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// TEST FINISHED AND VIDEO OPTION IS SET TO TRUE
|
|
73
|
-
// Save test status, test suite name and random PW video name
|
|
74
|
-
if (event.name === "test_done" && process.env.VIDEO) {
|
|
75
|
-
const success = event.test.errors.length === 0 ? true : false;
|
|
76
|
-
const testSuiteName = event.test.parent.name;
|
|
77
|
-
const videoName = await this.global.page.video().path();
|
|
78
|
-
this.videos.push({ testSuiteName, videoName, success });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// TEST FINISHES AND FAILS
|
|
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 'FailFast'
|
|
85
|
-
if (this.failedTest && event.name === 'test_start' && event.test.parent.name.includes('FailFast')) {
|
|
86
|
-
event.test.mode = 'skip';
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
module.exports = JestEnvironmentFailFast
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
const customViewport = {
|
|
2
|
-
width: 1920,
|
|
3
|
-
height: 1080,
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
let runModeBrowser = [];
|
|
7
|
-
|
|
8
|
-
// Run in chromium browser by default (if process.env.BROWSER is not specified)
|
|
9
|
-
// Use + separator for multi-browser run, e.g. BROWSER=edge+safari
|
|
10
|
-
if (!process.env.BROWSER) {
|
|
11
|
-
runModeBrowser = ['chromium'];
|
|
12
|
-
process.env.BROWSER = 'chromium';
|
|
13
|
-
} else {
|
|
14
|
-
process.env.BROWSER.split('+').map(browser => {
|
|
15
|
-
switch (browser) {
|
|
16
|
-
case 'firefox':
|
|
17
|
-
runModeBrowser.push('firefox');
|
|
18
|
-
break;
|
|
19
|
-
case 'chrome':
|
|
20
|
-
case 'chromium':
|
|
21
|
-
runModeBrowser.push('chromium');
|
|
22
|
-
break;
|
|
23
|
-
case 'safari':
|
|
24
|
-
case 'webkit':
|
|
25
|
-
runModeBrowser.push('webkit');
|
|
26
|
-
break;
|
|
27
|
-
case 'edge':
|
|
28
|
-
case 'msedge':
|
|
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
|
|
31
|
-
break;
|
|
32
|
-
default:
|
|
33
|
-
runModeBrowser.push('chromium');
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
return runModeBrowser;
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Run in headless mode by default (if process.env.HEADLESS is not specified)
|
|
41
|
-
const runModeHead = !process.env.HEADLESS || process.env.HEADLESS === 'true' ? true : false;
|
|
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
|
-
|
|
52
|
-
module.exports = {
|
|
53
|
-
exitOnPageError: false,
|
|
54
|
-
launchOptions: {
|
|
55
|
-
args: runModeArgs,
|
|
56
|
-
headless: runModeHead,
|
|
57
|
-
slowMo: process.env.SLOWMO ? +process.env.SLOWMO : 0,
|
|
58
|
-
},
|
|
59
|
-
launchType: "LAUNCH",
|
|
60
|
-
contextOptions: {
|
|
61
|
-
acceptDownloads: true,
|
|
62
|
-
viewport: runModeViewport,
|
|
63
|
-
...(process.env.VIDEO && {recordVideo: { dir: process.cwd() + '/results/video' } }),
|
|
64
|
-
},
|
|
65
|
-
browsers: runModeBrowser
|
|
66
|
-
}
|
package/jest.auth.js
DELETED
package/jest.config.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
globalSetup: './jest.auth.js',
|
|
3
|
-
maxWorkers: +process.env.WORKERS,
|
|
4
|
-
preset: "jest-playwright-preset",
|
|
5
|
-
reporters: [
|
|
6
|
-
"default",
|
|
7
|
-
[
|
|
8
|
-
"jest-junit",
|
|
9
|
-
{
|
|
10
|
-
includeShortConsoleOutput: true,
|
|
11
|
-
classNameTemplate: "{classname}",
|
|
12
|
-
titleTemplate: "{classname}{title}",
|
|
13
|
-
outputDirectory: "results-e2e/jest-junit-report",
|
|
14
|
-
outputName: process.env.SUITE ? `TEST-${process.env.SUITE}.xml` : `TEST-result.xml`
|
|
15
|
-
},
|
|
16
|
-
],
|
|
17
|
-
],
|
|
18
|
-
setupFilesAfterEnv: [
|
|
19
|
-
"./jest.setup.js",
|
|
20
|
-
"jest-extended/all",
|
|
21
|
-
// https://github.com/mattphillips/jest-expect-message/issues/39
|
|
22
|
-
// "jest-expect-message",
|
|
23
|
-
"@alex_neo/jest-expect-message"
|
|
24
|
-
],
|
|
25
|
-
testEnvironment: "./jest-environment.config.js",
|
|
26
|
-
testRunner: "jest-circus/runner",
|
|
27
|
-
testTimeout: +process.env.TIMEOUT || 30000,
|
|
28
|
-
transform: {
|
|
29
|
-
"^.+\\.(ts)$": "ts-jest",
|
|
30
|
-
},
|
|
31
|
-
transformIgnorePatterns: [
|
|
32
|
-
"/node_modules/?!(@spscommerce/e2e-core)"
|
|
33
|
-
],
|
|
34
|
-
verbose: true,
|
|
35
|
-
}
|
package/jest.setup.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const { authenticate } = require('./helpers/authSetup');
|
|
2
|
-
const { enableTracer, disableTracer, enableLogger, disableLogger } = require('./helpers/loggerSetup');
|
|
3
|
-
|
|
4
|
-
// Global hooks
|
|
5
|
-
// You can add additional hooks for a specific suite directly in the test file
|
|
6
|
-
beforeAll(async () => {
|
|
7
|
-
await authenticate();
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
if (process.env.LOGGER || process.env.TRACER) {
|
|
11
|
-
beforeEach(async () => {
|
|
12
|
-
process.env.LOGGER && (process.env.LOGGER.toLowerCase() === 'true') && await enableLogger();
|
|
13
|
-
process.env.TRACER && (process.env.TRACER.toLowerCase() === 'true') && await enableTracer();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
afterEach(async () => {
|
|
17
|
-
process.env.LOGGER && (process.env.LOGGER.toLowerCase() === 'true') && await disableLogger();
|
|
18
|
-
process.env.TRACER && (process.env.TRACER.toLowerCase() === 'true') && await disableTracer();
|
|
19
|
-
});
|
|
20
|
-
}
|