@trackunit/iris-app-e2e 1.8.89 → 1.8.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +2511 -0
  2. package/generators.json +12 -0
  3. package/package.json +19 -14
  4. package/src/commands/defaultCommands.js +135 -0
  5. package/src/commands/defaultCommands.js.map +1 -0
  6. package/src/generators/e2e-configuration/README.md +79 -0
  7. package/src/generators/e2e-configuration/files/root/cypress/e2e/app.cy.ts__tmpl__ +18 -0
  8. package/src/generators/e2e-configuration/files/root/cypress/fixtures/auth.json__tmpl__ +4 -0
  9. package/src/generators/e2e-configuration/files/root/cypress/support/commands.ts__tmpl__ +3 -0
  10. package/src/generators/e2e-configuration/files/root/cypress/support/e2e.ts__tmpl__ +18 -0
  11. package/src/generators/e2e-configuration/files/root/cypress/tsconfig.json__tmpl__ +17 -0
  12. package/src/generators/e2e-configuration/files/root/cypress.config.ts__tmpl__ +25 -0
  13. package/src/generators/e2e-configuration/generator.js +238 -0
  14. package/src/generators/e2e-configuration/generator.js.map +1 -0
  15. package/src/generators/e2e-configuration/schema.json +20 -0
  16. package/src/index.js +16 -0
  17. package/src/index.js.map +1 -0
  18. package/src/plugins/createLogFile.js +42 -0
  19. package/src/plugins/createLogFile.js.map +1 -0
  20. package/src/plugins/defaultPlugins.js +142 -0
  21. package/src/plugins/defaultPlugins.js.map +1 -0
  22. package/src/plugins/nxPreset.js +41 -0
  23. package/src/plugins/nxPreset.js.map +1 -0
  24. package/src/plugins/writeFileWithPrettier.js +35 -0
  25. package/src/plugins/writeFileWithPrettier.js.map +1 -0
  26. package/src/setup/defaultE2ESetup.js +45 -0
  27. package/src/setup/defaultE2ESetup.js.map +1 -0
  28. package/src/setup/setupHarRecording.js +24 -0
  29. package/src/setup/setupHarRecording.js.map +1 -0
  30. package/src/support.js +13 -0
  31. package/src/support.js.map +1 -0
  32. package/src/utils/Codeowner.js +136 -0
  33. package/src/utils/Codeowner.js.map +1 -0
  34. package/src/utils/fileNameBuilder.js +38 -0
  35. package/src/utils/fileNameBuilder.js.map +1 -0
  36. package/src/utils/fileUpdater.js +37 -0
  37. package/src/utils/fileUpdater.js.map +1 -0
  38. package/index.cjs.default.js +0 -1
  39. package/index.cjs.js +0 -422
  40. package/index.cjs.mjs +0 -2
  41. package/index.d.ts +0 -1
  42. package/index.esm.js +0 -398
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "name": "iris-app-e2e",
4
+ "version": "1.0.3",
5
+ "generators": {
6
+ "e2e-configuration": {
7
+ "factory": "./src/generators/e2e-configuration/generator#e2eConfigurationGenerator",
8
+ "schema": "./src/generators/e2e-configuration/schema.json",
9
+ "description": "e2e configuration setup for Iris app projects"
10
+ }
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,12 +1,27 @@
1
1
  {
2
2
  "name": "@trackunit/iris-app-e2e",
3
- "version": "1.8.89",
3
+ "version": "1.8.91",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "generators": "./generators.json",
7
7
  "engines": {
8
8
  "node": ">=24.x"
9
9
  },
10
+ "main": "./src/index.js",
11
+ "types": "./src/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./src/index.d.ts",
15
+ "require": "./src/index.js",
16
+ "import": "./src/index.js"
17
+ },
18
+ "./support": {
19
+ "types": "./src/support.d.ts",
20
+ "require": "./src/support.js",
21
+ "import": "./src/support.js"
22
+ },
23
+ "./package.json": "./package.json"
24
+ },
10
25
  "dependencies": {
11
26
  "@neuralegion/cypress-har-generator": "^5.17.0",
12
27
  "@nx/cypress": "22.4.4",
@@ -14,18 +29,8 @@
14
29
  "@nx/vite": "22.4.4",
15
30
  "cypress-terminal-report": "7.0.3",
16
31
  "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
- }
32
+ "prettier": "^3.4.2",
33
+ "tslib": "^2.6.2"
27
34
  },
28
- "module": "./index.esm.js",
29
- "main": "./index.cjs.js",
30
- "types": "./index.d.ts"
35
+ "type": "commonjs"
31
36
  }
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupDefaultCommands = setupDefaultCommands;
4
+ /**
5
+ * Sets up default Cypress commands for E2E testing.
6
+ * Adds custom commands like getByTestId, login, enterIrisApp, etc.
7
+ */
8
+ function setupDefaultCommands() {
9
+ Cypress.Commands.add("getByTestId", {
10
+ prevSubject: ["optional"],
11
+ },
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ (subject, testId, options = {}) => {
14
+ const selector = `[data-testid="${testId}"]`;
15
+ const timeout = options.timeout ?? 15000;
16
+ if (subject) {
17
+ if (Cypress.dom.isElement(subject) || Cypress.dom.isJquery(subject)) {
18
+ return cy.wrap(subject, { timeout }).find(selector, { timeout });
19
+ }
20
+ else if (Cypress.dom.isWindow(subject)) {
21
+ return cy.get(selector, { timeout });
22
+ }
23
+ else if (Array.isArray(subject)) {
24
+ const element = subject.map(el => cy.wrap(el, { timeout }).find(selector, { timeout }));
25
+ if (element[0]) {
26
+ return element[0];
27
+ }
28
+ return cy.wrap(null);
29
+ }
30
+ return cy.get(selector, { timeout });
31
+ }
32
+ else {
33
+ return cy.get(selector, { timeout });
34
+ }
35
+ });
36
+ Cypress.Commands.add("login", fixture => {
37
+ const envUrl = `${Cypress.config().baseUrl}/env`;
38
+ cy.log(`Getting env from: ${envUrl}`);
39
+ cy.request({
40
+ method: "GET",
41
+ url: envUrl,
42
+ headers: {
43
+ "Content-Type": "application/json",
44
+ },
45
+ }).then(envResponse => {
46
+ const env = envResponse.body;
47
+ const domain = env.auth?.url;
48
+ if (!(Boolean(domain))) {
49
+ throw new Error(`No domain found from servers /env found env: ${JSON.stringify(env)}`);
50
+ }
51
+ cy.log(`Using: ${domain}`);
52
+ cy.clearCookies();
53
+ cy.fixture(fixture ?? "auth").then(({ username, password }) => {
54
+ const options = {
55
+ warnBeforePasswordExpired: true,
56
+ multiOptionalFactorEnroll: false,
57
+ };
58
+ cy.request({
59
+ method: "POST",
60
+ url: `${domain}/api/v1/authn`,
61
+ headers: {
62
+ "Content-Type": "application/json",
63
+ },
64
+ body: {
65
+ username,
66
+ password,
67
+ options,
68
+ },
69
+ })
70
+ .then(response => {
71
+ if (response.isOkStatusCode) {
72
+ const sessionToken = response.body.sessionToken;
73
+ return cy.visit(`/auth/manager-classic#session_token=${sessionToken}&fleetHome=true`);
74
+ }
75
+ else {
76
+ throw new Error(`Could not get a session token for user: ${username}, ${JSON.stringify(response)}`);
77
+ }
78
+ })
79
+ .then(() => cy.url({ timeout: 20000 }).should("not.include", "manager-classic")) // Wait for redirect away from manager-classic
80
+ .url()
81
+ .should("contain", `${Cypress.config().baseUrl}`);
82
+ cy.get("#host-layout-content", { timeout: 15000 }).should("be.visible"); // Wait for host layout to be visible
83
+ });
84
+ });
85
+ });
86
+ Cypress.Commands.add("switchToLocalDevMode", () => {
87
+ cy.getByTestId("developerPortalNav").click();
88
+ cy.url({ timeout: 15000, log: true }).should("contain", "/iris-sdk-portal");
89
+ //eslint-disable-next-line @typescript-eslint/no-explicit-any
90
+ cy.getByTestId("localDevModeSwitch-input").then(($ele) => {
91
+ if ($ele && !$ele.is(":checked")) {
92
+ cy.getByTestId("localDevModeSwitch-thumb").click();
93
+ }
94
+ });
95
+ });
96
+ Cypress.Commands.add("enterIrisApp", options => {
97
+ return cy
98
+ .get(`iframe[data-testid="${options?.testId ?? "app-iframe"}"]`, { timeout: 30000 })
99
+ .first()
100
+ .its("0.contentDocument.body", { timeout: 30000, log: true })
101
+ .should("not.be.empty")
102
+ .then($body => {
103
+ return () => cy.wrap($body);
104
+ });
105
+ });
106
+ Cypress.Commands.add("enterStorybookPreview", options => {
107
+ return cy
108
+ .get(`iframe[id="${options?.testId ?? "storybook-preview-iframe"}"]`, { timeout: 30000 })
109
+ .first()
110
+ .its("0.contentDocument.body", { timeout: 30000, log: true })
111
+ .should("not.be.empty")
112
+ .then($body => {
113
+ return () => cy.wrap($body);
114
+ });
115
+ });
116
+ Cypress.Commands.add("getValidateFeatureFlags", () => {
117
+ cy.intercept({ url: "**/ValidateFeatureFlags" }).as("ValidateFeatureFlags");
118
+ cy.intercept({ url: "**/UserPermissions" }).as("UserPermissions");
119
+ cy.intercept({ url: "**/ActiveSubscription" }).as("ActiveSubscription");
120
+ });
121
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
+ let ccData = null;
123
+ Cypress.Commands.add("configCat", value => {
124
+ if (!ccData) {
125
+ cy.wait("@ValidateFeatureFlags").then(intercept => {
126
+ ccData = intercept.response?.body?.data?.featureFlags;
127
+ return ccData?.find((ff) => ff.key === value).state;
128
+ });
129
+ }
130
+ else {
131
+ return ccData?.find((ff) => ff.key === value).state;
132
+ }
133
+ });
134
+ }
135
+ //# sourceMappingURL=defaultCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultCommands.js","sourceRoot":"","sources":["../../../../../../libs/iris-app-sdk/iris-app-e2e/src/commands/defaultCommands.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Sets up default Cypress commands for E2E testing.\n * Adds custom commands like getByTestId, login, enterIrisApp, etc.\n */\nexport function setupDefaultCommands() {\n Cypress.Commands.add(\n \"getByTestId\",\n {\n prevSubject: [\"optional\"],\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (subject: any, testId: string, options: { timeout?: number } = {}) => {\n const selector = `[data-testid=\"${testId}\"]`;\n const timeout = options.timeout ?? 15000;\n\n if (subject) {\n if (Cypress.dom.isElement(subject) || Cypress.dom.isJquery(subject)) {\n return cy.wrap(subject, { timeout }).find(selector, { timeout });\n } else if (Cypress.dom.isWindow(subject)) {\n return cy.get(selector, { timeout });\n } else if (Array.isArray(subject)) {\n const element = subject.map(el => cy.wrap(el, { timeout }).find(selector, { timeout }));\n if (element[0]) {\n return element[0];\n }\n return cy.wrap(null);\n }\n return cy.get(selector, { timeout });\n } else {\n return cy.get(selector, { timeout });\n }\n }\n );\n\n Cypress.Commands.add(\"login\", fixture => {\n const envUrl = `${Cypress.config().baseUrl}/env`;\n cy.log(`Getting env from: ${envUrl}`);\n cy.request({\n method: \"GET\",\n url: envUrl,\n headers: {\n \"Content-Type\": \"application/json\",\n },\n }).then(envResponse => {\n const env = envResponse.body;\n const domain = env.auth?.url;\n if (!(Boolean(domain))) {\n throw new Error(`No domain found from servers /env found env: ${JSON.stringify(env)}`);\n }\n cy.log(`Using: ${domain}`);\n cy.clearCookies();\n cy.fixture(fixture ?? \"auth\").then(({ username, password }) => {\n const options = {\n warnBeforePasswordExpired: true,\n multiOptionalFactorEnroll: false,\n };\n\n cy.request({\n method: \"POST\",\n url: `${domain}/api/v1/authn`,\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: {\n username,\n password,\n options,\n },\n })\n .then(response => {\n if (response.isOkStatusCode) {\n const sessionToken = response.body.sessionToken;\n\n return cy.visit(`/auth/manager-classic#session_token=${sessionToken}&fleetHome=true`);\n } else {\n throw new Error(`Could not get a session token for user: ${username}, ${JSON.stringify(response)}`);\n }\n })\n .then(() => cy.url({ timeout: 20000 }).should(\"not.include\", \"manager-classic\")) // Wait for redirect away from manager-classic\n .url()\n .should(\"contain\", `${Cypress.config().baseUrl}`);\n cy.get(\"#host-layout-content\", { timeout: 15000 }).should(\"be.visible\"); // Wait for host layout to be visible\n });\n });\n });\n\n Cypress.Commands.add(\"switchToLocalDevMode\", () => {\n cy.getByTestId(\"developerPortalNav\").click();\n cy.url({ timeout: 15000, log: true }).should(\"contain\", \"/iris-sdk-portal\");\n\n //eslint-disable-next-line @typescript-eslint/no-explicit-any\n cy.getByTestId(\"localDevModeSwitch-input\").then(($ele: any) => {\n if ($ele && !$ele.is(\":checked\")) {\n cy.getByTestId(\"localDevModeSwitch-thumb\").click();\n }\n });\n });\n\n Cypress.Commands.add(\"enterIrisApp\", options => {\n return cy\n .get(`iframe[data-testid=\"${options?.testId ?? \"app-iframe\"}\"]`, { timeout: 30000 })\n .first()\n .its(\"0.contentDocument.body\", { timeout: 30000, log: true })\n .should(\"not.be.empty\")\n .then($body => {\n return () => cy.wrap($body);\n });\n });\n\n Cypress.Commands.add(\"enterStorybookPreview\", options => {\n return cy\n .get(`iframe[id=\"${options?.testId ?? \"storybook-preview-iframe\"}\"]`, { timeout: 30000 })\n .first()\n .its(\"0.contentDocument.body\", { timeout: 30000, log: true })\n .should(\"not.be.empty\")\n .then($body => {\n return () => cy.wrap($body);\n });\n });\n\n Cypress.Commands.add(\"getValidateFeatureFlags\", () => {\n cy.intercept({ url: \"**/ValidateFeatureFlags\" }).as(\"ValidateFeatureFlags\");\n cy.intercept({ url: \"**/UserPermissions\" }).as(\"UserPermissions\");\n cy.intercept({ url: \"**/ActiveSubscription\" }).as(\"ActiveSubscription\");\n });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let ccData: any = null;\n Cypress.Commands.add(\"configCat\", value => {\n if (!ccData) {\n cy.wait(\"@ValidateFeatureFlags\").then(intercept => {\n ccData = intercept.response?.body?.data?.featureFlags;\n return ccData?.find((ff: { key: string }) => ff.key === value).state;\n });\n } else {\n return ccData?.find((ff: { key: string }) => ff.key === value).state;\n }\n });\n}\n"]}
@@ -0,0 +1,79 @@
1
+
2
+ # E2E Configuration Generator
3
+
4
+ This generator sets up Cypress E2E test configuration within an existing project
5
+ using the Iris App E2E library, rather than creating a separate E2E app.
6
+
7
+ ## Usage
8
+
9
+ You can run the generator with or without explicitly specifying a project name.
10
+
11
+ ### 1. With the project name
12
+
13
+ Use this option to directly target a specific project for the E2E setup.
14
+
15
+ ```bash
16
+ nx generate @trackunit/iris-app-e2e:e2e-configuration --project <PROJECT_NAME>
17
+ ```
18
+
19
+ ### 2. Without the project name
20
+
21
+ This option presents a list of available projects to select from.
22
+
23
+ ```bash
24
+ nx generate @trackunit/iris-app-e2e:e2e-configuration
25
+ ```
26
+
27
+ ## What it generates
28
+
29
+ - **cypress.config.ts** - Main Cypress configuration using Iris App E2E library
30
+ - **cypress/e2e/app.cy.ts** - Sample test file with basic structure
31
+ - **cypress/support/commands.ts** - Custom commands setup using public API
32
+ - **cypress/support/e2e.ts** - Support file configuration
33
+ - **cypress/fixtures/auth.json** - Authentication fixture template
34
+ - **Project targets** for e2e and e2e-ui execution with multiple environment configurations
35
+
36
+ ## Prerequisites
37
+
38
+ - The target application needs to be deployed and accessible for tests to run successfully
39
+ - Authentication credentials must be configured in the `cypress/fixtures/auth.json` file
40
+
41
+ ## Test User Considerations
42
+
43
+ When setting up E2E tests, consider the following for test user management:
44
+
45
+ ### Authentication Setup
46
+
47
+ - Configure test user credentials in `cypress/fixtures/auth.json`
48
+ - Use dedicated test accounts separate from production users
49
+ - Ensure test users have appropriate permissions for the features being tested
50
+
51
+ ### User Context Planning
52
+
53
+ - **Basic Access**: Use standard user accounts for general functionality testing
54
+ - **Administrative Access**: Use admin-level accounts when testing administrative features
55
+ - **Role-based Testing**: Consider different user roles if your application has role-based access control
56
+ - **Environment Considerations**: Test users and permissions may vary across different environments (dev, staging, production)
57
+
58
+ ### Security Best Practices
59
+
60
+ - Regularly rotate test user passwords
61
+ - Ensure test users have minimal necessary permissions
62
+
63
+ ## Running Tests
64
+
65
+ After generation, you can run your E2E tests using:
66
+
67
+ ```bash
68
+ # Run tests in headless mode
69
+ nx run <project-name>:e2e
70
+
71
+ # Run tests with UI for development
72
+ nx run <project-name>:e2e-ui
73
+
74
+ # Run against different environments
75
+ nx run <project-name>:e2e --configuration=local
76
+ nx run <project-name>:e2e --configuration=dev
77
+ nx run <project-name>:e2e --configuration=stage
78
+ nx run <project-name>:e2e --configuration=prod
79
+ ```
@@ -0,0 +1,18 @@
1
+ describe("Sample E2E Test", () => {
2
+ beforeEach(() => {
3
+ // NEVER do anything in before or after as they are never timed out and retries will never happen
4
+ });
5
+
6
+ it("Can log in and navigate to app", () => {
7
+ cy.login("auth");
8
+
9
+ // Example: Test your application here
10
+ // cy.getByTestId("your-app-element").should("be.visible");
11
+
12
+ // Example: Test Iris app integration
13
+ // cy.enterIrisApp().then(irisApp => {
14
+ // irisApp().find(".your-selector").should("contain.text", "Expected Text");
15
+ // irisApp().getByTestId("your-test-id").should("exist");
16
+ // });
17
+ });
18
+ });
@@ -0,0 +1,4 @@
1
+ {
2
+ "username": "your-test-user@example.com",
3
+ "password": "your-test-password"
4
+ }
@@ -0,0 +1,3 @@
1
+ import { setupDefaultCommands } from "@trackunit/iris-app-e2e/support";
2
+ import "@testing-library/cypress/add-commands";
3
+ setupDefaultCommands();
@@ -0,0 +1,18 @@
1
+ // ***********************************************************
2
+ // This example support/e2e.ts is processed and
3
+ // loaded automatically before your test files.
4
+ //
5
+ // This is a great place to put global configuration and
6
+ // behavior that modifies Cypress.
7
+ //
8
+ // You can change the location of this file or turn off
9
+ // automatically serving support files with the
10
+ // 'supportFile' configuration option.
11
+ //
12
+ // You can read more here:
13
+ // https://on.cypress.io/configuration
14
+ // ***********************************************************
15
+
16
+ import { setupE2E } from "@trackunit/iris-app-e2e/support";
17
+ import "./commands";
18
+ setupE2E();
@@ -0,0 +1,17 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "types": ["cypress", "node", "@testing-library/cypress"],
5
+ "sourceMap": false
6
+ },
7
+ "include": [
8
+ "**/*.ts",
9
+ "**/*.js",
10
+ "../cypress.config.ts",
11
+ "./**/*.cy.ts",
12
+ "./**/*.cy.tsx",
13
+ "./**/*.cy.js",
14
+ "./**/*.cy.jsx",
15
+ "./**/*.d.ts"
16
+ ]
17
+ }
@@ -0,0 +1,25 @@
1
+ import { install as installHarGenerator } from "@neuralegion/cypress-har-generator";
2
+ import { createNxPreset, CypressPluginConfig, defaultCypressConfig, setupPlugins } from "@trackunit/iris-app-e2e";
3
+ import { defineConfig } from "cypress";
4
+ import { format, resolveConfig } from "prettier";
5
+
6
+ const nxPreset = createNxPreset(__filename);
7
+
8
+ const cypressJsonConfig: Partial<CypressPluginConfig> = {
9
+ ...defaultCypressConfig(__dirname),
10
+ fixturesFolder: "./cypress/fixtures",
11
+ supportFile: "cypress/support/e2e.ts",
12
+ specPattern: "cypress/e2e/**/*.cy.ts",
13
+ };
14
+ export default defineConfig({
15
+ chromeWebSecurity: false,
16
+ e2e: {
17
+ ...nxPreset,
18
+ ...cypressJsonConfig,
19
+ async setupNodeEvents(on, config: CypressPluginConfig) {
20
+ await nxPreset.setupNodeEvents?.(on, config);
21
+ const formatter = { format, resolveConfig };
22
+ return setupPlugins(on, config, formatter, installHarGenerator);
23
+ },
24
+ },
25
+ });
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.e2eConfigurationGenerator = e2eConfigurationGenerator;
4
+ const tslib_1 = require("tslib");
5
+ const configuration_1 = tslib_1.__importDefault(require("@nx/cypress/src/generators/configuration/configuration"));
6
+ const devkit_1 = require("@nx/devkit");
7
+ const fileUpdater_1 = require("../../utils/fileUpdater");
8
+ /**
9
+ * Generates E2E test configuration for an existing project using Iris App E2E library.
10
+ *
11
+ * This generator sets up Cypress end-to-end testing infrastructure within an existing project,
12
+ * including configuration files, test targets, and template files that use the public
13
+ * iris-app-e2e library for cross-platform compatibility.
14
+ *
15
+ * @param tree - The NX virtual file system tree
16
+ * @param options - Configuration options for the generator
17
+ * @param options.project - The name of the project to add E2E configuration to
18
+ * @returns {Function} A function that logs completion message with the path to the generated test file
19
+ * @example
20
+ * ```bash
21
+ * nx generate @trackunit/iris-app-e2e:e2e-configuration --project my-app
22
+ * ```
23
+ *
24
+ * Generated structure:
25
+ * - cypress.config.ts - Main Cypress configuration using Iris App E2E
26
+ * - cypress/e2e/app.cy.ts - Sample test file
27
+ * - cypress/support/commands.ts - Custom commands setup
28
+ * - cypress/support/e2e.ts - Support file configuration
29
+ * - cypress/fixtures/auth.json - Authentication fixture template
30
+ * - Project targets for e2e and e2e-ui execution with multiple environment configurations
31
+ */
32
+ async function e2eConfigurationGenerator(tree, options) {
33
+ const libName = options.project.replace(new RegExp("/", "g"), "-");
34
+ const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, libName);
35
+ const projectRoot = projectConfig.root; // Get the actual root of the library
36
+ const optionsWithDefaults = {
37
+ linter: "none",
38
+ project: options.project,
39
+ bundler: "vite",
40
+ directory: "cypress",
41
+ baseUrl: "https://dev.manager.trackunit.com",
42
+ };
43
+ // reading project.json content
44
+ const existingProjectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
45
+ // generate the cypress configuration files
46
+ await (0, configuration_1.default)(tree, optionsWithDefaults);
47
+ //adding cypress target to project.json
48
+ (0, devkit_1.updateProjectConfiguration)(tree, options.project, {
49
+ ...existingProjectConfig,
50
+ targets: {
51
+ ...existingProjectConfig.targets,
52
+ "e2e-ui": {
53
+ executor: "@nx/cypress:cypress",
54
+ options: {
55
+ cypressConfig: `${projectRoot}/cypress.config.ts`,
56
+ baseUrl: "https://dev.manager.trackunit.com",
57
+ headed: true,
58
+ watch: true,
59
+ testingType: "e2e",
60
+ },
61
+ configurations: {
62
+ local: {
63
+ baseUrl: "http://localhost:3000",
64
+ },
65
+ dev: {
66
+ baseUrl: "https://dev.manager.trackunit.com",
67
+ },
68
+ stage: {
69
+ baseUrl: "https://stage.manager.trackunit.com",
70
+ },
71
+ prod: {
72
+ baseUrl: "https://new.manager.trackunit.com",
73
+ },
74
+ },
75
+ },
76
+ e2e: {
77
+ executor: "@nx/cypress:cypress",
78
+ options: {
79
+ cypressConfig: `${projectRoot}/cypress.config.ts`,
80
+ browser: "chrome",
81
+ testingType: "e2e",
82
+ },
83
+ configurations: {
84
+ local: {
85
+ baseUrl: "http://localhost:3000",
86
+ },
87
+ dev: {
88
+ baseUrl: "https://dev.manager.trackunit.com",
89
+ },
90
+ stage: {
91
+ baseUrl: "https://stage.manager.trackunit.com",
92
+ },
93
+ prod: {
94
+ baseUrl: "https://new.manager.trackunit.com",
95
+ },
96
+ "feature-branch": {},
97
+ },
98
+ },
99
+ },
100
+ });
101
+ // overwrite the generated files with the custom files
102
+ (0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, "files/root"), projectRoot, {
103
+ ...options,
104
+ directory: "",
105
+ tmpl: "",
106
+ });
107
+ // Check if we're in the manager-side monorepo (has eslint-e2e.config.cjs at root)
108
+ const isInternalWorkspace = tree.exists("eslint-e2e.config.cjs");
109
+ const workspaceEslintConfigCjsPath = "eslint.config.cjs";
110
+ const workspaceEslintConfigMjsPath = "eslint.config.mjs";
111
+ const eslintConfigCjsPath = (0, devkit_1.joinPathFragments)(projectConfig.root, "eslint.config.cjs");
112
+ const eslintConfigMjsPath = (0, devkit_1.joinPathFragments)(projectConfig.root, "eslint.config.mjs");
113
+ if (isInternalWorkspace) {
114
+ // Manager-side monorepo: update existing eslint.config.cjs
115
+ (0, fileUpdater_1.updateFileInTree)(tree, eslintConfigCjsPath, _ => {
116
+ const hasPublishNpmTag = projectConfig.tags?.includes("publish:npm") ?? false;
117
+ const publicRulesImport = hasPublishNpmTag
118
+ ? `\nconst publicConfig = require("${(0, devkit_1.offsetFromRoot)(projectRoot)}eslint-public.config.cjs");`
119
+ : "";
120
+ const publicRulesSpread = hasPublishNpmTag ? ", ...publicConfig" : "";
121
+ return `const clientConfig = require("${(0, devkit_1.offsetFromRoot)(projectRoot)}eslint-client.config.cjs");
122
+ const e2eConfig = require("${(0, devkit_1.offsetFromRoot)(projectRoot)}eslint-e2e.config.cjs");
123
+ const { defineConfig } = require("eslint/config");${publicRulesImport}
124
+ module.exports = defineConfig([...clientConfig, ...e2eConfig${publicRulesSpread}]);
125
+ `;
126
+ });
127
+ }
128
+ else {
129
+ // External workspace: extend existing config or create minimal config with Cypress support
130
+ // Check for both .mjs and .cjs ESLint config files
131
+ const workspaceMjsContent = tree.exists(workspaceEslintConfigMjsPath)
132
+ ? tree.read(workspaceEslintConfigMjsPath, "utf-8") || ""
133
+ : "";
134
+ const workspaceCjsContent = tree.exists(workspaceEslintConfigCjsPath)
135
+ ? tree.read(workspaceEslintConfigCjsPath, "utf-8") || ""
136
+ : "";
137
+ const existingMjsContent = tree.exists(eslintConfigMjsPath) ? tree.read(eslintConfigMjsPath, "utf-8") || "" : "";
138
+ const existingCjsContent = tree.exists(eslintConfigCjsPath) ? tree.read(eslintConfigCjsPath, "utf-8") || "" : "";
139
+ const cypressConfigBlock = `{
140
+ files: ["cypress/**/*.ts", "cypress/**/*.js"],
141
+ languageOptions: {
142
+ globals: {
143
+ cy: "readonly",
144
+ Cypress: "readonly",
145
+ describe: "readonly",
146
+ it: "readonly",
147
+ beforeEach: "readonly",
148
+ afterEach: "readonly",
149
+ before: "readonly",
150
+ after: "readonly",
151
+ expect: "readonly",
152
+ },
153
+ },
154
+ rules: {
155
+ "@typescript-eslint/no-namespace": "off",
156
+ },
157
+ }`;
158
+ if (existingMjsContent && !existingMjsContent.includes("cypress")) {
159
+ // Update existing .mjs config to add Cypress support
160
+ // Find the export default array and add cypress config to it
161
+ const updatedContent = existingMjsContent.replace(/export\s+default\s+(\[[\s\S]*?)(];?\s*$)/m, (_, arrayContent, closing) => {
162
+ const trimmedContent = arrayContent.trimEnd();
163
+ const needsComma = trimmedContent.endsWith("}") || trimmedContent.endsWith(")");
164
+ return `export default ${trimmedContent}${needsComma ? "," : ""}\n ${cypressConfigBlock}\n${closing}`;
165
+ });
166
+ tree.write(eslintConfigMjsPath, updatedContent);
167
+ }
168
+ else if (existingCjsContent && !existingCjsContent.includes("cypress")) {
169
+ // Update existing .cjs config to add Cypress support
170
+ const eslintContent = `const baseConfig = require("./eslint.config.base.cjs");
171
+
172
+ module.exports = [
173
+ ...(Array.isArray(baseConfig) ? baseConfig : [baseConfig]),
174
+ ${cypressConfigBlock},
175
+ ];
176
+ `;
177
+ // Backup the original config
178
+ tree.write((0, devkit_1.joinPathFragments)(projectConfig.root, "eslint.config.base.cjs"), existingCjsContent);
179
+ tree.write(eslintConfigCjsPath, eslintContent);
180
+ }
181
+ else if (!existingMjsContent && !existingCjsContent) {
182
+ // No project-level config - compose with the workspace base config when available.
183
+ if (workspaceCjsContent) {
184
+ const eslintContent = `const { defineConfig } = require("eslint/config");
185
+ const baseConfig = require("${(0, devkit_1.offsetFromRoot)(projectRoot)}eslint.config.cjs");
186
+
187
+ module.exports = defineConfig([
188
+ ...(Array.isArray(baseConfig) ? baseConfig : [baseConfig]),
189
+ ${cypressConfigBlock},
190
+ ]);
191
+ `;
192
+ tree.write(eslintConfigCjsPath, eslintContent);
193
+ }
194
+ else if (workspaceMjsContent) {
195
+ const eslintContent = `import baseConfig from "${(0, devkit_1.offsetFromRoot)(projectRoot)}eslint.config.mjs";
196
+
197
+ export default [
198
+ ...(Array.isArray(baseConfig) ? baseConfig : [baseConfig]),
199
+ ${cypressConfigBlock},
200
+ ];
201
+ `;
202
+ tree.write(eslintConfigMjsPath, eslintContent);
203
+ }
204
+ else {
205
+ // Final fallback for workspaces without any ESLint setup: bootstrap the same Nx React flat
206
+ // config shape our app generators start from, then layer Cypress overrides on top.
207
+ const eslintContent = `const nx = require("@nx/eslint-plugin");
208
+ const { defineConfig } = require("eslint/config");
209
+
210
+ module.exports = defineConfig([
211
+ ...nx.configs["flat/react"],
212
+ {
213
+ files: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
214
+ languageOptions: {
215
+ parserOptions: { project: ["./tsconfig.*?.json"] },
216
+ },
217
+ rules: {},
218
+ },
219
+ {
220
+ ignores: ["node_modules/**", "dist/**"],
221
+ },
222
+ ${cypressConfigBlock},
223
+ ]);
224
+ `;
225
+ tree.write(eslintConfigCjsPath, eslintContent);
226
+ }
227
+ }
228
+ // If eslintConfig already includes cypress, don't modify it
229
+ }
230
+ (0, fileUpdater_1.deleteFileInTree)(tree, (0, devkit_1.joinPathFragments)(projectConfig.root, "cypress/support/app.po.ts"));
231
+ await (0, devkit_1.formatFiles)(tree);
232
+ return () => {
233
+ // eslint-disable-next-line no-console
234
+ console.log("\n\n\nāœ… šŸš€ Please open this file to get started\n", (0, devkit_1.joinPathFragments)(projectConfig.root, "cypress/e2e/app.cy.ts"));
235
+ return tree;
236
+ };
237
+ }
238
+ //# sourceMappingURL=generator.js.map