@postxl/generators 1.16.0 → 1.17.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.
- package/dist/base/template/.claude/commands/README.md +65 -0
- package/dist/base/template/.claude/settings.json +3 -1
- package/dist/base/template/.github/.copilot-prompts.json +22 -0
- package/dist/devops/generators/e2e-yml.generator.js +18 -1
- package/dist/devops/generators/e2e-yml.generator.js.map +1 -1
- package/dist/devops/generators/frontend-dockerfile.generator.js +1 -2
- package/dist/devops/generators/frontend-dockerfile.generator.js.map +1 -1
- package/dist/devops/generators/jenkinsfile.generator.js +2 -6
- package/dist/devops/generators/jenkinsfile.generator.js.map +1 -1
- package/dist/devops/template/docker/frontend/entrypoint.sh +2 -5
- package/dist/e2e/e2e.generator.js +44 -1
- package/dist/e2e/e2e.generator.js.map +1 -1
- package/dist/e2e/template/.claude/commands/prepare-e2e-tests.md +251 -0
- package/dist/e2e/template/.claude/commands/run-e2e-tests.md +221 -0
- package/dist/e2e/template/scripts/e2e.sh +399 -0
- package/dist/frontend-core/frontend.generator.js +2 -2
- package/dist/frontend-core/frontend.generator.js.map +1 -1
- package/dist/frontend-core/template/.env.example +0 -10
- package/dist/frontend-core/template/README.md +0 -7
- package/dist/frontend-core/template/src/lib/config.ts +1 -9
- package/dist/frontend-core/template/vite.config.ts +2 -1
- package/package.json +3 -3
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Custom Commands for Claude Code
|
|
2
|
+
|
|
3
|
+
This directory contains custom command documentation that Claude Code can reference when assisting with development tasks.
|
|
4
|
+
|
|
5
|
+
## Available Commands
|
|
6
|
+
|
|
7
|
+
### 1. Run E2E Tests (`run-e2e-tests.md`)
|
|
8
|
+
Comprehensive instructions for running end-to-end tests using Playwright.
|
|
9
|
+
|
|
10
|
+
**Trigger phrases:**
|
|
11
|
+
- "run e2e tests"
|
|
12
|
+
- "run end-to-end tests"
|
|
13
|
+
- "execute e2e test suite"
|
|
14
|
+
|
|
15
|
+
### 2. Fix CI (`fix-ci.md`)
|
|
16
|
+
Guidance for troubleshooting and fixing CI/CD pipeline issues.
|
|
17
|
+
|
|
18
|
+
**Trigger phrases:**
|
|
19
|
+
- "fix ci"
|
|
20
|
+
- "help with continuous integration"
|
|
21
|
+
|
|
22
|
+
### 3. Browser Observe (`browser-observe.md`)
|
|
23
|
+
Instructions for browser-based testing and observation workflows.
|
|
24
|
+
|
|
25
|
+
**Trigger phrases:**
|
|
26
|
+
- "observe browser"
|
|
27
|
+
- "start browser observation"
|
|
28
|
+
|
|
29
|
+
### 4. Prepare E2E Tests (`prepare-e2e-tests.md`)
|
|
30
|
+
Analyzes code changes, determines needed E2E test coverage, writes test specs, and runs them to verify they pass.
|
|
31
|
+
|
|
32
|
+
**Trigger phrases:**
|
|
33
|
+
- "prepare e2e tests"
|
|
34
|
+
- "write e2e tests for my changes"
|
|
35
|
+
- "add e2e test coverage"
|
|
36
|
+
|
|
37
|
+
## How It Works
|
|
38
|
+
|
|
39
|
+
When you ask Claude to perform one of these tasks, it will:
|
|
40
|
+
1. Automatically detect the request based on your natural language
|
|
41
|
+
2. Reference the appropriate command file
|
|
42
|
+
3. Follow the detailed instructions in that file
|
|
43
|
+
4. Execute the necessary steps to complete the task
|
|
44
|
+
|
|
45
|
+
## Adding New Commands
|
|
46
|
+
|
|
47
|
+
To add a new custom command:
|
|
48
|
+
|
|
49
|
+
1. Create a new markdown file in this directory with a descriptive name (e.g., `deploy-staging.md`)
|
|
50
|
+
2. Write clear, detailed instructions including:
|
|
51
|
+
- Overview of the task
|
|
52
|
+
- Prerequisites
|
|
53
|
+
- Step-by-step instructions
|
|
54
|
+
- Common issues and troubleshooting
|
|
55
|
+
- Examples
|
|
56
|
+
3. Update this README to list the new command
|
|
57
|
+
5. Commit all files to version control
|
|
58
|
+
|
|
59
|
+
## Best Practices
|
|
60
|
+
|
|
61
|
+
- **Be specific**: Include exact commands, file paths, and configuration details
|
|
62
|
+
- **Include context**: Explain why certain steps are necessary
|
|
63
|
+
- **Add troubleshooting**: Document common errors and solutions
|
|
64
|
+
- **Use examples**: Show concrete examples of usage
|
|
65
|
+
- **Keep updated**: Review and update commands as the project evolves
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
"Read CLAUDE.md for project-specific guidance before making changes",
|
|
5
5
|
"This is a PostXL-generated project - see the main PostXL monorepo CLAUDE.md for full documentation",
|
|
6
6
|
"Always run type checks after code changes: pnpm run test:types",
|
|
7
|
-
"Use @postxl/ui-components for all UI elements - never create custom generic UI primitives"
|
|
7
|
+
"Use @postxl/ui-components for all UI elements - never create custom generic UI primitives",
|
|
8
|
+
"When asked to 'run e2e tests', 'run end-to-end tests', or 'execute e2e': ALWAYS read the detailed instructions from .claude/commands/run-e2e-tests.md first, then run the automated script at scripts/e2e.sh from the project root",
|
|
9
|
+
"When significant frontend or backend changes are made (new pages, routes, forms, tables, or API endpoints), proactively suggest running /prepare-e2e-tests to ensure E2E test coverage"
|
|
8
10
|
]
|
|
9
11
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompts": [
|
|
3
|
+
{
|
|
4
|
+
"name": "Run E2E Tests",
|
|
5
|
+
"description": "Run end-to-end tests using Playwright",
|
|
6
|
+
"path": "../.claude/commands/run-e2e-tests.md",
|
|
7
|
+
"tags": ["testing", "e2e", "playwright"]
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "Fix CI",
|
|
11
|
+
"description": "Help fix CI/CD pipeline issues",
|
|
12
|
+
"path": "../.claude/commands/fix-ci.md",
|
|
13
|
+
"tags": ["ci", "devops"]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "Browser Observe",
|
|
17
|
+
"description": "Browser testing and observation workflows",
|
|
18
|
+
"path": "../.claude/commands/browser-observe.md",
|
|
19
|
+
"tags": ["testing", "browser"]
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -70,7 +70,6 @@ function generateE2EYaml(context) {
|
|
|
70
70
|
env:
|
|
71
71
|
VITE_PUBLIC_API_URL: http://localhost:3001
|
|
72
72
|
VITE_PUBLIC_BASE_URL: http://localhost:3000
|
|
73
|
-
VITE_PUBLIC_MAPBOX_ACCESS_TOKEN: \${{ secrets.MAPBOX_ACCESS_TOKEN }}
|
|
74
73
|
|
|
75
74
|
- name: Check if Backend and Frontend are running and Print Logs
|
|
76
75
|
run: |
|
|
@@ -164,6 +163,14 @@ function generateE2EYaml(context) {
|
|
|
164
163
|
ENABLE_BACKEND_SHUTDOWN: true
|
|
165
164
|
PLAYWRIGHT_WORKERS: 2
|
|
166
165
|
|
|
166
|
+
- name: Dump runtime logs on failure
|
|
167
|
+
if: \${{ failure() }}
|
|
168
|
+
run: |
|
|
169
|
+
echo "Backend runtime log tail:"
|
|
170
|
+
tail -n 500 backend/backend.log || true
|
|
171
|
+
echo "Frontend runtime log tail:"
|
|
172
|
+
tail -n 500 frontend/frontend.log || true
|
|
173
|
+
|
|
167
174
|
- name: Upload Playwright Report
|
|
168
175
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 #v4.6.2
|
|
169
176
|
if: \${{ !cancelled() }}
|
|
@@ -172,6 +179,16 @@ function generateE2EYaml(context) {
|
|
|
172
179
|
path: e2e/test-results
|
|
173
180
|
retention-days: 5
|
|
174
181
|
|
|
182
|
+
- name: Upload Runtime Logs
|
|
183
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 #v4.6.2
|
|
184
|
+
if: \${{ failure() && !cancelled() }}
|
|
185
|
+
with:
|
|
186
|
+
name: runtime-logs
|
|
187
|
+
path: |
|
|
188
|
+
backend/backend.log
|
|
189
|
+
frontend/frontend.log
|
|
190
|
+
retention-days: 5
|
|
191
|
+
|
|
175
192
|
- name: Upload Backend Coverage
|
|
176
193
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 #v4.6.2
|
|
177
194
|
if: \${{ !cancelled() }}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"e2e-yml.generator.js","sourceRoot":"","sources":["../../../src/devops/generators/e2e-yml.generator.ts"],"names":[],"mappings":";;AAEA,
|
|
1
|
+
{"version":3,"file":"e2e-yml.generator.js","sourceRoot":"","sources":["../../../src/devops/generators/e2e-yml.generator.ts"],"names":[],"mappings":";;AAEA,0CAqNC;AArND,SAAgB,eAAe,CAAC,OAA4B;IAC1D,MAAM,EACJ,MAAM,EAAE,EAAE,QAAQ,EAAE,GACrB,GAAG,OAAO,CAAA;IACX,0FAA0F;IAC1F,MAAM,gBAAgB,GAAG,gDAAgD,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;IAE9F,OAAO,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;QAqBT,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;UAoB9C,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;cAgB3C,QAAQ,CAAC,CAAC,CAAC,yBAAyB,gBAAgB,GAAG,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoJzE,CAAA;AACD,CAAC;AAED,MAAM,sBAAsB,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAkB,EAAE,EAAE,CAAC;;;;;6BAKxD,QAAQ;iCACJ,QAAQ;2BACd,QAAQ;;;;;;;;;CASlC,CAAA;AAED,MAAM,aAAa,GAAG,CAAC,gBAAwB,EAAE,EAAE,CAAC;;;;;oCAKhB,gBAAgB;CACnD,CAAA"}
|
|
@@ -28,8 +28,7 @@ ENV TURBO_TEAM=postxl
|
|
|
28
28
|
# Safely use Turborepo secret according to https://docs.docker.com/build/building/secrets/
|
|
29
29
|
RUN --mount=type=secret,id=TURBO_TOKEN \
|
|
30
30
|
VITE_PUBLIC_API_URL=PLACEHOLDER_VITE_PUBLIC_API_URL VITE_PUBLIC_BASE_URL=PLACEHOLDER_VITE_PUBLIC_BASE_URL \
|
|
31
|
-
|
|
32
|
-
VITE_PUBLIC_ANALYTICS_SCRIPT_URL=PLACEHOLDER_VITE_PUBLIC_ANALYTICS_SCRIPT_URL VITE_PUBLIC_KEYCLOAK_URL=PLACEHOLDER_VITE_PUBLIC_KEYCLOAK_URL \
|
|
31
|
+
VITE_PUBLIC_KEYCLOAK_URL=PLACEHOLDER_VITE_PUBLIC_KEYCLOAK_URL \
|
|
33
32
|
VITE_PUBLIC_KEYCLOAK_CLIENT_ID=PLACEHOLDER_VITE_PUBLIC_KEYCLOAK_CLIENT_ID VITE_PUBLIC_KEYCLOAK_REALM=PLACEHOLDER_VITE_PUBLIC_KEYCLOAK_REALM \
|
|
34
33
|
VITE_PUBLIC_CHECK_LOGIN_IFRAME=PLACEHOLDER_VITE_PUBLIC_CHECK_LOGIN_IFRAME \
|
|
35
34
|
pnpm ${isWorkspace ? `turbo:ci build --filter=@postxl/${slug} --filter=@postxl/${slug}-frontend --filter=@postxl/${slug}-api` : `turbo run build --filter=@postxl/${slug}-frontend --filter=@postxl/${slug}-api`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontend-dockerfile.generator.js","sourceRoot":"","sources":["../../../src/devops/generators/frontend-dockerfile.generator.ts"],"names":[],"mappings":";;AAEA,gEAKC;AALD,SAAgB,0BAA0B,CAAC,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAuB;IAC/F,MAAM,WAAW,GAAG,WAAW,KAAK,WAAW,CAAA;IAC/C,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,YAAY,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IAExD,OAAO,kBAAkB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;AAC7D,CAAC;AAED,MAAM,kBAAkB,GAAG,CAAC,EAC1B,SAAS,EACT,IAAI,EACJ,WAAW,GAKZ,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAA;;oCAEK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW
|
|
1
|
+
{"version":3,"file":"frontend-dockerfile.generator.js","sourceRoot":"","sources":["../../../src/devops/generators/frontend-dockerfile.generator.ts"],"names":[],"mappings":";;AAEA,gEAKC;AALD,SAAgB,0BAA0B,CAAC,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAuB;IAC/F,MAAM,WAAW,GAAG,WAAW,KAAK,WAAW,CAAA;IAC/C,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,YAAY,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IAExD,OAAO,kBAAkB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;AAC7D,CAAC;AAED,MAAM,kBAAkB,GAAG,CAAC,EAC1B,SAAS,EACT,IAAI,EACJ,WAAW,GAKZ,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAA;;oCAEK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW;;;;;;;;;;;;;;;;;;;;;;;SAuBzD,WAAW,CAAC,CAAC,CAAC,mCAAmC,IAAI,qBAAqB,IAAI,8BAA8B,IAAI,MAAM,CAAC,CAAC,CAAC,oCAAoC,IAAI,8BAA8B,IAAI,MAAM;;;;;;;;;;;;;6BAarL,SAAS;;;6BAGT,SAAS;;;aAGzB,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;aAUxC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;CAUpD,CAAA"}
|
|
@@ -38,8 +38,7 @@ function generateJenkinsfile({ devOps: { database, keycloak, useS3 }, schema: {
|
|
|
38
38
|
-e KEYCLOAK_LOGOUT_REDIRECT_URL=\${KEYCLOAK_LOGOUT_REDIRECT_URL} \\`
|
|
39
39
|
: '';
|
|
40
40
|
const keycloakRunEnvFrontend = keycloak
|
|
41
|
-
? `
|
|
42
|
-
-e VITE_PUBLIC_KEYCLOAK_URL=\${KEYCLOAK_URL} \\
|
|
41
|
+
? ` -e VITE_PUBLIC_KEYCLOAK_URL=\${KEYCLOAK_URL} \\
|
|
43
42
|
-e VITE_PUBLIC_KEYCLOAK_CLIENT_ID=\${KEYCLOAK_CLIENT_ID} \\
|
|
44
43
|
-e VITE_PUBLIC_KEYCLOAK_REALM=\${KEYCLOAK_REALM} \\`
|
|
45
44
|
: '';
|
|
@@ -82,7 +81,6 @@ function generateJenkinsfile({ devOps: { database, keycloak, useS3 }, schema: {
|
|
|
82
81
|
return /*Jenkinsfile*/ `
|
|
83
82
|
def secrets = [${dbSecrets}${keycloakSecrets}
|
|
84
83
|
[envVar: 'TURBO_TOKEN', secretRef: 'op://PostXL service/Turborepo Cache Token/password'],
|
|
85
|
-
[envVar: 'MAPBOX_ACCESS_TOKEN', secretRef: 'op://PostXL service/Mapbox Access Token/password'],
|
|
86
84
|
]
|
|
87
85
|
|
|
88
86
|
pipeline {
|
|
@@ -154,9 +152,7 @@ pipeline {
|
|
|
154
152
|
\${REGISTRY}/\${PROJECT_NAME}_backend:latest"
|
|
155
153
|
sh "\${SSH_COMMAND} docker run -d --name \${PROJECT_NAME}_frontend \\
|
|
156
154
|
-e VITE_PUBLIC_API_URL=\${API_URL} \\
|
|
157
|
-
-e VITE_PUBLIC_BASE_URL=\${FRONTEND_URL}
|
|
158
|
-
-e VITE_PUBLIC_MAPBOX_ACCESS_TOKEN=\${MAPBOX_ACCESS_TOKEN} \\
|
|
159
|
-
-e VITE_PUBLIC_ENABLE_ANALYTICS=false \\${keycloakRunEnvFrontend}
|
|
155
|
+
-e VITE_PUBLIC_BASE_URL=\${FRONTEND_URL} \\${keycloakRunEnvFrontend ? `\n${keycloakRunEnvFrontend}` : ''}
|
|
160
156
|
-e VITE_PUBLIC_CHECK_LOGIN_IFRAME=true \\
|
|
161
157
|
-p 0.0.0.0:\${PORT_MAPPING_FRONTEND} \\
|
|
162
158
|
\${REGISTRY}/\${PROJECT_NAME}_frontend:latest"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jenkinsfile.generator.js","sourceRoot":"","sources":["../../../src/devops/generators/jenkinsfile.generator.ts"],"names":[],"mappings":";;AAEA,
|
|
1
|
+
{"version":3,"file":"jenkinsfile.generator.js","sourceRoot":"","sources":["../../../src/devops/generators/jenkinsfile.generator.ts"],"names":[],"mappings":";;AAEA,kDA+KC;AA/KD,SAAgB,mBAAmB,CAAC,EAClC,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,EACrC,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,GACT;IACpB,iFAAiF;IACjF,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IACtC,MAAM,SAAS,GAAG,QAAQ;QACxB,CAAC,CAAC;mFAC6E,IAAI;mFACJ,IAAI;KAClF;QACD,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,eAAe,GAAG,QAAQ;QAC9B,CAAC,CAAC;oGAC8F,IAAI;KACnG;QACD,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,WAAW,GAAG,QAAQ;QAC1B,CAAC,CAAC;;gCAE0B,IAAI;4BACR,IAAI;;;SAGvB;QACL,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,KAAK,GAAG,QAAQ;QACpB,CAAC,CAAC;wIACkI;QACpI,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,cAAc,GAAG,QAAQ;QAC7B,CAAC,CAAC;;;;;;0GAMoG;QACtG,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,sBAAsB,GAAG,QAAQ;QACrC,CAAC,CAAC;;sFAEgF;QAClF,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,KAAK,GAAG,KAAK;QACjB,CAAC,CAAC;;;;SAIG;QACL,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,QAAQ,GAAG,KAAK;QACpB,CAAC,CAAC;;;sEAGgE;QAClE,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,KAAK,GAAG;;;;;;;;;;;SAWP,CAAA;IAEP,MAAM,QAAQ,GAAG;;;;;;;;;;sGAUmF,CAAA;IAEpG,OAAO,eAAe,CAAC;iBACR,SAAS,GAAG,eAAe;;;;;;;;;;;;;0BAalB,IAAI;;;;;;6BAMD,IAAI;;;kCAGC,IAAI;;UAE5B,WAAW,GAAG,KAAK,GAAG,KAAK;;;;;;;sBAOf,WAAW,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,GAAG;;;;;;;;sBAQvD,WAAW,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;uEA2BN,KAAK,GAAG,cAAc,GAAG,QAAQ;wCAChE,QAAQ;;;;;;+EAM+B,sBAAsB,CAAC,CAAC,CAAC,KAAK,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;CAUzI,CAAA;AACD,CAAC"}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
|
|
3
|
-
if [ -z "$VITE_PUBLIC_API_URL" ] || [ -z "$VITE_PUBLIC_BASE_URL" ] || [ -z "$
|
|
4
|
-
echo "Error: VITE_PUBLIC_API_URL, VITE_PUBLIC_BASE_URL,
|
|
3
|
+
if [ -z "$VITE_PUBLIC_API_URL" ] || [ -z "$VITE_PUBLIC_BASE_URL" ] || [ -z "$VITE_PUBLIC_KEYCLOAK_URL" ] || [ -z "$VITE_PUBLIC_KEYCLOAK_CLIENT_ID" ] || [ -z "$VITE_PUBLIC_KEYCLOAK_REALM" ] || [ -z "$VITE_PUBLIC_CHECK_LOGIN_IFRAME" ]; then
|
|
4
|
+
echo "Error: VITE_PUBLIC_API_URL, VITE_PUBLIC_BASE_URL, VITE_PUBLIC_KEYCLOAK_URL, VITE_PUBLIC_KEYCLOAK_CLIENT_ID, VITE_PUBLIC_KEYCLOAK_REALM, VITE_PUBLIC_CHECK_LOGIN_IFRAME is not defined" >&2
|
|
5
5
|
exit 1
|
|
6
6
|
fi
|
|
7
7
|
|
|
8
8
|
find /app/frontend/dist \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#PLACEHOLDER_VITE_PUBLIC_API_URL#$VITE_PUBLIC_API_URL#g"
|
|
9
9
|
find /app/frontend/dist \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#PLACEHOLDER_VITE_PUBLIC_BASE_URL#$VITE_PUBLIC_BASE_URL#g"
|
|
10
|
-
find /app/frontend/dist \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#PLACEHOLDER_VITE_PUBLIC_MAPBOX_ACCESS_TOKEN#$VITE_PUBLIC_MAPBOX_ACCESS_TOKEN#g"
|
|
11
|
-
find /app/frontend/dist \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#PLACEHOLDER_VITE_PUBLIC_ENABLE_ANALYTICS#$VITE_PUBLIC_ENABLE_ANALYTICS#g"
|
|
12
|
-
find /app/frontend/dist \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#PLACEHOLDER_VITE_PUBLIC_ANALYTICS_SCRIPT_URL#$VITE_PUBLIC_ANALYTICS_SCRIPT_URL#g"
|
|
13
10
|
find /app/frontend/dist \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#PLACEHOLDER_VITE_PUBLIC_KEYCLOAK_URL#$VITE_PUBLIC_KEYCLOAK_URL#g"
|
|
14
11
|
find /app/frontend/dist \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#PLACEHOLDER_VITE_PUBLIC_KEYCLOAK_CLIENT_ID#$VITE_PUBLIC_KEYCLOAK_CLIENT_ID#g"
|
|
15
12
|
find /app/frontend/dist \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#PLACEHOLDER_VITE_PUBLIC_KEYCLOAK_REALM#$VITE_PUBLIC_KEYCLOAK_REALM#g"
|
|
@@ -54,7 +54,23 @@ exports.generator = {
|
|
|
54
54
|
},
|
|
55
55
|
generate: async (context) => {
|
|
56
56
|
const vfs = new Generator.VirtualFileSystem();
|
|
57
|
-
const
|
|
57
|
+
const { projectType, slug } = context.schema;
|
|
58
|
+
const isWorkspace = projectType === 'workspace';
|
|
59
|
+
// Template context with project-type-aware values
|
|
60
|
+
const templateContext = {
|
|
61
|
+
schema: context.schema,
|
|
62
|
+
// For e2e.sh
|
|
63
|
+
monorepoRootExpr: isWorkspace ? '"$(cd "$PROJECT_DIR/../.." && pwd)"' : '"$PROJECT_DIR"',
|
|
64
|
+
dockerWorkDir: isWorkspace ? `/pxl/projects/${slug}/e2e` : '/pxl/e2e',
|
|
65
|
+
e2eArtifactsPath: isWorkspace ? `/projects/${slug}/e2e` : '/e2e',
|
|
66
|
+
nodeShimsCleanup: isWorkspace
|
|
67
|
+
? 'rm -f /pxl/node_modules/.bin/node /pxl/projects/*/node_modules/.bin/node 2>/dev/null'
|
|
68
|
+
: 'rm -f /pxl/node_modules/.bin/node 2>/dev/null',
|
|
69
|
+
// For run-e2e-tests.md
|
|
70
|
+
cdProjectDir: isWorkspace ? `cd projects/${slug}\n` : '',
|
|
71
|
+
backendPath: isWorkspace ? `projects/${slug}/backend` : 'backend',
|
|
72
|
+
frontendPath: isWorkspace ? `projects/${slug}/frontend` : 'frontend',
|
|
73
|
+
};
|
|
58
74
|
// Load e2e folder, excluding files that need template substitution
|
|
59
75
|
await vfs.loadFolder({
|
|
60
76
|
diskPath: path.resolve(__dirname, './template/e2e'),
|
|
@@ -70,6 +86,33 @@ exports.generator = {
|
|
|
70
86
|
throw new Error(`Failed to generate package.json: ${packageJsonContent.unwrapErr().message}`);
|
|
71
87
|
}
|
|
72
88
|
vfs.write(`/e2e/${PACKAGE_JSON_FILENAME}`, packageJsonContent.unwrap());
|
|
89
|
+
// Generate e2e.sh with project-type-aware paths
|
|
90
|
+
const e2eShContent = await Generator.generateFromTemplate({
|
|
91
|
+
file: path.resolve(__dirname, './template/scripts/e2e.sh'),
|
|
92
|
+
context: templateContext,
|
|
93
|
+
});
|
|
94
|
+
if (e2eShContent.isErr()) {
|
|
95
|
+
throw new Error(`Failed to generate e2e.sh: ${e2eShContent.unwrapErr().message}`);
|
|
96
|
+
}
|
|
97
|
+
vfs.write('/scripts/e2e.sh', e2eShContent.unwrap());
|
|
98
|
+
// Generate run-e2e-tests.md with project-type-aware paths
|
|
99
|
+
const runE2eTestsMdContent = await Generator.generateFromTemplate({
|
|
100
|
+
file: path.resolve(__dirname, './template/.claude/commands/run-e2e-tests.md'),
|
|
101
|
+
context: templateContext,
|
|
102
|
+
});
|
|
103
|
+
if (runE2eTestsMdContent.isErr()) {
|
|
104
|
+
throw new Error(`Failed to generate run-e2e-tests.md: ${runE2eTestsMdContent.unwrapErr().message}`);
|
|
105
|
+
}
|
|
106
|
+
vfs.write('/.claude/commands/run-e2e-tests.md', runE2eTestsMdContent.unwrap());
|
|
107
|
+
// Generate prepare-e2e-tests.md with project-type-aware paths
|
|
108
|
+
const prepareE2eTestsMdContent = await Generator.generateFromTemplate({
|
|
109
|
+
file: path.resolve(__dirname, './template/.claude/commands/prepare-e2e-tests.md'),
|
|
110
|
+
context: templateContext,
|
|
111
|
+
});
|
|
112
|
+
if (prepareE2eTestsMdContent.isErr()) {
|
|
113
|
+
throw new Error(`Failed to generate prepare-e2e-tests.md: ${prepareE2eTestsMdContent.unwrapErr().message}`);
|
|
114
|
+
}
|
|
115
|
+
vfs.write('/.claude/commands/prepare-e2e-tests.md', prepareE2eTestsMdContent.unwrap());
|
|
73
116
|
// write dynamic files
|
|
74
117
|
vfs.write('/e2e/support/model-test-ids.ts', (0, model_test_id_generator_1.generateModelTestIds)(context.e2e));
|
|
75
118
|
vfs.write('/scripts/docker.sh', (0, docker_sh_generator_1.generateDockerSh)(context));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"e2e.generator.js","sourceRoot":"","sources":["../../src/e2e/e2e.generator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAiC;AAEjC,6DAA8C;AAI9C,0EAAmE;AACnE,kFAA2E;AAE3E,MAAM,qBAAqB,GAAG,cAAc,CAAA;AAsB/B,QAAA,WAAW,GAAG,SAAS,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAA;AAErD,QAAA,SAAS,GAAiC;IACrD,EAAE,EAAE,mBAAW;IACf,QAAQ,EAAE,CAAC,mBAAW,CAAC;IAEvB,QAAQ,EAAE,CAAiD,GAAY,EAAiB,EAAE;QACxF,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAA;QACvF,OAAO;YACL,GAAG,GAAG;YACN,GAAG,EAAE;gBACH,OAAO,EAAE,EAAE;aACZ;SACF,CAAA;IACH,CAAC;IAED,QAAQ,EAAE,KAAK,EAAiC,OAAgB,EAAoB,EAAE;QACpF,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,iBAAiB,EAAE,CAAA;QAC7C,MAAM,eAAe,GAAG,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"e2e.generator.js","sourceRoot":"","sources":["../../src/e2e/e2e.generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Prepare E2E Tests
|
|
2
|
+
|
|
3
|
+
You are tasked with analyzing code changes and preparing end-to-end (E2E) tests for the <% schema.slug %> project.
|
|
4
|
+
|
|
5
|
+
## Arguments
|
|
6
|
+
|
|
7
|
+
- `$ARGUMENTS` - Optional: specific features or areas to focus testing on. If not provided, analyze the git diff automatically.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
This command analyzes your recent code changes, determines what E2E test coverage is needed, writes the test specs following existing patterns, and runs them to verify they pass.
|
|
12
|
+
|
|
13
|
+
## Step 1: Analyze Changes
|
|
14
|
+
|
|
15
|
+
### 1.1 Get Changed Files
|
|
16
|
+
|
|
17
|
+
Determine what has changed by examining the git diff:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# If on a feature branch, diff against main
|
|
21
|
+
git diff main...HEAD --name-only
|
|
22
|
+
|
|
23
|
+
# Also check for any uncommitted changes
|
|
24
|
+
git diff --name-only
|
|
25
|
+
git diff --staged --name-only
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If `$ARGUMENTS` was provided, focus analysis on the specified features or areas instead of the full diff.
|
|
29
|
+
|
|
30
|
+
### 1.2 Categorize Changes
|
|
31
|
+
|
|
32
|
+
Read each changed file and categorize it:
|
|
33
|
+
|
|
34
|
+
- **Frontend pages/routes**: Files in `<% frontendPath %>/src/routes/` — need navigation tests, content verification, and visual snapshots
|
|
35
|
+
- **Frontend components**: Files in `<% frontendPath %>/src/components/` or `<% frontendPath %>/src/pages/` — need interaction tests
|
|
36
|
+
- **Backend endpoints/routers**: Files in `<% backendPath %>/libs/router-trpc/` or `<% backendPath %>/libs/actions/` — need frontend integration tests verifying data display
|
|
37
|
+
- **Backend views**: Files in `<% backendPath %>/libs/view/` — need data display verification tests
|
|
38
|
+
- **Shared types**: Files in `<% backendPath %>/libs/types/` — may affect existing tests
|
|
39
|
+
- **Schema changes**: `postxl-schema.json` — affect models and may need comprehensive new tests
|
|
40
|
+
- **Forms**: Components containing form elements (TanStack Form, inputs, selects) — need form interaction tests
|
|
41
|
+
- **Tables/DataGrids**: Components using DataGrid — need data display, sorting, and filtering tests
|
|
42
|
+
|
|
43
|
+
### 1.3 Read Changed Files
|
|
44
|
+
|
|
45
|
+
For each changed file, read its content to understand:
|
|
46
|
+
|
|
47
|
+
- What routes/URLs are added or modified
|
|
48
|
+
- What data is displayed (model names, field names)
|
|
49
|
+
- What user interactions are possible (clicks, form fills, navigation)
|
|
50
|
+
- What test IDs are used (`data-test-id` attributes)
|
|
51
|
+
|
|
52
|
+
Also read these reference files for context:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
e2e/support/model-test-ids.ts # Available test IDs
|
|
56
|
+
e2e/fixtures/test-fixtures.ts # Available test fixtures
|
|
57
|
+
e2e/support/wait-for-page-loaded.ts # Page loading utility
|
|
58
|
+
e2e/support/page-stability.ts # API stability utility
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 1.4 Check Existing Test Coverage
|
|
62
|
+
|
|
63
|
+
Read all existing spec files in `e2e/specs/` to understand what is already covered. Do not duplicate existing tests.
|
|
64
|
+
|
|
65
|
+
## Step 2: Determine Test Coverage
|
|
66
|
+
|
|
67
|
+
Based on the analysis, determine which tests to write:
|
|
68
|
+
|
|
69
|
+
### For New Pages/Routes
|
|
70
|
+
|
|
71
|
+
- **Navigation test**: Verify the page is accessible at its URL
|
|
72
|
+
- **Content verification test**: Verify key content elements are visible
|
|
73
|
+
- **Visual regression snapshot**: Capture a baseline screenshot with `toHaveScreenshot()`
|
|
74
|
+
|
|
75
|
+
### For New Forms
|
|
76
|
+
|
|
77
|
+
- **Form display test**: Verify form fields are rendered correctly
|
|
78
|
+
- **Form interaction test**: Fill fields, submit, and verify success feedback
|
|
79
|
+
- **Form validation test**: Submit with invalid data and verify error messages
|
|
80
|
+
|
|
81
|
+
### For New Tables/Data Views
|
|
82
|
+
|
|
83
|
+
- **Data display test**: Verify table renders with expected columns and data
|
|
84
|
+
- **Sorting test**: Click column headers and verify sort order (if sortable)
|
|
85
|
+
- **Empty state test**: Verify empty state message when no data exists
|
|
86
|
+
|
|
87
|
+
### For API/Backend Changes
|
|
88
|
+
|
|
89
|
+
- **Data integration test**: Verify the frontend correctly displays API data
|
|
90
|
+
- **Error state test**: Verify error handling when API returns errors (if applicable)
|
|
91
|
+
|
|
92
|
+
### For UI Component Changes
|
|
93
|
+
|
|
94
|
+
- **Visual regression snapshot**: Capture screenshots of affected pages
|
|
95
|
+
- **Interaction test**: Verify component interactions work correctly
|
|
96
|
+
|
|
97
|
+
## Step 3: Write Tests
|
|
98
|
+
|
|
99
|
+
### 3.1 Test File Structure
|
|
100
|
+
|
|
101
|
+
Create new test files in `e2e/specs/` following the naming convention `<feature>.spec.ts`.
|
|
102
|
+
|
|
103
|
+
**CRITICAL**: Always use the project's custom test fixtures, NOT bare Playwright imports:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { expect, test } from '../fixtures/test-fixtures'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 3.2 Test Patterns to Follow
|
|
110
|
+
|
|
111
|
+
**Reference the existing examples:**
|
|
112
|
+
|
|
113
|
+
- `e2e/specs/example.spec.ts` — basic navigation, visual snapshots, content verification
|
|
114
|
+
- `e2e/specs/example-backend-isolation.spec.ts` — backend isolation pattern
|
|
115
|
+
|
|
116
|
+
**Navigation and Snapshot Test Pattern:**
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { expect, test } from '../fixtures/test-fixtures'
|
|
120
|
+
|
|
121
|
+
test.describe('<Feature Name>', () => {
|
|
122
|
+
test('<Page name> page is accessible', async ({ page }) => {
|
|
123
|
+
await page.goto('/<route>')
|
|
124
|
+
await expect(page).toHaveURL(/.*\/<route>$/)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
test('<Page name> page visual snapshot', async ({ page }) => {
|
|
128
|
+
await page.goto('/<route>')
|
|
129
|
+
await expect(page).toHaveScreenshot()
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Content Verification Pattern:**
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
test('<Page> displays expected content', async ({ page }) => {
|
|
138
|
+
await page.goto('/<route>')
|
|
139
|
+
await expect(page.getByText('Expected Heading')).toBeVisible()
|
|
140
|
+
await expect(page.getByTestId('some-test-id')).toBeVisible()
|
|
141
|
+
})
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Form Interaction Pattern:**
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
test.describe('<Form Name> Form', () => {
|
|
148
|
+
test('can fill and submit form', async ({ page }) => {
|
|
149
|
+
await page.goto('/<form-route>')
|
|
150
|
+
|
|
151
|
+
// Fill form fields
|
|
152
|
+
await page.getByLabel('Field Name').fill('Test Value')
|
|
153
|
+
await page.getByRole('combobox').click()
|
|
154
|
+
await page.getByRole('option', { name: 'Option' }).click()
|
|
155
|
+
|
|
156
|
+
// Submit
|
|
157
|
+
await page.getByRole('button', { name: 'Submit' }).click()
|
|
158
|
+
|
|
159
|
+
// Verify success
|
|
160
|
+
await expect(page.getByText('Success')).toBeVisible()
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Table/DataGrid Verification Pattern:**
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
test.describe('<Model> Table', () => {
|
|
169
|
+
test('displays data in table', async ({ page }) => {
|
|
170
|
+
await page.goto('/<admin-route>')
|
|
171
|
+
|
|
172
|
+
// Verify table headers
|
|
173
|
+
await expect(page.getByText('Column Header')).toBeVisible()
|
|
174
|
+
|
|
175
|
+
// Verify data is loaded
|
|
176
|
+
await expect(page.locator('table tbody tr').first()).toBeVisible()
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 3.3 Available Utilities
|
|
182
|
+
|
|
183
|
+
Use these utilities from the `support/` and `fixtures/` directories:
|
|
184
|
+
|
|
185
|
+
- `waitForPageLoaded(page)` from `support/wait-for-page-loaded` — wait for DOM + images
|
|
186
|
+
- `waitForAppIdle(page)` from `support/page-stability` — wait for API requests to complete
|
|
187
|
+
- `resetData()` from `support/reset-data` — reset backend data (stateful mode only)
|
|
188
|
+
- `setData(data)` from `support/set-data` — set specific test data (stateful mode only)
|
|
189
|
+
- `maskToast(page)` from `support/mask-toast` — hide toast notifications for screenshots
|
|
190
|
+
- `clickCheckboxByName(page, name)` from `support/checkbox-click` — click checkboxes reliably
|
|
191
|
+
- `MODEL_TEST_IDS` from `support/model-test-ids` — generated test IDs for models
|
|
192
|
+
- `DataMocker` from `@mock-data/dataMocker.class` — create mock data
|
|
193
|
+
|
|
194
|
+
### 3.4 Best Practices
|
|
195
|
+
|
|
196
|
+
1. **Always use custom test fixtures**: `import { expect, test } from '../fixtures/test-fixtures'`
|
|
197
|
+
2. **Use descriptive test names**: Explain what the test verifies, not how
|
|
198
|
+
3. **Use `data-test-id` attributes**: Prefer `getByTestId()` for stable selectors
|
|
199
|
+
4. **Use role-based selectors**: `getByRole('button', { name: '...' })` for accessibility
|
|
200
|
+
5. **Avoid `waitForTimeout()`**: Use condition-based waits instead
|
|
201
|
+
6. **Group related tests**: Use `test.describe()` blocks
|
|
202
|
+
7. **Mask toasts for screenshots**: Use `maskToast(page)` before `toHaveScreenshot()` if toasts interfere
|
|
203
|
+
8. **Do not modify existing test files** unless the changes specifically affect those tests
|
|
204
|
+
|
|
205
|
+
## Step 4: Run and Verify
|
|
206
|
+
|
|
207
|
+
### 4.1 Run the Tests
|
|
208
|
+
|
|
209
|
+
Execute the E2E tests using the automated script:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
<% cdProjectDir %>./scripts/e2e.sh
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 4.2 Handle First-Run Snapshot Baselines
|
|
216
|
+
|
|
217
|
+
New tests with `toHaveScreenshot()` will fail on the first run because no baseline snapshot exists yet. This is expected. Re-run the tests to create the baseline and confirm the snapshot is correct.
|
|
218
|
+
|
|
219
|
+
### 4.3 Investigate Failures
|
|
220
|
+
|
|
221
|
+
If tests fail for reasons other than missing baselines, follow the investigation workflow from the `run-e2e-tests` command:
|
|
222
|
+
|
|
223
|
+
1. **Parse test output** to identify which tests failed
|
|
224
|
+
2. **Read the last run status**: `cat e2e/test-results/.last-run.json`
|
|
225
|
+
3. **Read monocart report**: `cat e2e/monocart-report/index.json`
|
|
226
|
+
4. **Analyze failure type**: snapshot mismatch, element not found, timeout, assertion failure
|
|
227
|
+
5. **Read screenshots** from `e2e/test-results/` for visual context
|
|
228
|
+
6. **Fix and re-run** until all tests pass
|
|
229
|
+
|
|
230
|
+
### 4.4 Common Fixes
|
|
231
|
+
|
|
232
|
+
- **Element not found**: Add `waitForPageLoaded()` or check the selector matches the DOM
|
|
233
|
+
- **Timeout**: Add proper waits for API responses using `waitForAppIdle()`
|
|
234
|
+
- **Toast interfering with screenshot**: Add `maskToast(page)` before the screenshot assertion
|
|
235
|
+
- **Animation causing flaky snapshots**: The fixture auto-disables them, but custom CSS animations may need attention
|
|
236
|
+
|
|
237
|
+
## Step 5: Report Summary
|
|
238
|
+
|
|
239
|
+
After completing the process, provide:
|
|
240
|
+
|
|
241
|
+
- **Changes analyzed**: List of files categorized by type
|
|
242
|
+
- **Tests created**: List of new spec files with test names
|
|
243
|
+
- **Test results**: Pass/fail status
|
|
244
|
+
- **Coverage gaps**: Any areas that could not be automatically tested (e.g., require auth, complex multi-step workflows)
|
|
245
|
+
|
|
246
|
+
## Important Notes
|
|
247
|
+
|
|
248
|
+
- **Tests run in Docker** for consistent snapshots — never update snapshots outside Docker
|
|
249
|
+
- **Use stateless mode** as default for E2E tests
|
|
250
|
+
- **Follow existing patterns**: Match the coding style and structure of `example.spec.ts`
|
|
251
|
+
- **CRITICAL**: Always run tests in a NEW terminal separate from backend and frontend
|