@toolstackhq/create-qa-patterns 1.0.6 → 1.0.7
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 +15 -2
- package/index.js +130 -48
- package/package.json +1 -1
- package/templates/cypress-template/.env.example +5 -0
- package/templates/cypress-template/.github/workflows/cypress-tests.yml +47 -0
- package/templates/cypress-template/README.md +195 -0
- package/templates/cypress-template/config/environments.ts +37 -0
- package/templates/cypress-template/config/runtime-config.ts +46 -0
- package/templates/cypress-template/config/secret-manager.ts +19 -0
- package/templates/cypress-template/config/test-env.ts +13 -0
- package/templates/cypress-template/cypress/e2e/ui-journey.cy.ts +20 -0
- package/templates/cypress-template/cypress/support/app-config.ts +23 -0
- package/templates/cypress-template/cypress/support/commands.d.ts +15 -0
- package/templates/cypress-template/cypress/support/commands.ts +17 -0
- package/templates/cypress-template/cypress/support/data/data-factory.ts +33 -0
- package/templates/cypress-template/cypress/support/data/id-generator.ts +13 -0
- package/templates/cypress-template/cypress/support/data/seeded-faker.ts +11 -0
- package/templates/cypress-template/cypress/support/e2e.ts +1 -0
- package/templates/cypress-template/cypress/support/pages/login-page.ts +23 -0
- package/templates/cypress-template/cypress/support/pages/people-page.ts +59 -0
- package/templates/cypress-template/cypress.config.ts +32 -0
- package/templates/cypress-template/demo-apps/ui-demo-app/package.json +10 -0
- package/templates/cypress-template/demo-apps/ui-demo-app/public/styles.css +120 -0
- package/templates/cypress-template/demo-apps/ui-demo-app/src/server.js +149 -0
- package/templates/cypress-template/demo-apps/ui-demo-app/src/store.js +43 -0
- package/templates/cypress-template/demo-apps/ui-demo-app/src/templates.js +121 -0
- package/templates/cypress-template/eslint.config.mjs +93 -0
- package/templates/cypress-template/package-lock.json +3758 -0
- package/templates/cypress-template/package.json +31 -0
- package/templates/cypress-template/scripts/run-cypress.mjs +105 -0
- package/templates/cypress-template/scripts/run-tests.sh +6 -0
- package/templates/cypress-template/tsconfig.json +15 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toolstackhq/cypress-template",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Production-ready Cypress automation framework template with a deterministic UI starter app.",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "node ./scripts/run-cypress.mjs run",
|
|
8
|
+
"open": "node ./scripts/run-cypress.mjs open",
|
|
9
|
+
"cy:run": "cypress run",
|
|
10
|
+
"cy:open": "cypress open",
|
|
11
|
+
"demo:ui": "node ./demo-apps/ui-demo-app/src/server.js",
|
|
12
|
+
"lint": "eslint .",
|
|
13
|
+
"typecheck": "tsc --noEmit",
|
|
14
|
+
"ci": "bash ./scripts/run-tests.sh"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@faker-js/faker": "^8.4.1",
|
|
18
|
+
"dotenv": "^16.4.5",
|
|
19
|
+
"express": "^4.21.0",
|
|
20
|
+
"zod": "^3.23.8"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@eslint/js": "^9.12.0",
|
|
24
|
+
"@types/node": "^22.7.4",
|
|
25
|
+
"@typescript-eslint/eslint-plugin": "^8.8.1",
|
|
26
|
+
"@typescript-eslint/parser": "^8.8.1",
|
|
27
|
+
"cypress": "^13.17.0",
|
|
28
|
+
"eslint": "^9.12.0",
|
|
29
|
+
"typescript": "^5.6.2"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
|
|
7
|
+
import dotenv from "dotenv";
|
|
8
|
+
|
|
9
|
+
const mode = process.argv[2] ?? "run";
|
|
10
|
+
const args = process.argv.slice(3);
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
const healthUrl = "http://127.0.0.1:3000/health";
|
|
13
|
+
const environment = process.env.TEST_ENV ?? "dev";
|
|
14
|
+
const environmentDefaults = {
|
|
15
|
+
dev: "http://127.0.0.1:3000",
|
|
16
|
+
staging: "https://staging-ui.example.internal",
|
|
17
|
+
prod: "https://ui.example.internal"
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
dotenv.config({ path: path.resolve(cwd, ".env") });
|
|
21
|
+
dotenv.config({ path: path.resolve(cwd, `.env.${environment}`), override: true });
|
|
22
|
+
|
|
23
|
+
const uiBaseUrl =
|
|
24
|
+
process.env[`${environment.toUpperCase()}_UI_BASE_URL`] ??
|
|
25
|
+
process.env.UI_BASE_URL ??
|
|
26
|
+
environmentDefaults[environment] ??
|
|
27
|
+
environmentDefaults.dev;
|
|
28
|
+
|
|
29
|
+
const shouldAutoStartDemoApp =
|
|
30
|
+
environment === "dev" &&
|
|
31
|
+
uiBaseUrl === environmentDefaults.dev &&
|
|
32
|
+
process.env.CY_DISABLE_LOCAL_DEMO_APP !== "true";
|
|
33
|
+
|
|
34
|
+
function getCommandName(command) {
|
|
35
|
+
return process.platform === "win32" ? `${command}.cmd` : command;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function spawnCommand(command, commandArgs, options = {}) {
|
|
39
|
+
return spawn(getCommandName(command), commandArgs, {
|
|
40
|
+
cwd,
|
|
41
|
+
stdio: "inherit",
|
|
42
|
+
...options
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function waitForHealthcheck(url, timeoutMs = 30_000) {
|
|
47
|
+
const start = Date.now();
|
|
48
|
+
|
|
49
|
+
while (Date.now() - start < timeoutMs) {
|
|
50
|
+
try {
|
|
51
|
+
const response = await globalThis.fetch(url);
|
|
52
|
+
if (response.ok) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// Service is not ready yet.
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await new Promise((resolve) => globalThis.setTimeout(resolve, 500));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
throw new Error(`Timed out waiting for ${url}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function killChild(child) {
|
|
66
|
+
if (!child || child.killed) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
child.kill("SIGTERM");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function run() {
|
|
74
|
+
let demoAppProcess;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
if (shouldAutoStartDemoApp) {
|
|
78
|
+
demoAppProcess = spawnCommand("npm", ["run", "demo:ui"]);
|
|
79
|
+
await waitForHealthcheck(healthUrl);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const cypressCommand = mode === "open" ? "open" : "run";
|
|
83
|
+
const cypressProcess = spawnCommand("npx", ["cypress", cypressCommand, ...args]);
|
|
84
|
+
|
|
85
|
+
const exitCode = await new Promise((resolve) => {
|
|
86
|
+
cypressProcess.on("close", resolve);
|
|
87
|
+
cypressProcess.on("error", () => resolve(1));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (exitCode !== 0) {
|
|
91
|
+
process.exit(Number(exitCode) || 1);
|
|
92
|
+
}
|
|
93
|
+
} finally {
|
|
94
|
+
killChild(demoAppProcess);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
99
|
+
process.on(signal, () => process.exit(1));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
run().catch((error) => {
|
|
103
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022", "DOM"],
|
|
5
|
+
"module": "NodeNext",
|
|
6
|
+
"moduleResolution": "NodeNext",
|
|
7
|
+
"resolveJsonModule": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"noEmit": true,
|
|
12
|
+
"types": ["cypress", "node"]
|
|
13
|
+
},
|
|
14
|
+
"include": ["cypress/**/*.ts", "cypress/**/*.d.ts", "config/**/*.ts", "cypress.config.ts"]
|
|
15
|
+
}
|