@trackunit/iris-app-e2e 1.8.86-alpha-3d825eabffe.0 → 1.8.86
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 +1 -2
- package/index.cjs.js +191 -34
- package/index.esm.js +189 -34
- package/package.json +3 -13
- package/src/index.d.ts +3 -10
- package/src/setup/setupHarRecording.d.ts +0 -1
- package/index.cjs.default.js +0 -1
- package/index.cjs.mjs +0 -2
- package/src/plugins/nxPreset.d.ts +0 -27
- package/src/support.d.ts +0 -8
package/README.md
CHANGED
|
@@ -53,8 +53,7 @@ npm install cypress @testing-library/cypress --save-dev
|
|
|
53
53
|
Create a `cypress/support/e2e.ts` file:
|
|
54
54
|
|
|
55
55
|
```typescript
|
|
56
|
-
|
|
57
|
-
import { setupDefaultCommands, setupE2E } from '@trackunit/iris-app-e2e/support';
|
|
56
|
+
import { setupDefaultCommands, setupE2E } from '@trackunit/iris-app-e2e';
|
|
58
57
|
|
|
59
58
|
// Set up E2E environment
|
|
60
59
|
setupE2E();
|
package/index.cjs.js
CHANGED
|
@@ -4,8 +4,6 @@ var fs = require('fs');
|
|
|
4
4
|
var path = require('path');
|
|
5
5
|
var crypto = require('crypto');
|
|
6
6
|
var nodeXlsx = require('node-xlsx');
|
|
7
|
-
var cypressPreset = require('@nx/cypress/plugins/cypress-preset');
|
|
8
|
-
var nxTsconfigPaths_plugin = require('@nx/vite/plugins/nx-tsconfig-paths.plugin');
|
|
9
7
|
|
|
10
8
|
function _interopNamespaceDefault(e) {
|
|
11
9
|
var n = Object.create(null);
|
|
@@ -26,6 +24,138 @@ function _interopNamespaceDefault(e) {
|
|
|
26
24
|
|
|
27
25
|
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
|
|
28
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Sets up default Cypress commands for E2E testing.
|
|
29
|
+
* Adds custom commands like getByTestId, login, enterIrisApp, etc.
|
|
30
|
+
*/
|
|
31
|
+
function setupDefaultCommands() {
|
|
32
|
+
Cypress.Commands.add("getByTestId", {
|
|
33
|
+
prevSubject: ["optional"],
|
|
34
|
+
},
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
(subject, testId, options = {}) => {
|
|
37
|
+
const selector = `[data-testid="${testId}"]`;
|
|
38
|
+
const timeout = options.timeout ?? 15000;
|
|
39
|
+
if (subject) {
|
|
40
|
+
if (Cypress.dom.isElement(subject) || Cypress.dom.isJquery(subject)) {
|
|
41
|
+
return cy.wrap(subject, { timeout }).find(selector, { timeout });
|
|
42
|
+
}
|
|
43
|
+
else if (Cypress.dom.isWindow(subject)) {
|
|
44
|
+
return cy.get(selector, { timeout });
|
|
45
|
+
}
|
|
46
|
+
else if (Array.isArray(subject)) {
|
|
47
|
+
const element = subject.map(el => cy.wrap(el, { timeout }).find(selector, { timeout }));
|
|
48
|
+
if (element[0]) {
|
|
49
|
+
return element[0];
|
|
50
|
+
}
|
|
51
|
+
return cy.wrap(null);
|
|
52
|
+
}
|
|
53
|
+
return cy.get(selector, { timeout });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return cy.get(selector, { timeout });
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
Cypress.Commands.add("login", fixture => {
|
|
60
|
+
const envUrl = `${Cypress.config().baseUrl}/env`;
|
|
61
|
+
cy.log(`Getting env from: ${envUrl}`);
|
|
62
|
+
cy.request({
|
|
63
|
+
method: "GET",
|
|
64
|
+
url: envUrl,
|
|
65
|
+
headers: {
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
},
|
|
68
|
+
}).then(envResponse => {
|
|
69
|
+
const env = envResponse.body;
|
|
70
|
+
const domain = env.auth?.url;
|
|
71
|
+
if (!(Boolean(domain))) {
|
|
72
|
+
throw new Error(`No domain found from servers /env found env: ${JSON.stringify(env)}`);
|
|
73
|
+
}
|
|
74
|
+
cy.log(`Using: ${domain}`);
|
|
75
|
+
cy.clearCookies();
|
|
76
|
+
cy.fixture(fixture ?? "auth").then(({ username, password }) => {
|
|
77
|
+
const options = {
|
|
78
|
+
warnBeforePasswordExpired: true,
|
|
79
|
+
multiOptionalFactorEnroll: false,
|
|
80
|
+
};
|
|
81
|
+
cy.request({
|
|
82
|
+
method: "POST",
|
|
83
|
+
url: `${domain}/api/v1/authn`,
|
|
84
|
+
headers: {
|
|
85
|
+
"Content-Type": "application/json",
|
|
86
|
+
},
|
|
87
|
+
body: {
|
|
88
|
+
username,
|
|
89
|
+
password,
|
|
90
|
+
options,
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
.then(response => {
|
|
94
|
+
if (response.isOkStatusCode) {
|
|
95
|
+
const sessionToken = response.body.sessionToken;
|
|
96
|
+
return cy.visit(`/auth/manager-classic#session_token=${sessionToken}&fleetHome=true`);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
throw new Error(`Could not get a session token for user: ${username}, ${JSON.stringify(response)}`);
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
.then(() => cy.url({ timeout: 20000 }).should("not.include", "manager-classic")) // Wait for redirect away from manager-classic
|
|
103
|
+
.url()
|
|
104
|
+
.should("contain", `${Cypress.config().baseUrl}`);
|
|
105
|
+
cy.get("#host-layout-content", { timeout: 15000 }).should("be.visible"); // Wait for host layout to be visible
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
Cypress.Commands.add("switchToLocalDevMode", () => {
|
|
110
|
+
cy.getByTestId("developerPortalNav").click();
|
|
111
|
+
cy.url({ timeout: 15000, log: true }).should("contain", "/iris-sdk-portal");
|
|
112
|
+
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
|
+
cy.getByTestId("localDevModeSwitch-input").then(($ele) => {
|
|
114
|
+
if ($ele && !$ele.is(":checked")) {
|
|
115
|
+
cy.getByTestId("localDevModeSwitch-thumb").click();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
Cypress.Commands.add("enterIrisApp", options => {
|
|
120
|
+
return cy
|
|
121
|
+
.get(`iframe[data-testid="${options?.testId ?? "app-iframe"}"]`, { timeout: 30000 })
|
|
122
|
+
.first()
|
|
123
|
+
.its("0.contentDocument.body", { timeout: 30000, log: true })
|
|
124
|
+
.should("not.be.empty")
|
|
125
|
+
.then($body => {
|
|
126
|
+
return () => cy.wrap($body);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
Cypress.Commands.add("enterStorybookPreview", options => {
|
|
130
|
+
return cy
|
|
131
|
+
.get(`iframe[id="${options?.testId ?? "storybook-preview-iframe"}"]`, { timeout: 30000 })
|
|
132
|
+
.first()
|
|
133
|
+
.its("0.contentDocument.body", { timeout: 30000, log: true })
|
|
134
|
+
.should("not.be.empty")
|
|
135
|
+
.then($body => {
|
|
136
|
+
return () => cy.wrap($body);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
Cypress.Commands.add("getValidateFeatureFlags", () => {
|
|
140
|
+
cy.intercept({ url: "**/ValidateFeatureFlags" }).as("ValidateFeatureFlags");
|
|
141
|
+
cy.intercept({ url: "**/UserPermissions" }).as("UserPermissions");
|
|
142
|
+
cy.intercept({ url: "**/ActiveSubscription" }).as("ActiveSubscription");
|
|
143
|
+
});
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
145
|
+
let ccData = null;
|
|
146
|
+
Cypress.Commands.add("configCat", value => {
|
|
147
|
+
if (!ccData) {
|
|
148
|
+
cy.wait("@ValidateFeatureFlags").then(intercept => {
|
|
149
|
+
ccData = intercept.response?.body?.data?.featureFlags;
|
|
150
|
+
return ccData?.find((ff) => ff.key === value).state;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
return ccData?.find((ff) => ff.key === value).state;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
29
159
|
/* eslint-disable no-console */
|
|
30
160
|
/**
|
|
31
161
|
* Writes a file with Prettier formatting applied.
|
|
@@ -246,7 +376,7 @@ function sanitizeForFilename(str) {
|
|
|
246
376
|
* // Returns: "Test with - special chars (passed) (attempt 3)"
|
|
247
377
|
*/
|
|
248
378
|
function fileNameBuilder(testName, state, currentRetry) {
|
|
249
|
-
const fileName = `${testName} ${""} (${state.toLowerCase()})`;
|
|
379
|
+
const fileName = `${testName} ${currentRetry !== undefined ? `(Attempt ${currentRetry + 1})` : ""} (${state.toLowerCase()})`;
|
|
250
380
|
return sanitizeForFilename(fileName);
|
|
251
381
|
}
|
|
252
382
|
|
|
@@ -379,44 +509,71 @@ const setupPlugins = (on, config, logWriter, installHarGenerator) => {
|
|
|
379
509
|
return config;
|
|
380
510
|
};
|
|
381
511
|
|
|
512
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
382
513
|
/**
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
* @param filename - Pass `__filename` from the calling cypress.config.ts
|
|
387
|
-
* @example
|
|
388
|
-
* ```ts
|
|
389
|
-
* import { createNxPreset, CypressPluginConfig, defaultCypressConfig, setupPlugins } from "@trackunit/iris-app-e2e";
|
|
390
|
-
* import { defineConfig } from "cypress";
|
|
391
|
-
*
|
|
392
|
-
* const nxPreset = createNxPreset(__filename);
|
|
393
|
-
*
|
|
394
|
-
* export default defineConfig({
|
|
395
|
-
* chromeWebSecurity: false,
|
|
396
|
-
* e2e: {
|
|
397
|
-
* ...nxPreset,
|
|
398
|
-
* ...defaultCypressConfig(__dirname),
|
|
399
|
-
* async setupNodeEvents(on, config: CypressPluginConfig) {
|
|
400
|
-
* await nxPreset.setupNodeEvents?.(on, config);
|
|
401
|
-
* return setupPlugins(on, config, formatter, installHarGenerator);
|
|
402
|
-
* },
|
|
403
|
-
* },
|
|
404
|
-
* });
|
|
405
|
-
* ```
|
|
514
|
+
* Sets up HAR (HTTP Archive) recording for E2E tests.
|
|
515
|
+
* Records network activity and saves HAR files for failed tests.
|
|
406
516
|
*/
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
517
|
+
function setupHarRecording() {
|
|
518
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
519
|
+
require("@neuralegion/cypress-har-generator/commands");
|
|
520
|
+
beforeEach(() => {
|
|
521
|
+
const harDir = Cypress.env("hars_folders");
|
|
522
|
+
cy.recordHar({ rootDir: harDir });
|
|
523
|
+
});
|
|
524
|
+
afterEach(function () {
|
|
525
|
+
if (this.currentTest.state === "failed") {
|
|
526
|
+
const harDir = Cypress.env("hars_folders");
|
|
527
|
+
const testName = fileNameBuilder(`${Cypress.currentTest.titlePath[0]} - ${Cypress.currentTest.title}`, "failed", Cypress.currentRetry);
|
|
528
|
+
cy.saveHar({ outDir: harDir, fileName: testName });
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Sets up the E2E testing environment with HAR recording and terminal logging.
|
|
535
|
+
*/
|
|
536
|
+
function setupE2E() {
|
|
537
|
+
setupHarRecording();
|
|
538
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
539
|
+
require("cypress-terminal-report/src/installLogsCollector")({
|
|
540
|
+
xhr: {
|
|
541
|
+
printHeaderData: true,
|
|
542
|
+
printRequestData: true,
|
|
414
543
|
},
|
|
415
544
|
});
|
|
545
|
+
Cypress.on("uncaught:exception", (err) => {
|
|
546
|
+
// eslint-disable-next-line no-console
|
|
547
|
+
console.log("Caught Error: ", err);
|
|
548
|
+
// returning false here prevents Cypress from
|
|
549
|
+
// failing the test
|
|
550
|
+
return false;
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
const originalDescribe = global.describe;
|
|
554
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
555
|
+
if (originalDescribe) {
|
|
556
|
+
const codeowner = Cypress.env("codeowner");
|
|
557
|
+
const addOwnerToTitle = (title) => {
|
|
558
|
+
return codeowner ? title + ` [${codeowner}]` : title;
|
|
559
|
+
};
|
|
560
|
+
function patchedDescribe(title, fn) {
|
|
561
|
+
return originalDescribe(addOwnerToTitle(title), fn);
|
|
562
|
+
}
|
|
563
|
+
patchedDescribe.only = (title, fn) => {
|
|
564
|
+
return originalDescribe.only(addOwnerToTitle(title), fn);
|
|
565
|
+
};
|
|
566
|
+
patchedDescribe.skip = (title, fn) => {
|
|
567
|
+
return originalDescribe.skip(addOwnerToTitle(title), fn);
|
|
568
|
+
};
|
|
569
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
570
|
+
global.describe = patchedDescribe;
|
|
416
571
|
}
|
|
417
572
|
|
|
418
573
|
exports.createLogFile = createLogFile;
|
|
419
|
-
exports.createNxPreset = createNxPreset;
|
|
420
574
|
exports.defaultCypressConfig = defaultCypressConfig;
|
|
575
|
+
exports.setupDefaultCommands = setupDefaultCommands;
|
|
576
|
+
exports.setupE2E = setupE2E;
|
|
577
|
+
exports.setupHarRecording = setupHarRecording;
|
|
421
578
|
exports.setupPlugins = setupPlugins;
|
|
422
579
|
exports.writeFileWithPrettier = writeFileWithPrettier;
|
package/index.esm.js
CHANGED
|
@@ -3,8 +3,138 @@ import * as path from 'path';
|
|
|
3
3
|
import path__default from 'path';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
5
|
import { parse } from 'node-xlsx';
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Sets up default Cypress commands for E2E testing.
|
|
9
|
+
* Adds custom commands like getByTestId, login, enterIrisApp, etc.
|
|
10
|
+
*/
|
|
11
|
+
function setupDefaultCommands() {
|
|
12
|
+
Cypress.Commands.add("getByTestId", {
|
|
13
|
+
prevSubject: ["optional"],
|
|
14
|
+
},
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
(subject, testId, options = {}) => {
|
|
17
|
+
const selector = `[data-testid="${testId}"]`;
|
|
18
|
+
const timeout = options.timeout ?? 15000;
|
|
19
|
+
if (subject) {
|
|
20
|
+
if (Cypress.dom.isElement(subject) || Cypress.dom.isJquery(subject)) {
|
|
21
|
+
return cy.wrap(subject, { timeout }).find(selector, { timeout });
|
|
22
|
+
}
|
|
23
|
+
else if (Cypress.dom.isWindow(subject)) {
|
|
24
|
+
return cy.get(selector, { timeout });
|
|
25
|
+
}
|
|
26
|
+
else if (Array.isArray(subject)) {
|
|
27
|
+
const element = subject.map(el => cy.wrap(el, { timeout }).find(selector, { timeout }));
|
|
28
|
+
if (element[0]) {
|
|
29
|
+
return element[0];
|
|
30
|
+
}
|
|
31
|
+
return cy.wrap(null);
|
|
32
|
+
}
|
|
33
|
+
return cy.get(selector, { timeout });
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
return cy.get(selector, { timeout });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
Cypress.Commands.add("login", fixture => {
|
|
40
|
+
const envUrl = `${Cypress.config().baseUrl}/env`;
|
|
41
|
+
cy.log(`Getting env from: ${envUrl}`);
|
|
42
|
+
cy.request({
|
|
43
|
+
method: "GET",
|
|
44
|
+
url: envUrl,
|
|
45
|
+
headers: {
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
},
|
|
48
|
+
}).then(envResponse => {
|
|
49
|
+
const env = envResponse.body;
|
|
50
|
+
const domain = env.auth?.url;
|
|
51
|
+
if (!(Boolean(domain))) {
|
|
52
|
+
throw new Error(`No domain found from servers /env found env: ${JSON.stringify(env)}`);
|
|
53
|
+
}
|
|
54
|
+
cy.log(`Using: ${domain}`);
|
|
55
|
+
cy.clearCookies();
|
|
56
|
+
cy.fixture(fixture ?? "auth").then(({ username, password }) => {
|
|
57
|
+
const options = {
|
|
58
|
+
warnBeforePasswordExpired: true,
|
|
59
|
+
multiOptionalFactorEnroll: false,
|
|
60
|
+
};
|
|
61
|
+
cy.request({
|
|
62
|
+
method: "POST",
|
|
63
|
+
url: `${domain}/api/v1/authn`,
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
},
|
|
67
|
+
body: {
|
|
68
|
+
username,
|
|
69
|
+
password,
|
|
70
|
+
options,
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
.then(response => {
|
|
74
|
+
if (response.isOkStatusCode) {
|
|
75
|
+
const sessionToken = response.body.sessionToken;
|
|
76
|
+
return cy.visit(`/auth/manager-classic#session_token=${sessionToken}&fleetHome=true`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
throw new Error(`Could not get a session token for user: ${username}, ${JSON.stringify(response)}`);
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
.then(() => cy.url({ timeout: 20000 }).should("not.include", "manager-classic")) // Wait for redirect away from manager-classic
|
|
83
|
+
.url()
|
|
84
|
+
.should("contain", `${Cypress.config().baseUrl}`);
|
|
85
|
+
cy.get("#host-layout-content", { timeout: 15000 }).should("be.visible"); // Wait for host layout to be visible
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
Cypress.Commands.add("switchToLocalDevMode", () => {
|
|
90
|
+
cy.getByTestId("developerPortalNav").click();
|
|
91
|
+
cy.url({ timeout: 15000, log: true }).should("contain", "/iris-sdk-portal");
|
|
92
|
+
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
+
cy.getByTestId("localDevModeSwitch-input").then(($ele) => {
|
|
94
|
+
if ($ele && !$ele.is(":checked")) {
|
|
95
|
+
cy.getByTestId("localDevModeSwitch-thumb").click();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
Cypress.Commands.add("enterIrisApp", options => {
|
|
100
|
+
return cy
|
|
101
|
+
.get(`iframe[data-testid="${options?.testId ?? "app-iframe"}"]`, { timeout: 30000 })
|
|
102
|
+
.first()
|
|
103
|
+
.its("0.contentDocument.body", { timeout: 30000, log: true })
|
|
104
|
+
.should("not.be.empty")
|
|
105
|
+
.then($body => {
|
|
106
|
+
return () => cy.wrap($body);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
Cypress.Commands.add("enterStorybookPreview", options => {
|
|
110
|
+
return cy
|
|
111
|
+
.get(`iframe[id="${options?.testId ?? "storybook-preview-iframe"}"]`, { timeout: 30000 })
|
|
112
|
+
.first()
|
|
113
|
+
.its("0.contentDocument.body", { timeout: 30000, log: true })
|
|
114
|
+
.should("not.be.empty")
|
|
115
|
+
.then($body => {
|
|
116
|
+
return () => cy.wrap($body);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
Cypress.Commands.add("getValidateFeatureFlags", () => {
|
|
120
|
+
cy.intercept({ url: "**/ValidateFeatureFlags" }).as("ValidateFeatureFlags");
|
|
121
|
+
cy.intercept({ url: "**/UserPermissions" }).as("UserPermissions");
|
|
122
|
+
cy.intercept({ url: "**/ActiveSubscription" }).as("ActiveSubscription");
|
|
123
|
+
});
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
+
let ccData = null;
|
|
126
|
+
Cypress.Commands.add("configCat", value => {
|
|
127
|
+
if (!ccData) {
|
|
128
|
+
cy.wait("@ValidateFeatureFlags").then(intercept => {
|
|
129
|
+
ccData = intercept.response?.body?.data?.featureFlags;
|
|
130
|
+
return ccData?.find((ff) => ff.key === value).state;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
return ccData?.find((ff) => ff.key === value).state;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
8
138
|
|
|
9
139
|
/* eslint-disable no-console */
|
|
10
140
|
/**
|
|
@@ -226,7 +356,7 @@ function sanitizeForFilename(str) {
|
|
|
226
356
|
* // Returns: "Test with - special chars (passed) (attempt 3)"
|
|
227
357
|
*/
|
|
228
358
|
function fileNameBuilder(testName, state, currentRetry) {
|
|
229
|
-
const fileName = `${testName} ${""} (${state.toLowerCase()})`;
|
|
359
|
+
const fileName = `${testName} ${currentRetry !== undefined ? `(Attempt ${currentRetry + 1})` : ""} (${state.toLowerCase()})`;
|
|
230
360
|
return sanitizeForFilename(fileName);
|
|
231
361
|
}
|
|
232
362
|
|
|
@@ -359,40 +489,65 @@ const setupPlugins = (on, config, logWriter, installHarGenerator) => {
|
|
|
359
489
|
return config;
|
|
360
490
|
};
|
|
361
491
|
|
|
492
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
362
493
|
/**
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
* @param filename - Pass `__filename` from the calling cypress.config.ts
|
|
367
|
-
* @example
|
|
368
|
-
* ```ts
|
|
369
|
-
* import { createNxPreset, CypressPluginConfig, defaultCypressConfig, setupPlugins } from "@trackunit/iris-app-e2e";
|
|
370
|
-
* import { defineConfig } from "cypress";
|
|
371
|
-
*
|
|
372
|
-
* const nxPreset = createNxPreset(__filename);
|
|
373
|
-
*
|
|
374
|
-
* export default defineConfig({
|
|
375
|
-
* chromeWebSecurity: false,
|
|
376
|
-
* e2e: {
|
|
377
|
-
* ...nxPreset,
|
|
378
|
-
* ...defaultCypressConfig(__dirname),
|
|
379
|
-
* async setupNodeEvents(on, config: CypressPluginConfig) {
|
|
380
|
-
* await nxPreset.setupNodeEvents?.(on, config);
|
|
381
|
-
* return setupPlugins(on, config, formatter, installHarGenerator);
|
|
382
|
-
* },
|
|
383
|
-
* },
|
|
384
|
-
* });
|
|
385
|
-
* ```
|
|
494
|
+
* Sets up HAR (HTTP Archive) recording for E2E tests.
|
|
495
|
+
* Records network activity and saves HAR files for failed tests.
|
|
386
496
|
*/
|
|
387
|
-
function
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
497
|
+
function setupHarRecording() {
|
|
498
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
499
|
+
require("@neuralegion/cypress-har-generator/commands");
|
|
500
|
+
beforeEach(() => {
|
|
501
|
+
const harDir = Cypress.env("hars_folders");
|
|
502
|
+
cy.recordHar({ rootDir: harDir });
|
|
503
|
+
});
|
|
504
|
+
afterEach(function () {
|
|
505
|
+
if (this.currentTest.state === "failed") {
|
|
506
|
+
const harDir = Cypress.env("hars_folders");
|
|
507
|
+
const testName = fileNameBuilder(`${Cypress.currentTest.titlePath[0]} - ${Cypress.currentTest.title}`, "failed", Cypress.currentRetry);
|
|
508
|
+
cy.saveHar({ outDir: harDir, fileName: testName });
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Sets up the E2E testing environment with HAR recording and terminal logging.
|
|
515
|
+
*/
|
|
516
|
+
function setupE2E() {
|
|
517
|
+
setupHarRecording();
|
|
518
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
519
|
+
require("cypress-terminal-report/src/installLogsCollector")({
|
|
520
|
+
xhr: {
|
|
521
|
+
printHeaderData: true,
|
|
522
|
+
printRequestData: true,
|
|
394
523
|
},
|
|
395
524
|
});
|
|
525
|
+
Cypress.on("uncaught:exception", (err) => {
|
|
526
|
+
// eslint-disable-next-line no-console
|
|
527
|
+
console.log("Caught Error: ", err);
|
|
528
|
+
// returning false here prevents Cypress from
|
|
529
|
+
// failing the test
|
|
530
|
+
return false;
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
const originalDescribe = global.describe;
|
|
534
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
535
|
+
if (originalDescribe) {
|
|
536
|
+
const codeowner = Cypress.env("codeowner");
|
|
537
|
+
const addOwnerToTitle = (title) => {
|
|
538
|
+
return codeowner ? title + ` [${codeowner}]` : title;
|
|
539
|
+
};
|
|
540
|
+
function patchedDescribe(title, fn) {
|
|
541
|
+
return originalDescribe(addOwnerToTitle(title), fn);
|
|
542
|
+
}
|
|
543
|
+
patchedDescribe.only = (title, fn) => {
|
|
544
|
+
return originalDescribe.only(addOwnerToTitle(title), fn);
|
|
545
|
+
};
|
|
546
|
+
patchedDescribe.skip = (title, fn) => {
|
|
547
|
+
return originalDescribe.skip(addOwnerToTitle(title), fn);
|
|
548
|
+
};
|
|
549
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
550
|
+
global.describe = patchedDescribe;
|
|
396
551
|
}
|
|
397
552
|
|
|
398
|
-
export { createLogFile,
|
|
553
|
+
export { createLogFile, defaultCypressConfig, setupDefaultCommands, setupE2E, setupHarRecording, setupPlugins, writeFileWithPrettier };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/iris-app-e2e",
|
|
3
|
-
"version": "1.8.86
|
|
3
|
+
"version": "1.8.86",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"generators": "./generators.json",
|
|
@@ -10,20 +10,10 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@neuralegion/cypress-har-generator": "^5.17.0",
|
|
12
12
|
"@nx/cypress": "22.4.4",
|
|
13
|
-
"@nx/devkit": "22.4.4",
|
|
14
|
-
"@nx/vite": "22.4.4",
|
|
15
13
|
"cypress-terminal-report": "7.0.3",
|
|
16
14
|
"node-xlsx": "^0.23.0",
|
|
17
|
-
"prettier": "^3.4.2"
|
|
18
|
-
|
|
19
|
-
"exports": {
|
|
20
|
-
"./package.json": "./package.json",
|
|
21
|
-
".": {
|
|
22
|
-
"module": "./index.esm.js",
|
|
23
|
-
"types": "./index.d.ts",
|
|
24
|
-
"import": "./index.cjs.mjs",
|
|
25
|
-
"default": "./index.cjs.js"
|
|
26
|
-
}
|
|
15
|
+
"prettier": "^3.4.2",
|
|
16
|
+
"@nx/devkit": "22.4.4"
|
|
27
17
|
},
|
|
28
18
|
"module": "./index.esm.js",
|
|
29
19
|
"main": "./index.cjs.js",
|
package/src/index.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
* Node.js-compatible exports for Cypress config files (cypress.config.ts).
|
|
3
|
-
* These can be safely imported in Node.js context during config processing.
|
|
4
|
-
*
|
|
5
|
-
* For browser-only exports (commands, setup functions that use Cypress global),
|
|
6
|
-
* import from "@trackunit/iris-app-e2e/support" instead.
|
|
7
|
-
*/
|
|
1
|
+
export * from "./commands/defaultCommands";
|
|
8
2
|
export * from "./plugins/createLogFile";
|
|
9
3
|
export * from "./plugins/defaultPlugins";
|
|
10
|
-
export * from "./plugins/nxPreset";
|
|
11
4
|
export * from "./plugins/writeFileWithPrettier";
|
|
12
|
-
export
|
|
13
|
-
export
|
|
5
|
+
export * from "./setup/defaultE2ESetup";
|
|
6
|
+
export * from "./setup/setupHarRecording";
|
package/index.cjs.default.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
exports._default = require('./index.cjs.js').default;
|
package/index.cjs.mjs
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { nxE2EPreset } from "@nx/cypress/plugins/cypress-preset";
|
|
2
|
-
/**
|
|
3
|
-
* Creates the standard NX E2E preset configuration for Cypress.
|
|
4
|
-
* This wraps the @nx/cypress preset with our default Vite configuration.
|
|
5
|
-
*
|
|
6
|
-
* @param filename - Pass `__filename` from the calling cypress.config.ts
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* import { createNxPreset, CypressPluginConfig, defaultCypressConfig, setupPlugins } from "@trackunit/iris-app-e2e";
|
|
10
|
-
* import { defineConfig } from "cypress";
|
|
11
|
-
*
|
|
12
|
-
* const nxPreset = createNxPreset(__filename);
|
|
13
|
-
*
|
|
14
|
-
* export default defineConfig({
|
|
15
|
-
* chromeWebSecurity: false,
|
|
16
|
-
* e2e: {
|
|
17
|
-
* ...nxPreset,
|
|
18
|
-
* ...defaultCypressConfig(__dirname),
|
|
19
|
-
* async setupNodeEvents(on, config: CypressPluginConfig) {
|
|
20
|
-
* await nxPreset.setupNodeEvents?.(on, config);
|
|
21
|
-
* return setupPlugins(on, config, formatter, installHarGenerator);
|
|
22
|
-
* },
|
|
23
|
-
* },
|
|
24
|
-
* });
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
export declare function createNxPreset(filename: string): ReturnType<typeof nxE2EPreset>;
|
package/src/support.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser-only exports for Cypress support files.
|
|
3
|
-
* These modules depend on the Cypress global and must only be imported
|
|
4
|
-
* in Cypress support files, NOT in cypress.config.ts or other Node.js contexts.
|
|
5
|
-
*/
|
|
6
|
-
export * from "./commands/defaultCommands";
|
|
7
|
-
export * from "./setup/defaultE2ESetup";
|
|
8
|
-
export * from "./setup/setupHarRecording";
|