@toolstackhq/create-qa-patterns 1.0.14 → 1.0.15

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 (123) hide show
  1. package/README.md +5 -0
  2. package/index.js +252 -1076
  3. package/lib/args.js +139 -0
  4. package/lib/constants.js +115 -0
  5. package/lib/interactive.js +131 -0
  6. package/lib/local-env.js +65 -0
  7. package/lib/metadata.js +329 -0
  8. package/lib/output.js +326 -0
  9. package/lib/prereqs.js +72 -0
  10. package/lib/scaffold.js +120 -0
  11. package/lib/templates.js +40 -0
  12. package/package.json +5 -3
  13. package/templates/cypress-template/.env.example +2 -2
  14. package/templates/cypress-template/.github/workflows/cypress-tests.yml +2 -2
  15. package/templates/cypress-template/README.md +10 -6
  16. package/templates/cypress-template/allurerc.mjs +1 -1
  17. package/templates/cypress-template/config/environments.ts +13 -11
  18. package/templates/cypress-template/config/runtime-config.ts +17 -12
  19. package/templates/cypress-template/config/secret-manager.ts +1 -1
  20. package/templates/cypress-template/config/test-env.ts +3 -3
  21. package/templates/cypress-template/cypress/e2e/ui-journey.cy.ts +12 -10
  22. package/templates/cypress-template/cypress/support/app-config.ts +5 -5
  23. package/templates/cypress-template/cypress/support/commands.ts +7 -7
  24. package/templates/cypress-template/cypress/support/data/data-factory.ts +6 -4
  25. package/templates/cypress-template/cypress/support/data/id-generator.ts +1 -1
  26. package/templates/cypress-template/cypress/support/data/seeded-faker.ts +2 -2
  27. package/templates/cypress-template/cypress/support/e2e.ts +2 -2
  28. package/templates/cypress-template/cypress/support/pages/login-page.ts +4 -4
  29. package/templates/cypress-template/cypress/support/pages/people-page.ts +10 -10
  30. package/templates/cypress-template/cypress.config.ts +9 -9
  31. package/templates/cypress-template/demo-apps/ui-demo-app/public/styles.css +1 -1
  32. package/templates/cypress-template/demo-apps/ui-demo-app/src/server.js +44 -41
  33. package/templates/cypress-template/demo-apps/ui-demo-app/src/store.js +31 -3
  34. package/templates/cypress-template/demo-apps/ui-demo-app/src/templates.js +5 -5
  35. package/templates/cypress-template/eslint.config.mjs +53 -45
  36. package/templates/cypress-template/package.json +6 -5
  37. package/templates/cypress-template/scripts/ensure-local-env.mjs +36 -0
  38. package/templates/cypress-template/scripts/generate-allure-report.mjs +16 -10
  39. package/templates/cypress-template/scripts/run-cypress.mjs +33 -24
  40. package/templates/cypress-template/scripts/run-tests.sh +1 -0
  41. package/templates/cypress-template/tsconfig.json +7 -1
  42. package/templates/playwright-template/.env.example +6 -6
  43. package/templates/playwright-template/.github/workflows/playwright-tests.yml +14 -5
  44. package/templates/playwright-template/README.md +6 -5
  45. package/templates/playwright-template/allurerc.mjs +1 -1
  46. package/templates/playwright-template/components/flash-message.ts +2 -2
  47. package/templates/playwright-template/config/environments.ts +16 -14
  48. package/templates/playwright-template/config/runtime-config.ts +17 -12
  49. package/templates/playwright-template/config/secret-manager.ts +1 -1
  50. package/templates/playwright-template/config/test-env.ts +3 -3
  51. package/templates/playwright-template/data/factories/data-factory.ts +6 -4
  52. package/templates/playwright-template/data/generators/id-generator.ts +1 -1
  53. package/templates/playwright-template/data/generators/seeded-faker.ts +2 -2
  54. package/templates/playwright-template/demo-apps/api-demo-server/src/server.js +9 -9
  55. package/templates/playwright-template/demo-apps/api-demo-server/src/store.js +1 -1
  56. package/templates/playwright-template/demo-apps/ui-demo-app/public/styles.css +1 -1
  57. package/templates/playwright-template/demo-apps/ui-demo-app/src/server.js +44 -41
  58. package/templates/playwright-template/demo-apps/ui-demo-app/src/store.js +31 -3
  59. package/templates/playwright-template/demo-apps/ui-demo-app/src/templates.js +5 -5
  60. package/templates/playwright-template/eslint.config.mjs +40 -40
  61. package/templates/playwright-template/fixtures/test-fixtures.ts +27 -12
  62. package/templates/playwright-template/lint/architecture-plugin.cjs +36 -31
  63. package/templates/playwright-template/package.json +7 -6
  64. package/templates/playwright-template/pages/base-page.ts +4 -4
  65. package/templates/playwright-template/pages/login-page.ts +9 -9
  66. package/templates/playwright-template/pages/people-page.ts +21 -17
  67. package/templates/playwright-template/playwright.config.ts +22 -19
  68. package/templates/playwright-template/reporters/structured-reporter.ts +11 -8
  69. package/templates/playwright-template/scripts/ensure-local-env.mjs +37 -0
  70. package/templates/playwright-template/scripts/generate-allure-report.mjs +16 -10
  71. package/templates/playwright-template/scripts/run-tests.sh +1 -0
  72. package/templates/playwright-template/tests/api-people.spec.ts +8 -6
  73. package/templates/playwright-template/tests/ui-journey.spec.ts +13 -8
  74. package/templates/playwright-template/tsconfig.json +3 -11
  75. package/templates/playwright-template/utils/logger.ts +12 -8
  76. package/templates/playwright-template/utils/test-step.ts +5 -5
  77. package/templates/wdio-template/.env.example +14 -0
  78. package/templates/wdio-template/.github/workflows/wdio-tests.yml +46 -0
  79. package/templates/wdio-template/README.md +241 -0
  80. package/templates/wdio-template/allurerc.mjs +10 -0
  81. package/templates/wdio-template/components/README.md +5 -0
  82. package/templates/wdio-template/components/flash-message.ts +16 -0
  83. package/templates/wdio-template/config/README.md +5 -0
  84. package/templates/wdio-template/config/environments.ts +40 -0
  85. package/templates/wdio-template/config/runtime-config.ts +53 -0
  86. package/templates/wdio-template/config/secret-manager.ts +29 -0
  87. package/templates/wdio-template/config/test-env.ts +9 -0
  88. package/templates/wdio-template/data/README.md +9 -0
  89. package/templates/wdio-template/data/factories/README.md +6 -0
  90. package/templates/wdio-template/data/factories/data-factory.ts +36 -0
  91. package/templates/wdio-template/data/generators/README.md +5 -0
  92. package/templates/wdio-template/data/generators/id-generator.ts +18 -0
  93. package/templates/wdio-template/data/generators/seeded-faker.ts +14 -0
  94. package/templates/wdio-template/demo-apps/ui-demo-app/public/styles.css +120 -0
  95. package/templates/wdio-template/demo-apps/ui-demo-app/src/server.js +152 -0
  96. package/templates/wdio-template/demo-apps/ui-demo-app/src/store.js +71 -0
  97. package/templates/wdio-template/demo-apps/ui-demo-app/src/templates.js +121 -0
  98. package/templates/wdio-template/eslint.config.mjs +86 -0
  99. package/templates/wdio-template/lint/architecture-plugin.cjs +123 -0
  100. package/templates/wdio-template/package-lock.json +11058 -0
  101. package/templates/wdio-template/package.json +44 -0
  102. package/templates/wdio-template/pages/README.md +6 -0
  103. package/templates/wdio-template/pages/base-page.ts +15 -0
  104. package/templates/wdio-template/pages/login-page.ts +27 -0
  105. package/templates/wdio-template/pages/people-page.ts +54 -0
  106. package/templates/wdio-template/reporters/README.md +5 -0
  107. package/templates/wdio-template/reporters/structured-reporter.ts +78 -0
  108. package/templates/wdio-template/scripts/README.md +5 -0
  109. package/templates/wdio-template/scripts/ensure-local-env.mjs +36 -0
  110. package/templates/wdio-template/scripts/generate-allure-report.mjs +72 -0
  111. package/templates/wdio-template/scripts/run-tests.sh +7 -0
  112. package/templates/wdio-template/scripts/run-wdio.mjs +114 -0
  113. package/templates/wdio-template/tests/README.md +7 -0
  114. package/templates/wdio-template/tests/ui-journey.spec.ts +52 -0
  115. package/templates/wdio-template/tsconfig.json +22 -0
  116. package/templates/wdio-template/utils/README.md +5 -0
  117. package/templates/wdio-template/utils/logger.ts +60 -0
  118. package/templates/wdio-template/utils/test-step.ts +20 -0
  119. package/templates/wdio-template/wdio.conf.ts +58 -0
  120. package/tests/args.test.js +58 -0
  121. package/tests/local-env.test.js +70 -0
  122. package/tests/metadata.test.js +147 -0
  123. package/tests/templates.test.js +44 -0
@@ -1,25 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // Starts the local demo app when needed, then launches Cypress in run or open mode.
4
- import process from "node:process";
5
- import path from "node:path";
6
- import { spawn } from "node:child_process";
4
+ import process from 'node:process';
5
+ import path from 'node:path';
6
+ import { spawn } from 'node:child_process';
7
7
 
8
- import dotenv from "dotenv";
8
+ import dotenv from 'dotenv';
9
9
 
10
- const mode = process.argv[2] ?? "run";
10
+ const mode = process.argv[2] ?? 'run';
11
11
  const args = process.argv.slice(3);
12
12
  const cwd = process.cwd();
13
- const healthUrl = "http://127.0.0.1:3000/health";
14
- const environment = process.env.TEST_ENV ?? "dev";
13
+ const healthUrl = 'http://127.0.0.1:3000/health';
14
+ const environment = process.env.TEST_ENV ?? 'dev';
15
15
  const environmentDefaults = {
16
- dev: "http://127.0.0.1:3000",
17
- staging: "https://staging-ui.example.internal",
18
- prod: "https://ui.example.internal"
16
+ dev: 'http://127.0.0.1:3000',
17
+ staging: 'https://staging-ui.example.internal',
18
+ prod: 'https://ui.example.internal'
19
19
  };
20
20
 
21
- dotenv.config({ path: path.resolve(cwd, ".env") });
22
- dotenv.config({ path: path.resolve(cwd, `.env.${environment}`), override: true });
21
+ dotenv.config({ path: path.resolve(cwd, '.env') });
22
+ dotenv.config({
23
+ path: path.resolve(cwd, `.env.${environment}`),
24
+ override: true
25
+ });
23
26
 
24
27
  const uiBaseUrl =
25
28
  process.env[`${environment.toUpperCase()}_UI_BASE_URL`] ??
@@ -28,18 +31,18 @@ const uiBaseUrl =
28
31
  environmentDefaults.dev;
29
32
 
30
33
  const shouldAutoStartDemoApp =
31
- environment === "dev" &&
34
+ environment === 'dev' &&
32
35
  uiBaseUrl === environmentDefaults.dev &&
33
- process.env.CY_DISABLE_LOCAL_DEMO_APP !== "true";
36
+ process.env.CY_DISABLE_LOCAL_DEMO_APP !== 'true';
34
37
 
35
38
  function getCommandName(command) {
36
- return process.platform === "win32" ? `${command}.cmd` : command;
39
+ return process.platform === 'win32' ? `${command}.cmd` : command;
37
40
  }
38
41
 
39
42
  function spawnCommand(command, commandArgs, options = {}) {
40
43
  return spawn(getCommandName(command), commandArgs, {
41
44
  cwd,
42
- stdio: "inherit",
45
+ stdio: 'inherit',
43
46
  ...options
44
47
  });
45
48
  }
@@ -68,7 +71,7 @@ function killChild(child) {
68
71
  return;
69
72
  }
70
73
 
71
- child.kill("SIGTERM");
74
+ child.kill('SIGTERM');
72
75
  }
73
76
 
74
77
  async function run() {
@@ -76,16 +79,20 @@ async function run() {
76
79
 
77
80
  try {
78
81
  if (shouldAutoStartDemoApp) {
79
- demoAppProcess = spawnCommand("npm", ["run", "demo:ui"]);
82
+ demoAppProcess = spawnCommand('npm', ['run', 'demo:ui']);
80
83
  await waitForHealthcheck(healthUrl);
81
84
  }
82
85
 
83
- const cypressCommand = mode === "open" ? "open" : "run";
84
- const cypressProcess = spawnCommand("npx", ["cypress", cypressCommand, ...args]);
86
+ const cypressCommand = mode === 'open' ? 'open' : 'run';
87
+ const cypressProcess = spawnCommand('npx', [
88
+ 'cypress',
89
+ cypressCommand,
90
+ ...args
91
+ ]);
85
92
 
86
93
  const exitCode = await new Promise((resolve) => {
87
- cypressProcess.on("close", resolve);
88
- cypressProcess.on("error", () => resolve(1));
94
+ cypressProcess.on('close', resolve);
95
+ cypressProcess.on('error', () => resolve(1));
89
96
  });
90
97
 
91
98
  if (exitCode !== 0) {
@@ -96,11 +103,13 @@ async function run() {
96
103
  }
97
104
  }
98
105
 
99
- for (const signal of ["SIGINT", "SIGTERM"]) {
106
+ for (const signal of ['SIGINT', 'SIGTERM']) {
100
107
  process.on(signal, () => process.exit(1));
101
108
  }
102
109
 
103
110
  run().catch((error) => {
104
- process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
111
+ process.stderr.write(
112
+ `${error instanceof Error ? error.message : String(error)}\n`
113
+ );
105
114
  process.exit(1);
106
115
  });
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
+ node ./scripts/ensure-local-env.mjs
4
5
  npm run lint
5
6
  npm run typecheck
6
7
  npx cypress run "$@"
@@ -11,5 +11,11 @@
11
11
  "noEmit": true,
12
12
  "types": ["cypress", "node"]
13
13
  },
14
- "include": ["cypress/**/*.ts", "cypress/**/*.d.ts", "config/**/*.ts", "cypress.config.ts", "allurerc.mjs"]
14
+ "include": [
15
+ "cypress/**/*.ts",
16
+ "cypress/**/*.d.ts",
17
+ "config/**/*.ts",
18
+ "cypress.config.ts",
19
+ "allurerc.mjs"
20
+ ]
15
21
  }
@@ -3,15 +3,15 @@ TEST_RUN_ID=local
3
3
 
4
4
  DEV_UI_BASE_URL=http://127.0.0.1:3000
5
5
  DEV_API_BASE_URL=http://127.0.0.1:3001
6
- DEV_APP_USERNAME=tester
7
- DEV_APP_PASSWORD=Password123!
6
+ DEV_APP_USERNAME=generate-a-local-username
7
+ DEV_APP_PASSWORD=generate-a-local-password
8
8
 
9
9
  STAGING_UI_BASE_URL=https://staging-ui.example.internal
10
10
  STAGING_API_BASE_URL=https://staging-api.example.internal
11
- STAGING_APP_USERNAME=staging-user
12
- STAGING_APP_PASSWORD=replace-me
11
+ STAGING_APP_USERNAME=your-staging-username
12
+ STAGING_APP_PASSWORD=your-staging-password
13
13
 
14
14
  PROD_UI_BASE_URL=https://ui.example.internal
15
15
  PROD_API_BASE_URL=https://api.example.internal
16
- PROD_APP_USERNAME=prod-user
17
- PROD_APP_PASSWORD=replace-me
16
+ PROD_APP_USERNAME=your-prod-username
17
+ PROD_APP_PASSWORD=your-prod-password
@@ -4,9 +4,9 @@ on:
4
4
  workflow_dispatch:
5
5
  push:
6
6
  paths:
7
- - "templates/playwright-template/**"
8
- - "test-apps/**"
9
- - "package.json"
7
+ - 'templates/playwright-template/**'
8
+ - 'test-apps/**'
9
+ - 'package.json'
10
10
 
11
11
  jobs:
12
12
  playwright:
@@ -17,7 +17,7 @@ jobs:
17
17
 
18
18
  - uses: actions/setup-node@v4
19
19
  with:
20
- node-version: "20"
20
+ node-version: '20'
21
21
 
22
22
  - name: Install workspace dependencies
23
23
  run: npm ci
@@ -50,13 +50,18 @@ jobs:
50
50
 
51
51
  playwright-docker:
52
52
  runs-on: ubuntu-latest
53
+ env:
54
+ DEV_APP_USERNAME: ci-docker-user
55
+ DEV_APP_PASSWORD: ci-docker-password-A1!
56
+ UI_DEMO_USERNAME: ci-docker-user
57
+ UI_DEMO_PASSWORD: ci-docker-password-A1!
53
58
 
54
59
  steps:
55
60
  - uses: actions/checkout@v4
56
61
 
57
62
  - uses: actions/setup-node@v4
58
63
  with:
59
- node-version: "20"
64
+ node-version: '20'
60
65
 
61
66
  - name: Install workspace dependencies
62
67
  run: npm ci
@@ -91,6 +96,10 @@ jobs:
91
96
  -e TEST_RUN_ID=ci-docker \
92
97
  -e DEV_UI_BASE_URL=http://host.docker.internal:3000 \
93
98
  -e DEV_API_BASE_URL=http://host.docker.internal:3001 \
99
+ -e DEV_APP_USERNAME=${DEV_APP_USERNAME} \
100
+ -e DEV_APP_PASSWORD=${DEV_APP_PASSWORD} \
101
+ -e UI_DEMO_USERNAME=${UI_DEMO_USERNAME} \
102
+ -e UI_DEMO_PASSWORD=${UI_DEMO_PASSWORD} \
94
103
  -v "${GITHUB_WORKSPACE}/templates/playwright-template/reports:/workspace/reports" \
95
104
  -v "${GITHUB_WORKSPACE}/templates/playwright-template/allure-results:/workspace/allure-results" \
96
105
  -v "${GITHUB_WORKSPACE}/templates/playwright-template/test-results:/workspace/test-results" \
@@ -89,8 +89,7 @@ Default local values:
89
89
 
90
90
  - UI base URL: `http://127.0.0.1:3000`
91
91
  - API base URL: `http://127.0.0.1:3001`
92
- - username: `tester`
93
- - password: `Password123!`
92
+ - credentials: generated into local `.env` on first run
94
93
 
95
94
  ## Environment and secrets
96
95
 
@@ -110,7 +109,7 @@ The same pattern is used for credentials:
110
109
 
111
110
  1. `DEV_APP_USERNAME` or `DEV_APP_PASSWORD`
112
111
  2. `APP_USERNAME` or `APP_PASSWORD`
113
- 3. built-in defaults for the selected environment
112
+ 3. built-in empty defaults for the selected environment
114
113
 
115
114
  For local overrides, copy:
116
115
 
@@ -129,6 +128,8 @@ The template loads:
129
128
  - `.env`
130
129
  - `.env.<TEST_ENV>`
131
130
 
131
+ On the first local run, the template also creates a `.env` file with random demo credentials if one does not already exist.
132
+
132
133
  Example:
133
134
 
134
135
  ```bash
@@ -192,7 +193,7 @@ If you only want Playwright reporting, remove the `allure-playwright` reporter e
192
193
  Create tests under `tests/` and import the shared fixtures:
193
194
 
194
195
  ```ts
195
- import { expect, test } from "../fixtures/test-fixtures";
196
+ import { expect, test } from '../fixtures/test-fixtures';
196
197
  ```
197
198
 
198
199
  Keep the pattern simple:
@@ -204,7 +205,7 @@ Keep the pattern simple:
204
205
  Example shape:
205
206
 
206
207
  ```ts
207
- test("do something @smoke", async ({ dataFactory, loginPage }) => {
208
+ test('do something @smoke', async ({ dataFactory, loginPage }) => {
208
209
  const person = dataFactory.person();
209
210
  // use page objects here
210
211
  });
@@ -1,5 +1,5 @@
1
1
  export default {
2
- name: "qa-patterns Playwright Template",
2
+ name: 'qa-patterns Playwright Template',
3
3
  plugins: {
4
4
  awesome: {
5
5
  options: {
@@ -1,10 +1,10 @@
1
- import type { Locator, Page } from "@playwright/test";
1
+ import type { Locator, Page } from '@playwright/test';
2
2
 
3
3
  export class FlashMessage {
4
4
  private readonly message: Locator;
5
5
 
6
6
  constructor(page: Page) {
7
- this.message = page.getByTestId("flash-message");
7
+ this.message = page.getByTestId('flash-message');
8
8
  }
9
9
 
10
10
  async getText(): Promise<string | null> {
@@ -1,5 +1,5 @@
1
1
  // Defines the built-in environment defaults used when env vars are not provided.
2
- import type { TestEnvironment } from "./test-env";
2
+ import type { TestEnvironment } from './test-env';
3
3
 
4
4
  type EnvironmentDefaults = {
5
5
  uiBaseUrl: string;
@@ -12,31 +12,33 @@ type EnvironmentDefaults = {
12
12
 
13
13
  const DEFAULTS: Record<TestEnvironment, EnvironmentDefaults> = {
14
14
  dev: {
15
- uiBaseUrl: "http://127.0.0.1:3000",
16
- apiBaseUrl: "http://127.0.0.1:3001",
15
+ uiBaseUrl: 'http://127.0.0.1:3000',
16
+ apiBaseUrl: 'http://127.0.0.1:3001',
17
17
  credentials: {
18
- username: "tester",
19
- password: "Password123!"
18
+ username: '',
19
+ password: ''
20
20
  }
21
21
  },
22
22
  staging: {
23
- uiBaseUrl: "https://staging-ui.example.internal",
24
- apiBaseUrl: "https://staging-api.example.internal",
23
+ uiBaseUrl: 'https://staging-ui.example.internal',
24
+ apiBaseUrl: 'https://staging-api.example.internal',
25
25
  credentials: {
26
- username: "staging-user",
27
- password: "replace-me"
26
+ username: '',
27
+ password: ''
28
28
  }
29
29
  },
30
30
  prod: {
31
- uiBaseUrl: "https://ui.example.internal",
32
- apiBaseUrl: "https://api.example.internal",
31
+ uiBaseUrl: 'https://ui.example.internal',
32
+ apiBaseUrl: 'https://api.example.internal',
33
33
  credentials: {
34
- username: "prod-user",
35
- password: "replace-me"
34
+ username: '',
35
+ password: ''
36
36
  }
37
37
  }
38
38
  };
39
39
 
40
- export function getEnvironmentDefaults(testEnv: TestEnvironment): EnvironmentDefaults {
40
+ export function getEnvironmentDefaults(
41
+ testEnv: TestEnvironment
42
+ ): EnvironmentDefaults {
41
43
  return DEFAULTS[testEnv];
42
44
  }
@@ -1,20 +1,23 @@
1
1
  // Builds the runtime configuration object that tests and fixtures consume.
2
- import path from "node:path";
2
+ import path from 'node:path';
3
3
 
4
- import dotenv from "dotenv";
5
- import { z } from "zod";
4
+ import dotenv from 'dotenv';
5
+ import { z } from 'zod';
6
6
 
7
- import { getEnvironmentDefaults } from "./environments";
8
- import { EnvSecretProvider, SecretManager } from "./secret-manager";
9
- import { loadTestEnvironment } from "./test-env";
7
+ import { getEnvironmentDefaults } from './environments';
8
+ import { EnvSecretProvider, SecretManager } from './secret-manager';
9
+ import { loadTestEnvironment } from './test-env';
10
10
 
11
11
  const environment = loadTestEnvironment();
12
12
 
13
- dotenv.config({ path: path.resolve(process.cwd(), ".env") });
14
- dotenv.config({ path: path.resolve(process.cwd(), `.env.${environment}`), override: true });
13
+ dotenv.config({ path: path.resolve(process.cwd(), '.env') });
14
+ dotenv.config({
15
+ path: path.resolve(process.cwd(), `.env.${environment}`),
16
+ override: true
17
+ });
15
18
 
16
19
  const runtimeConfigSchema = z.object({
17
- testEnv: z.enum(["dev", "staging", "prod"]),
20
+ testEnv: z.enum(['dev', 'staging', 'prod']),
18
21
  testRunId: z.string().min(1),
19
22
  uiBaseUrl: z.string().url(),
20
23
  apiBaseUrl: z.string().url(),
@@ -41,14 +44,16 @@ export function loadRuntimeConfig(): RuntimeConfig {
41
44
 
42
45
  return runtimeConfigSchema.parse({
43
46
  testEnv: environment,
44
- testRunId: process.env.TEST_RUN_ID ?? "local",
47
+ testRunId: process.env.TEST_RUN_ID ?? 'local',
45
48
  uiBaseUrl,
46
49
  apiBaseUrl,
47
50
  credentials: {
48
51
  username:
49
- secretManager.getOptionalSecret("APP_USERNAME", environment) ?? defaults.credentials.username,
52
+ secretManager.getOptionalSecret('APP_USERNAME', environment) ??
53
+ defaults.credentials.username,
50
54
  password:
51
- secretManager.getOptionalSecret("APP_PASSWORD", environment) ?? defaults.credentials.password
55
+ secretManager.getOptionalSecret('APP_PASSWORD', environment) ??
56
+ defaults.credentials.password
52
57
  }
53
58
  });
54
59
  }
@@ -1,5 +1,5 @@
1
1
  // Minimal secret abstraction so env-based secrets can later be replaced cleanly.
2
- import type { TestEnvironment } from "./test-env";
2
+ import type { TestEnvironment } from './test-env';
3
3
 
4
4
  export interface SecretProvider {
5
5
  getSecret(key: string, testEnv: TestEnvironment): string | undefined;
@@ -1,9 +1,9 @@
1
- import { z } from "zod";
1
+ import { z } from 'zod';
2
2
 
3
- export const testEnvironmentSchema = z.enum(["dev", "staging", "prod"]);
3
+ export const testEnvironmentSchema = z.enum(['dev', 'staging', 'prod']);
4
4
 
5
5
  export type TestEnvironment = z.infer<typeof testEnvironmentSchema>;
6
6
 
7
7
  export function loadTestEnvironment(): TestEnvironment {
8
- return testEnvironmentSchema.parse(process.env.TEST_ENV ?? "dev");
8
+ return testEnvironmentSchema.parse(process.env.TEST_ENV ?? 'dev');
9
9
  }
@@ -1,6 +1,6 @@
1
1
  // Generic data builders that keep tests readable and deterministic.
2
- import { createSeededFaker } from "../generators/seeded-faker";
3
- import { IdGenerator } from "../generators/id-generator";
2
+ import { createSeededFaker } from '../generators/seeded-faker';
3
+ import { IdGenerator } from '../generators/id-generator';
4
4
 
5
5
  export type PersonRecord = {
6
6
  personId: string;
@@ -17,7 +17,7 @@ export class DataFactory {
17
17
  }
18
18
 
19
19
  person(overrides?: Partial<PersonRecord>): PersonRecord {
20
- const personId = overrides?.personId ?? this.idGenerator.next("person");
20
+ const personId = overrides?.personId ?? this.idGenerator.next('person');
21
21
  const seededFaker = createSeededFaker(`${this.testRunId}:${personId}`);
22
22
  const firstName = seededFaker.person.firstName();
23
23
  const lastName = seededFaker.person.lastName();
@@ -27,7 +27,9 @@ export class DataFactory {
27
27
  personId,
28
28
  name,
29
29
  role: overrides?.role ?? seededFaker.person.jobTitle(),
30
- email: overrides?.email ?? `${firstName}.${lastName}.${personId}@example.test`.toLowerCase(),
30
+ email:
31
+ overrides?.email ??
32
+ `${firstName}.${lastName}.${personId}@example.test`.toLowerCase(),
31
33
  ...overrides
32
34
  };
33
35
  }
@@ -7,7 +7,7 @@ export class IdGenerator {
7
7
  next(prefix: string): string {
8
8
  const counter = (this.counters.get(prefix) ?? 0) + 1;
9
9
  this.counters.set(prefix, counter);
10
- return `${prefix}-${this.runId}-${String(counter).padStart(4, "0")}`;
10
+ return `${prefix}-${this.runId}-${String(counter).padStart(4, '0')}`;
11
11
  }
12
12
 
13
13
  nextSequence(prefix: string): number {
@@ -1,8 +1,8 @@
1
1
  // Seeded faker wrapper so generated values stay stable for a given run id.
2
- import { Faker, en } from "@faker-js/faker";
2
+ import { Faker, en } from '@faker-js/faker';
3
3
 
4
4
  function hashSeed(value: string): number {
5
- return value.split("").reduce((seed, character) => {
5
+ return value.split('').reduce((seed, character) => {
6
6
  return ((seed << 5) - seed + character.charCodeAt(0)) | 0;
7
7
  }, 0);
8
8
  }
@@ -1,25 +1,25 @@
1
- const express = require("express");
1
+ const express = require('express');
2
2
 
3
- const { createPerson, state } = require("./store");
3
+ const { createPerson, state } = require('./store');
4
4
 
5
5
  const app = express();
6
- const host = process.env.HOST || "0.0.0.0";
7
- const port = Number(process.env.PORT || "3001");
6
+ const host = process.env.HOST || '0.0.0.0';
7
+ const port = Number(process.env.PORT || '3001');
8
8
 
9
9
  app.use(express.json());
10
10
 
11
- app.get("/health", (_request, response) => {
12
- response.json({ status: "ok" });
11
+ app.get('/health', (_request, response) => {
12
+ response.json({ status: 'ok' });
13
13
  });
14
14
 
15
- app.get("/people", (_request, response) => {
15
+ app.get('/people', (_request, response) => {
16
16
  response.json(state.people);
17
17
  });
18
18
 
19
- app.post("/people", (request, response) => {
19
+ app.post('/people', (request, response) => {
20
20
  const { name, role, email } = request.body;
21
21
  if (!name || !role || !email) {
22
- response.status(400).json({ error: "name, role, and email are required" });
22
+ response.status(400).json({ error: 'name, role, and email are required' });
23
23
  return;
24
24
  }
25
25
 
@@ -7,7 +7,7 @@ const state = {
7
7
 
8
8
  function nextId() {
9
9
  state.counters.person += 1;
10
- return `person-${String(state.counters.person).padStart(4, "0")}`;
10
+ return `person-${String(state.counters.person).padStart(4, '0')}`;
11
11
  }
12
12
 
13
13
  function getPerson(personId) {
@@ -1,6 +1,6 @@
1
1
  body {
2
2
  margin: 0;
3
- font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
3
+ font-family: 'IBM Plex Sans', 'Segoe UI', sans-serif;
4
4
  background: linear-gradient(180deg, #f5f7fb 0%, #eef2f7 100%);
5
5
  color: #102038;
6
6
  }