@trackunit/iris-app-playwright 0.1.1

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 (60) hide show
  1. package/README.md +230 -0
  2. package/generators.json +12 -0
  3. package/package.json +43 -0
  4. package/src/fixtures/baseTest.d.ts +9 -0
  5. package/src/fixtures/baseTest.js +12 -0
  6. package/src/fixtures/describe.d.ts +11 -0
  7. package/src/fixtures/describe.js +23 -0
  8. package/src/fixtures/featureFlags.fixture.d.ts +19 -0
  9. package/src/fixtures/featureFlags.fixture.js +76 -0
  10. package/src/fixtures/harRecording.fixture.d.ts +12 -0
  11. package/src/fixtures/harRecording.fixture.js +35 -0
  12. package/src/fixtures/hydrateStorageState.d.ts +36 -0
  13. package/src/fixtures/hydrateStorageState.js +43 -0
  14. package/src/fixtures/irisApp.fixture.d.ts +15 -0
  15. package/src/fixtures/irisApp.fixture.js +13 -0
  16. package/src/fixtures/localDevMode.fixture.d.ts +5 -0
  17. package/src/fixtures/localDevMode.fixture.js +17 -0
  18. package/src/fixtures/login.fixture.d.ts +24 -0
  19. package/src/fixtures/login.fixture.js +95 -0
  20. package/src/fixtures/loginCacheKey.d.ts +25 -0
  21. package/src/fixtures/loginCacheKey.js +29 -0
  22. package/src/fixtures/resolveCredentials.d.ts +14 -0
  23. package/src/fixtures/resolveCredentials.js +49 -0
  24. package/src/generators/playwright-configuration/files/root/playwright/fixtures/auth.json__tmpl__ +4 -0
  25. package/src/generators/playwright-configuration/files/root/playwright/support/fixtures.ts__tmpl__ +1 -0
  26. package/src/generators/playwright-configuration/files/root/playwright/tests/app.spec.ts__tmpl__ +16 -0
  27. package/src/generators/playwright-configuration/files/root/playwright/tsconfig.json__tmpl__ +15 -0
  28. package/src/generators/playwright-configuration/files/root/playwright.config.ts__tmpl__ +13 -0
  29. package/src/generators/playwright-configuration/generator.d.ts +18 -0
  30. package/src/generators/playwright-configuration/generator.js +77 -0
  31. package/src/generators/playwright-configuration/schema.d.ts +3 -0
  32. package/src/generators/playwright-configuration/schema.json +18 -0
  33. package/src/index.d.ts +15 -0
  34. package/src/index.js +14 -0
  35. package/src/plugins/createLogFile.d.ts +24 -0
  36. package/src/plugins/createLogFile.js +51 -0
  37. package/src/plugins/defaultPlaywrightConfig.d.ts +68 -0
  38. package/src/plugins/defaultPlaywrightConfig.js +150 -0
  39. package/src/plugins/logsReporter.d.ts +55 -0
  40. package/src/plugins/logsReporter.js +119 -0
  41. package/src/plugins/nxPreset.d.ts +9 -0
  42. package/src/plugins/nxPreset.js +17 -0
  43. package/src/plugins/redactHar.d.ts +17 -0
  44. package/src/plugins/redactHar.js +138 -0
  45. package/src/plugins/redactSensitive.d.ts +31 -0
  46. package/src/plugins/redactSensitive.js +72 -0
  47. package/src/plugins/redactTrace.d.ts +12 -0
  48. package/src/plugins/redactTrace.js +48 -0
  49. package/src/plugins/setupPlugins.d.ts +11 -0
  50. package/src/plugins/setupPlugins.js +68 -0
  51. package/src/plugins/writeFileWithPrettier.d.ts +17 -0
  52. package/src/plugins/writeFileWithPrettier.js +37 -0
  53. package/src/support.d.ts +2 -0
  54. package/src/support.js +6 -0
  55. package/src/utils/Codeowner.d.ts +26 -0
  56. package/src/utils/Codeowner.js +89 -0
  57. package/src/utils/fileNameBuilder.d.ts +18 -0
  58. package/src/utils/fileNameBuilder.js +32 -0
  59. package/src/utils/fileUpdater.d.ts +16 -0
  60. package/src/utils/fileUpdater.js +38 -0
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.expect = exports.test = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ const hydrateStorageState_1 = require("./hydrateStorageState");
6
+ const loginCacheKey_1 = require("./loginCacheKey");
7
+ const resolveCredentials_1 = require("./resolveCredentials");
8
+ /**
9
+ * Each worker is its own Node process, so a `Map` is sufficient — no
10
+ * cross-worker sharing concerns, no disk I/O, no cleanup needed. The cache
11
+ * dies with the worker, which is the desired behaviour: a fresh `nx run`
12
+ * always starts with empty caches.
13
+ */
14
+ exports.test = test_1.test.extend({
15
+ _loginCache: [
16
+ async ({}, provide) => {
17
+ await provide(new Map());
18
+ },
19
+ { scope: "worker" },
20
+ ],
21
+ login: async ({ page, baseURL, _loginCache }, provide) => {
22
+ const loginFn = async (options) => {
23
+ const credentials = (0, resolveCredentials_1.resolveCredentials)(options);
24
+ const cacheKey = (0, loginCacheKey_1.buildLoginCacheKey)(baseURL, credentials);
25
+ const cached = _loginCache.get(cacheKey);
26
+ if (cached !== undefined) {
27
+ try {
28
+ await (0, hydrateStorageState_1.hydrateStorageState)(page.context(), cached);
29
+ await page.goto("/");
30
+ await page.locator("#host-layout-content").waitFor({ state: "visible", timeout: 15000 });
31
+ return;
32
+ }
33
+ catch {
34
+ _loginCache.delete(cacheKey);
35
+ }
36
+ }
37
+ await performOktaLogin({ page, baseURL, credentials });
38
+ // Capture the fully-authenticated state for reuse by later tests in
39
+ // this worker. Errors here would mask the actual login success, so we
40
+ // never let snapshot capture throw — at worst we'll re-hit Okta next
41
+ // time, which is the pre-cache behaviour.
42
+ try {
43
+ const snapshot = await page.context().storageState();
44
+ _loginCache.set(cacheKey, snapshot);
45
+ }
46
+ catch {
47
+ /* Snapshot capture is best-effort; failing here is non-fatal. */
48
+ }
49
+ };
50
+ await provide(loginFn);
51
+ },
52
+ });
53
+ /**
54
+ * Performs the slow path: hit Okta's `/api/v1/authn`, redeem the
55
+ * `sessionToken` via the manager-classic redirect, and wait for the host
56
+ * layout to render. Factored out so the cache hit/miss branches read clearly.
57
+ */
58
+ const performOktaLogin = async ({ page, baseURL, credentials, }) => {
59
+ const context = await test_1.request.newContext({ baseURL });
60
+ try {
61
+ const envResponse = await context.get("/env");
62
+ const env = await envResponse.json();
63
+ const domainValue = env.auth?.url;
64
+ if (typeof domainValue !== "string" || domainValue === "") {
65
+ throw new Error(`No domain found from servers /env found env: ${JSON.stringify(env)}`);
66
+ }
67
+ const domain = domainValue;
68
+ const authResponse = await context.post(`${domain}/api/v1/authn`, {
69
+ data: {
70
+ username: credentials.username,
71
+ password: credentials.password,
72
+ options: {
73
+ warnBeforePasswordExpired: true,
74
+ multiOptionalFactorEnroll: false,
75
+ },
76
+ },
77
+ headers: { "Content-Type": "application/json" },
78
+ timeout: 20000,
79
+ });
80
+ if (!authResponse.ok()) {
81
+ const body = await authResponse.json().catch(() => ({}));
82
+ throw new Error(`Could not get a session token for user: ${credentials.username}, ${JSON.stringify(body)}`);
83
+ }
84
+ const { sessionToken } = await authResponse.json();
85
+ await page.goto(`/auth/manager-classic#session_token=${sessionToken}&userProfile=true`);
86
+ await page.waitForURL(url => !url.href.includes("manager-classic"), { timeout: 20000 });
87
+ await page.locator("#host-layout-content").waitFor({ state: "visible", timeout: 15000 });
88
+ }
89
+ finally {
90
+ await context.dispose();
91
+ }
92
+ };
93
+ var test_2 = require("@playwright/test");
94
+ Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return test_2.expect; } });
95
+ //# sourceMappingURL=login.fixture.js.map
@@ -0,0 +1,25 @@
1
+ import type { AuthCredentials } from "./resolveCredentials";
2
+ /**
3
+ * Builds the lookup key used by the worker-scoped login cache.
4
+ *
5
+ * The cache reuses an authenticated `storageState` snapshot across every test
6
+ * within a Playwright worker that logs in as the same `(baseURL, username)`
7
+ * pair. Two principles drive the key shape:
8
+ *
9
+ * 1. **Username, not username+password.** Different passwords for the same
10
+ * user are almost always a credential rotation, not a deliberate second
11
+ * identity — if a test really needs different identities it will use a
12
+ * different `username`. Including the password would make a typo silently
13
+ * cache-miss instead of failing loudly, and would also serialise it into
14
+ * the key string. Kept out for both correctness and minor hygiene.
15
+ * 2. **`baseURL` is required.** Okta sessions are domain-bound (the auth
16
+ * cookies are set on the manager origin), so a snapshot from
17
+ * `https://dev.manager.trackunit.com` cannot authenticate a request to
18
+ * `https://stage.manager.trackunit.com`. We always partition by baseURL.
19
+ *
20
+ * `JSON.stringify` over a fixed-shape object guarantees no collision between
21
+ * (undefined baseURL, username "X") and (baseURL "X", undefined-equivalent
22
+ * username) — `null` and `"X"` serialise distinctly, and the field names
23
+ * anchor each value to its slot.
24
+ */
25
+ export declare const buildLoginCacheKey: (baseURL: string | undefined, credentials: AuthCredentials) => string;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildLoginCacheKey = void 0;
4
+ /**
5
+ * Builds the lookup key used by the worker-scoped login cache.
6
+ *
7
+ * The cache reuses an authenticated `storageState` snapshot across every test
8
+ * within a Playwright worker that logs in as the same `(baseURL, username)`
9
+ * pair. Two principles drive the key shape:
10
+ *
11
+ * 1. **Username, not username+password.** Different passwords for the same
12
+ * user are almost always a credential rotation, not a deliberate second
13
+ * identity — if a test really needs different identities it will use a
14
+ * different `username`. Including the password would make a typo silently
15
+ * cache-miss instead of failing loudly, and would also serialise it into
16
+ * the key string. Kept out for both correctness and minor hygiene.
17
+ * 2. **`baseURL` is required.** Okta sessions are domain-bound (the auth
18
+ * cookies are set on the manager origin), so a snapshot from
19
+ * `https://dev.manager.trackunit.com` cannot authenticate a request to
20
+ * `https://stage.manager.trackunit.com`. We always partition by baseURL.
21
+ *
22
+ * `JSON.stringify` over a fixed-shape object guarantees no collision between
23
+ * (undefined baseURL, username "X") and (baseURL "X", undefined-equivalent
24
+ * username) — `null` and `"X"` serialise distinctly, and the field names
25
+ * anchor each value to its slot.
26
+ */
27
+ const buildLoginCacheKey = (baseURL, credentials) => JSON.stringify({ baseURL: baseURL ?? null, username: credentials.username });
28
+ exports.buildLoginCacheKey = buildLoginCacheKey;
29
+ //# sourceMappingURL=loginCacheKey.js.map
@@ -0,0 +1,14 @@
1
+ export interface AuthCredentials {
2
+ username: string;
3
+ password: string;
4
+ }
5
+ export interface ResolveCredentialsOptions {
6
+ credentials?: AuthCredentials;
7
+ /** Path to a JSON file with `{ username, password }`. Defaults to `playwright/fixtures/auth.json` relative to cwd. */
8
+ fixturePath?: string;
9
+ }
10
+ /**
11
+ * Resolves auth credentials from (in order): explicit options, JSON fixture file, env vars.
12
+ * Throws if none yield a complete credential pair.
13
+ */
14
+ export declare const resolveCredentials: (options?: ResolveCredentialsOptions) => AuthCredentials;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveCredentials = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs_1 = require("fs");
6
+ const path = tslib_1.__importStar(require("path"));
7
+ const isAuthCredentials = (value) => {
8
+ if (typeof value !== "object" || value === null) {
9
+ return false;
10
+ }
11
+ const record = { ...value };
12
+ return typeof record.username === "string" && typeof record.password === "string";
13
+ };
14
+ const readFixtureCredentials = (fixturePath) => {
15
+ if (!(0, fs_1.existsSync)(fixturePath)) {
16
+ return undefined;
17
+ }
18
+ const parsed = JSON.parse((0, fs_1.readFileSync)(fixturePath, "utf-8"));
19
+ if (!isAuthCredentials(parsed)) {
20
+ return undefined;
21
+ }
22
+ if (parsed.username === "REPLACE_ME" || parsed.password === "REPLACE_ME") {
23
+ return undefined;
24
+ }
25
+ return parsed;
26
+ };
27
+ /**
28
+ * Resolves auth credentials from (in order): explicit options, JSON fixture file, env vars.
29
+ * Throws if none yield a complete credential pair.
30
+ */
31
+ const resolveCredentials = (options) => {
32
+ if (options?.credentials !== undefined) {
33
+ return options.credentials;
34
+ }
35
+ const fixturePath = options?.fixturePath ?? path.join(process.cwd(), "playwright", "fixtures", "auth.json");
36
+ const fromFixture = readFixtureCredentials(fixturePath);
37
+ if (fromFixture !== undefined) {
38
+ return fromFixture;
39
+ }
40
+ const envUsername = process.env.PLAYWRIGHT_USERNAME;
41
+ const envPassword = process.env.PLAYWRIGHT_PASSWORD;
42
+ if (envUsername !== undefined && envUsername !== "" && envPassword !== undefined && envPassword !== "") {
43
+ return { username: envUsername, password: envPassword };
44
+ }
45
+ throw new Error(`No credentials provided to login(). Tried: options.credentials, ${fixturePath}, PLAYWRIGHT_USERNAME/PLAYWRIGHT_PASSWORD env vars. ` +
46
+ `Fill in playwright/fixtures/auth.json, set the env vars, or pass credentials to login().`);
47
+ };
48
+ exports.resolveCredentials = resolveCredentials;
49
+ //# sourceMappingURL=resolveCredentials.js.map
@@ -0,0 +1,4 @@
1
+ {
2
+ "username": "REPLACE_ME",
3
+ "password": "REPLACE_ME"
4
+ }
@@ -0,0 +1 @@
1
+ export { describe, expect, test } from "@trackunit/iris-app-playwright/support";
@@ -0,0 +1,16 @@
1
+ import { describe, expect, test } from "../support/fixtures";
2
+
3
+ describe("Sample E2E Test", () => {
4
+ test("Can log in and navigate to app", async ({ page, login }) => {
5
+ await login();
6
+
7
+ // Example: Test your application here
8
+ // await expect(page.getByTestId("your-app-element")).toBeVisible();
9
+
10
+ // Example: Test Iris app integration
11
+ // const app = irisApp();
12
+ // await expect(app.getByTestId("your-test-id")).toBeVisible();
13
+
14
+ await expect(page.locator("#host-layout-content")).toBeVisible();
15
+ });
16
+ });
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "types": ["node"],
5
+ "sourceMap": false
6
+ },
7
+ "include": [
8
+ "**/*.ts",
9
+ "**/*.js",
10
+ "../playwright.config.ts",
11
+ "./**/*.spec.ts",
12
+ "./**/*.spec.tsx",
13
+ "./**/*.d.ts"
14
+ ]
15
+ }
@@ -0,0 +1,13 @@
1
+ import { createNxPreset, defaultPlaywrightConfig, setupPlugins } from "@trackunit/iris-app-playwright";
2
+ import { format, resolveConfig } from "prettier";
3
+
4
+ const nxPreset = createNxPreset(__filename);
5
+ const config = defaultPlaywrightConfig({ configDir: __dirname, projectConfig: { testDir: "./playwright/tests" } });
6
+
7
+ export default setupPlugins(
8
+ {
9
+ ...nxPreset,
10
+ ...config,
11
+ },
12
+ { format, resolveConfig }
13
+ );
@@ -0,0 +1,18 @@
1
+ import { Tree } from "@nx/devkit";
2
+ import { PlaywrightConfigurationGeneratorSchema } from "./schema";
3
+ /**
4
+ * Generates Playwright E2E test configuration for an existing project.
5
+ *
6
+ * Sets up Playwright end-to-end testing infrastructure within an existing project,
7
+ * including configuration files, test targets, and template files that use the public
8
+ * iris-app-playwright library. Does not modify any existing Cypress configuration.
9
+ *
10
+ * @param tree - The NX virtual file system tree
11
+ * @param options - Configuration options for the generator
12
+ * @returns {Promise<() => void>} A function that logs completion message with the path to the generated test file
13
+ * @example
14
+ * ```bash
15
+ * nx generate @trackunit/iris-app-playwright:playwright-configuration --project my-app
16
+ * ```
17
+ */
18
+ export declare function playwrightConfigurationGenerator(tree: Tree, options: PlaywrightConfigurationGeneratorSchema): Promise<() => void>;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.playwrightConfigurationGenerator = playwrightConfigurationGenerator;
4
+ const devkit_1 = require("@nx/devkit");
5
+ /**
6
+ * Generates Playwright E2E test configuration for an existing project.
7
+ *
8
+ * Sets up Playwright end-to-end testing infrastructure within an existing project,
9
+ * including configuration files, test targets, and template files that use the public
10
+ * iris-app-playwright library. Does not modify any existing Cypress configuration.
11
+ *
12
+ * @param tree - The NX virtual file system tree
13
+ * @param options - Configuration options for the generator
14
+ * @returns {Promise<() => void>} A function that logs completion message with the path to the generated test file
15
+ * @example
16
+ * ```bash
17
+ * nx generate @trackunit/iris-app-playwright:playwright-configuration --project my-app
18
+ * ```
19
+ */
20
+ async function playwrightConfigurationGenerator(tree, options) {
21
+ const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
22
+ const projectRoot = projectConfig.root;
23
+ const playwrightConfigPath = (0, devkit_1.joinPathFragments)(projectRoot, "playwright.config.ts");
24
+ if (tree.exists(playwrightConfigPath)) {
25
+ throw new Error(`playwright.config.ts already exists at ${playwrightConfigPath}. Remove it before running this generator.`);
26
+ }
27
+ const existingProjectConfig = projectConfig;
28
+ // We use `nx:run-commands` (not `@nx/playwright:playwright`) because that executor
29
+ // does not honour an `env` option in target configurations — it forwards unknown options
30
+ // as Playwright CLI flags, so `BASE_URL` would never reach `process.env`.
31
+ // `nx:run-commands` sets the configuration's `env` on the spawned process, which is what
32
+ // `defaultPlaywrightConfig()` reads.
33
+ //
34
+ // `cwd` is pinned to `projectRoot` so `process.cwd()` inside Playwright matches the
35
+ // project directory. `login()`'s default fixture path is
36
+ // `path.join(process.cwd(), "playwright", "fixtures", "auth.json")`, and the generator
37
+ // scaffolds that file under `${projectRoot}/playwright/fixtures/auth.json` — without
38
+ // this `cwd`, nx:run-commands would spawn from the workspace root and `login()` would
39
+ // miss the scaffolded fixture (failing unless env vars are set).
40
+ const configurations = {
41
+ local: { env: { BASE_URL: "http://localhost:3000" } },
42
+ dev: { env: { BASE_URL: "https://dev.manager.trackunit.com" } },
43
+ stage: { env: { BASE_URL: "https://stage.manager.trackunit.com" } },
44
+ prod: { env: { BASE_URL: "https://new.manager.trackunit.com" } },
45
+ };
46
+ (0, devkit_1.updateProjectConfiguration)(tree, options.project, {
47
+ ...existingProjectConfig,
48
+ targets: {
49
+ ...existingProjectConfig.targets,
50
+ "e2e-pw-ui": {
51
+ executor: "nx:run-commands",
52
+ options: {
53
+ command: `playwright test --config=playwright.config.ts --headed --ui`,
54
+ cwd: projectRoot,
55
+ },
56
+ configurations,
57
+ },
58
+ "e2e-pw": {
59
+ executor: "nx:run-commands",
60
+ options: {
61
+ command: `playwright test --config=playwright.config.ts`,
62
+ cwd: projectRoot,
63
+ },
64
+ configurations,
65
+ },
66
+ },
67
+ });
68
+ (0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, "files/root"), projectRoot, {
69
+ ...options,
70
+ tmpl: "",
71
+ });
72
+ await (0, devkit_1.formatFiles)(tree);
73
+ return () => {
74
+ devkit_1.logger.info(`\n\n\n✅ 🚀 Please open this file to get started\n${(0, devkit_1.joinPathFragments)(projectRoot, "playwright/tests/app.spec.ts")}`);
75
+ };
76
+ }
77
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1,3 @@
1
+ export interface PlaywrightConfigurationGeneratorSchema {
2
+ project: string;
3
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "PlaywrightConfiguration",
4
+ "title": "",
5
+ "type": "object",
6
+ "properties": {
7
+ "project": {
8
+ "type": "string",
9
+ "description": "feature name",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "Which project would you like to add Playwright E2E tests for?"
15
+ }
16
+ },
17
+ "required": ["project"]
18
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ export * from "./plugins/createLogFile";
2
+ export * from "./plugins/defaultPlaywrightConfig";
3
+ export * from "./plugins/logsReporter";
4
+ export * from "./plugins/nxPreset";
5
+ export * from "./plugins/redactSensitive";
6
+ export * from "./plugins/setupPlugins";
7
+ export * from "./plugins/writeFileWithPrettier";
8
+ export * from "./utils/Codeowner";
9
+ export * from "./utils/fileNameBuilder";
10
+ export * from "./utils/fileUpdater";
11
+ export type { FeatureFlag, FeatureFlagFixtures } from "./fixtures/featureFlags.fixture";
12
+ export type { CachedStorageState } from "./fixtures/hydrateStorageState";
13
+ export type { IrisAppFixtures, IrisAppOptions, StorybookPreviewOptions } from "./fixtures/irisApp.fixture";
14
+ export type { LocalDevModeFixtures } from "./fixtures/localDevMode.fixture";
15
+ export type { AuthCredentials, LoginOptions, TrackunitBaseFixtures } from "./fixtures/login.fixture";
package/src/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./plugins/createLogFile"), exports);
5
+ tslib_1.__exportStar(require("./plugins/defaultPlaywrightConfig"), exports);
6
+ tslib_1.__exportStar(require("./plugins/logsReporter"), exports);
7
+ tslib_1.__exportStar(require("./plugins/nxPreset"), exports);
8
+ tslib_1.__exportStar(require("./plugins/redactSensitive"), exports);
9
+ tslib_1.__exportStar(require("./plugins/setupPlugins"), exports);
10
+ tslib_1.__exportStar(require("./plugins/writeFileWithPrettier"), exports);
11
+ tslib_1.__exportStar(require("./utils/Codeowner"), exports);
12
+ tslib_1.__exportStar(require("./utils/fileNameBuilder"), exports);
13
+ tslib_1.__exportStar(require("./utils/fileUpdater"), exports);
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,24 @@
1
+ import { Formatter } from "./writeFileWithPrettier";
2
+ type Log = {
3
+ type: string;
4
+ message: string;
5
+ severity: string;
6
+ };
7
+ /**
8
+ * Creates structured log files for Playwright test runs.
9
+ * Generates separate files for all logs, errors, and network errors.
10
+ *
11
+ * Returns a promise that resolves once every file has been formatted and
12
+ * written. Callers (e.g. `LogsReporter.onTestEnd`) must await — or collect
13
+ * and later await — this promise to avoid fire-and-forget writes that
14
+ * Playwright may finish before Prettier settles.
15
+ *
16
+ * @param nxRoot - Absolute path to the NX workspace root
17
+ * @param logsPath - Directory where log files will be written
18
+ * @param fileNameWithoutExtension - Base filename (without extension) for the log files
19
+ * @param logs - Array of log entries to write
20
+ * @param logWriter - Prettier formatter instance for formatting JSON output
21
+ * @returns {Promise<void>} Resolves once all log files have been written
22
+ */
23
+ export declare function createLogFile(nxRoot: string, logsPath: string, fileNameWithoutExtension: string, logs: Array<Log>, logWriter: Formatter): Promise<void>;
24
+ export {};
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLogFile = createLogFile;
4
+ const tslib_1 = require("tslib");
5
+ const fs_1 = tslib_1.__importStar(require("fs"));
6
+ const path_1 = tslib_1.__importDefault(require("path"));
7
+ const writeFileWithPrettier_1 = require("./writeFileWithPrettier");
8
+ const isNetworkCall = (log) => {
9
+ return (log.type === "network:request" ||
10
+ log.type === "network:response" ||
11
+ log.type === "network:fetch" ||
12
+ log.type === "network:websocket");
13
+ };
14
+ /**
15
+ * Creates structured log files for Playwright test runs.
16
+ * Generates separate files for all logs, errors, and network errors.
17
+ *
18
+ * Returns a promise that resolves once every file has been formatted and
19
+ * written. Callers (e.g. `LogsReporter.onTestEnd`) must await — or collect
20
+ * and later await — this promise to avoid fire-and-forget writes that
21
+ * Playwright may finish before Prettier settles.
22
+ *
23
+ * @param nxRoot - Absolute path to the NX workspace root
24
+ * @param logsPath - Directory where log files will be written
25
+ * @param fileNameWithoutExtension - Base filename (without extension) for the log files
26
+ * @param logs - Array of log entries to write
27
+ * @param logWriter - Prettier formatter instance for formatting JSON output
28
+ * @returns {Promise<void>} Resolves once all log files have been written
29
+ */
30
+ async function createLogFile(nxRoot, logsPath, fileNameWithoutExtension, logs, logWriter) {
31
+ if (!(0, fs_1.existsSync)(logsPath)) {
32
+ fs_1.default.mkdirSync(logsPath, { recursive: true });
33
+ }
34
+ const logFilePath = path_1.default.join(logsPath, fileNameWithoutExtension);
35
+ const writes = [
36
+ (0, writeFileWithPrettier_1.writeFileWithPrettier)(nxRoot, logFilePath + "-all.json", JSON.stringify(logs), logWriter),
37
+ ];
38
+ const errorCmds = logs.filter(log => log.severity === "error");
39
+ if (errorCmds.length > 0) {
40
+ writes.push((0, writeFileWithPrettier_1.writeFileWithPrettier)(nxRoot, logFilePath + "-errors.json", JSON.stringify(errorCmds), logWriter));
41
+ }
42
+ const networkErrorsCmds = logs.filter(log => isNetworkCall(log) &&
43
+ !log.message.includes("Status: 200") &&
44
+ log.severity !== "success" &&
45
+ !log.message.includes("sentry.io/api/"));
46
+ if (networkErrorsCmds.length > 0) {
47
+ writes.push((0, writeFileWithPrettier_1.writeFileWithPrettier)(nxRoot, logFilePath + "-network-errors.json", JSON.stringify(networkErrorsCmds), logWriter));
48
+ }
49
+ await Promise.all(writes);
50
+ }
51
+ //# sourceMappingURL=createLogFile.js.map
@@ -0,0 +1,68 @@
1
+ import type { PlaywrightTestConfig } from "@playwright/test";
2
+ /**
3
+ * Finds the NX workspace root by walking up the directory tree looking for nx.json or workspace.json.
4
+ *
5
+ * @param startDir - Starting directory for the search (defaults to current working directory)
6
+ * @returns {string} Absolute path to the workspace root
7
+ * @throws Error if workspace root cannot be found
8
+ */
9
+ export declare function findWorkspaceRoot(startDir?: string): string;
10
+ export interface E2EPluginConfig {
11
+ outputPath?: string;
12
+ }
13
+ export interface E2EProjectConfig {
14
+ testDir?: string;
15
+ }
16
+ export interface E2EBehaviorConfig {
17
+ timeout?: number;
18
+ retries?: number;
19
+ navigationTimeout?: number;
20
+ actionTimeout?: number;
21
+ /**
22
+ * Override Playwright's worker count. Useful when the suite hits external,
23
+ * rate-limited services (Okta `/api/v1/authn` returns `E0000047` once you
24
+ * fan out past a handful of parallel logins against a shared environment).
25
+ *
26
+ * The `login` fixture caches one authenticated `storageState` per worker,
27
+ * so worker count is effectively the upper bound on parallel Okta calls
28
+ * per `(baseURL, username)` pair. Capping workers therefore caps Okta load.
29
+ *
30
+ * Leave unset to use Playwright's default (`Math.ceil(os.cpus().length / 2)`
31
+ * locally, `1` on CI).
32
+ */
33
+ workers?: number | string;
34
+ /**
35
+ * Whether tests within the same file should run in parallel.
36
+ *
37
+ * Defaults to Playwright's behaviour (`undefined`, i.e. the project-level
38
+ * setting wins, which in turn defaults to `false`). Set explicitly when a
39
+ * project wants file-level parallelism or wants to opt out of it.
40
+ */
41
+ fullyParallel?: boolean;
42
+ }
43
+ export interface E2EConfigOptions {
44
+ /**
45
+ * Absolute path to the directory of the calling `playwright.config.ts`. Pass `__dirname`.
46
+ *
47
+ * Required when running under `@nx/playwright` (or any executor that forks Playwright from
48
+ * the workspace root) — without it, `process.cwd()` resolves to the workspace root and
49
+ * codeowner lookup / report paths land outside the intended project directory.
50
+ *
51
+ * When omitted, falls back to `process.cwd()` for backwards compatibility with consumers
52
+ * invoking Playwright directly from their project directory.
53
+ */
54
+ configDir?: string;
55
+ nxRoot?: string;
56
+ outputDirOverride?: string;
57
+ projectConfig?: E2EProjectConfig;
58
+ behaviorConfig?: E2EBehaviorConfig;
59
+ pluginConfig?: E2EPluginConfig;
60
+ }
61
+ /**
62
+ * Creates default Playwright configuration for E2E testing.
63
+ * Returns a partial `PlaywrightTestConfig` that projects can spread and extend.
64
+ *
65
+ * @param options - Optional configuration overrides
66
+ * @returns {Partial<PlaywrightTestConfig>} Playwright test configuration
67
+ */
68
+ export declare const defaultPlaywrightConfig: (options?: E2EConfigOptions) => Partial<PlaywrightTestConfig>;