qualia-framework 2.1.0
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 +50 -0
- package/bin/cli.js +519 -0
- package/framework/agents/architecture-strategist.md +53 -0
- package/framework/agents/backend-agent.md +150 -0
- package/framework/agents/code-simplicity-reviewer.md +86 -0
- package/framework/agents/frontend-agent.md +111 -0
- package/framework/agents/kieran-typescript-reviewer.md +96 -0
- package/framework/agents/performance-oracle.md +111 -0
- package/framework/agents/qualia-codebase-mapper.md +760 -0
- package/framework/agents/qualia-debugger.md +1203 -0
- package/framework/agents/qualia-executor.md +881 -0
- package/framework/agents/qualia-integration-checker.md +423 -0
- package/framework/agents/qualia-phase-researcher.md +453 -0
- package/framework/agents/qualia-plan-checker.md +699 -0
- package/framework/agents/qualia-planner.md +1241 -0
- package/framework/agents/qualia-project-researcher.md +602 -0
- package/framework/agents/qualia-research-synthesizer.md +236 -0
- package/framework/agents/qualia-roadmapper.md +605 -0
- package/framework/agents/qualia-verifier.md +685 -0
- package/framework/agents/team-orchestrator.md +228 -0
- package/framework/agents/teams/full-stack-team.md +48 -0
- package/framework/agents/teams/optimize-team.md +53 -0
- package/framework/agents/teams/review-team.md +62 -0
- package/framework/agents/teams/ship-team.md +86 -0
- package/framework/agents/test-agent.md +182 -0
- package/framework/askpass.sh +2 -0
- package/framework/commands/design.md +53 -0
- package/framework/commands/quick-db.md +22 -0
- package/framework/config/retention.json +35 -0
- package/framework/core/PRINCIPLES.md +77 -0
- package/framework/hooks/auto-format.sh +45 -0
- package/framework/hooks/block-env-edit.sh +42 -0
- package/framework/hooks/branch-guard.sh +46 -0
- package/framework/hooks/confirm-delete.sh +56 -0
- package/framework/hooks/migration-validate.sh +68 -0
- package/framework/hooks/notification-speak.sh +15 -0
- package/framework/hooks/pre-commit.sh +80 -0
- package/framework/hooks/pre-compact.sh +55 -0
- package/framework/hooks/pre-deploy-gate.sh +151 -0
- package/framework/hooks/qualia-colors.sh +32 -0
- package/framework/hooks/retention-cleanup.sh +43 -0
- package/framework/hooks/save-session-state.sh +153 -0
- package/framework/hooks/session-context-loader.sh +28 -0
- package/framework/hooks/session-learn.sh +30 -0
- package/framework/knowledge/claudecode-bible.md +1384 -0
- package/framework/knowledge/client-prefs.md +22 -0
- package/framework/knowledge/common-fixes.md +25 -0
- package/framework/knowledge/deployment-map.md +35 -0
- package/framework/knowledge/email-signature.html +1 -0
- package/framework/knowledge/employees.md +8 -0
- package/framework/knowledge/learned-patterns.md +51 -0
- package/framework/knowledge/optimization-research-2026.md +137 -0
- package/framework/knowledge/qualia-context.md +67 -0
- package/framework/knowledge/supabase-patterns.md +50 -0
- package/framework/knowledge/voice-agent-patterns.md +46 -0
- package/framework/qualia-engine/VERSION +1 -0
- package/framework/qualia-engine/bin/qualia-tools.js +2160 -0
- package/framework/qualia-engine/bin/qualia-tools.test.js +1054 -0
- package/framework/qualia-engine/references/checkpoints.md +775 -0
- package/framework/qualia-engine/references/continuation-format.md +249 -0
- package/framework/qualia-engine/references/decimal-phase-calculation.md +65 -0
- package/framework/qualia-engine/references/design-quality.md +56 -0
- package/framework/qualia-engine/references/git-integration.md +254 -0
- package/framework/qualia-engine/references/git-planning-commit.md +50 -0
- package/framework/qualia-engine/references/model-profile-resolution.md +32 -0
- package/framework/qualia-engine/references/model-profiles.md +73 -0
- package/framework/qualia-engine/references/phase-argument-parsing.md +61 -0
- package/framework/qualia-engine/references/planning-config.md +195 -0
- package/framework/qualia-engine/references/questioning.md +141 -0
- package/framework/qualia-engine/references/tdd.md +263 -0
- package/framework/qualia-engine/references/ui-brand.md +160 -0
- package/framework/qualia-engine/references/verification-patterns.md +612 -0
- package/framework/qualia-engine/templates/DEBUG.md +159 -0
- package/framework/qualia-engine/templates/DESIGN.md +81 -0
- package/framework/qualia-engine/templates/UAT.md +247 -0
- package/framework/qualia-engine/templates/codebase/architecture.md +255 -0
- package/framework/qualia-engine/templates/codebase/concerns.md +310 -0
- package/framework/qualia-engine/templates/codebase/conventions.md +307 -0
- package/framework/qualia-engine/templates/codebase/integrations.md +280 -0
- package/framework/qualia-engine/templates/codebase/stack.md +186 -0
- package/framework/qualia-engine/templates/codebase/structure.md +285 -0
- package/framework/qualia-engine/templates/codebase/testing.md +480 -0
- package/framework/qualia-engine/templates/config.json +35 -0
- package/framework/qualia-engine/templates/context.md +283 -0
- package/framework/qualia-engine/templates/continue-here.md +78 -0
- package/framework/qualia-engine/templates/debug-subagent-prompt.md +91 -0
- package/framework/qualia-engine/templates/discovery.md +146 -0
- package/framework/qualia-engine/templates/milestone-archive.md +123 -0
- package/framework/qualia-engine/templates/milestone.md +115 -0
- package/framework/qualia-engine/templates/phase-prompt.md +567 -0
- package/framework/qualia-engine/templates/planner-subagent-prompt.md +117 -0
- package/framework/qualia-engine/templates/project.md +184 -0
- package/framework/qualia-engine/templates/projects/ai-agent.md +156 -0
- package/framework/qualia-engine/templates/projects/mobile-app.md +181 -0
- package/framework/qualia-engine/templates/projects/voice-agent.md +134 -0
- package/framework/qualia-engine/templates/projects/website.md +137 -0
- package/framework/qualia-engine/templates/requirements.md +231 -0
- package/framework/qualia-engine/templates/research-project/ARCHITECTURE.md +204 -0
- package/framework/qualia-engine/templates/research-project/FEATURES.md +147 -0
- package/framework/qualia-engine/templates/research-project/PITFALLS.md +200 -0
- package/framework/qualia-engine/templates/research-project/STACK.md +120 -0
- package/framework/qualia-engine/templates/research-project/SUMMARY.md +170 -0
- package/framework/qualia-engine/templates/research.md +552 -0
- package/framework/qualia-engine/templates/roadmap.md +202 -0
- package/framework/qualia-engine/templates/state.md +176 -0
- package/framework/qualia-engine/templates/summary-complex.md +59 -0
- package/framework/qualia-engine/templates/summary-minimal.md +41 -0
- package/framework/qualia-engine/templates/summary-standard.md +48 -0
- package/framework/qualia-engine/templates/summary.md +246 -0
- package/framework/qualia-engine/templates/user-setup.md +311 -0
- package/framework/qualia-engine/templates/verification-report.md +322 -0
- package/framework/qualia-engine/workflows/add-phase.md +179 -0
- package/framework/qualia-engine/workflows/add-todo.md +157 -0
- package/framework/qualia-engine/workflows/audit-milestone.md +241 -0
- package/framework/qualia-engine/workflows/check-todos.md +176 -0
- package/framework/qualia-engine/workflows/complete-milestone.md +858 -0
- package/framework/qualia-engine/workflows/diagnose-issues.md +219 -0
- package/framework/qualia-engine/workflows/discovery-phase.md +289 -0
- package/framework/qualia-engine/workflows/discuss-phase.md +534 -0
- package/framework/qualia-engine/workflows/execute-phase.md +559 -0
- package/framework/qualia-engine/workflows/execute-plan.md +438 -0
- package/framework/qualia-engine/workflows/help.md +470 -0
- package/framework/qualia-engine/workflows/insert-phase.md +220 -0
- package/framework/qualia-engine/workflows/list-phase-assumptions.md +178 -0
- package/framework/qualia-engine/workflows/map-codebase.md +327 -0
- package/framework/qualia-engine/workflows/new-milestone.md +363 -0
- package/framework/qualia-engine/workflows/new-project.md +1037 -0
- package/framework/qualia-engine/workflows/pause-work.md +122 -0
- package/framework/qualia-engine/workflows/plan-milestone-gaps.md +256 -0
- package/framework/qualia-engine/workflows/plan-phase.md +422 -0
- package/framework/qualia-engine/workflows/progress.md +354 -0
- package/framework/qualia-engine/workflows/quick.md +252 -0
- package/framework/qualia-engine/workflows/remove-phase.md +326 -0
- package/framework/qualia-engine/workflows/research-phase.md +74 -0
- package/framework/qualia-engine/workflows/resume-project.md +306 -0
- package/framework/qualia-engine/workflows/set-profile.md +80 -0
- package/framework/qualia-engine/workflows/settings.md +145 -0
- package/framework/qualia-engine/workflows/transition.md +556 -0
- package/framework/qualia-engine/workflows/update.md +197 -0
- package/framework/qualia-engine/workflows/verify-phase.md +195 -0
- package/framework/qualia-engine/workflows/verify-work.md +625 -0
- package/framework/rules/context7.md +11 -0
- package/framework/rules/deployment.md +29 -0
- package/framework/rules/frontend.md +33 -0
- package/framework/rules/security.md +12 -0
- package/framework/rules/speed.md +20 -0
- package/framework/scripts/__pycache__/say.cpython-314.pyc +0 -0
- package/framework/scripts/apply-retention.sh +120 -0
- package/framework/scripts/bootstrap-pop-os.sh +354 -0
- package/framework/scripts/claude-voice +13 -0
- package/framework/scripts/cleanup.sh +131 -0
- package/framework/scripts/cowork-mode.sh +141 -0
- package/framework/scripts/generate-project-claude-md.sh +153 -0
- package/framework/scripts/load-test-webhook.js +172 -0
- package/framework/scripts/say.py +236 -0
- package/framework/scripts/showcase-video-recorder/ffmpeg-builder.js +167 -0
- package/framework/scripts/showcase-video-recorder/playwright-helpers.js +216 -0
- package/framework/scripts/speak.py +55 -0
- package/framework/scripts/speak.sh +18 -0
- package/framework/scripts/status.sh +138 -0
- package/framework/scripts/sync-to-framework.sh +65 -0
- package/framework/scripts/voice-hotkey.py +227 -0
- package/framework/scripts/voice-input.sh +51 -0
- package/framework/skills/animate/SKILL.md +202 -0
- package/framework/skills/bolder/SKILL.md +144 -0
- package/framework/skills/browser-qa/SKILL.md +536 -0
- package/framework/skills/clarify/SKILL.md +179 -0
- package/framework/skills/colorize/SKILL.md +170 -0
- package/framework/skills/critique/SKILL.md +126 -0
- package/framework/skills/deep-research/SKILL.md +271 -0
- package/framework/skills/delight/SKILL.md +329 -0
- package/framework/skills/deploy/SKILL.md +261 -0
- package/framework/skills/deploy-verify/SKILL.md +377 -0
- package/framework/skills/deploy-verify/scripts/canary-check.sh +206 -0
- package/framework/skills/deploy-verify/scripts/check-console-errors.js +147 -0
- package/framework/skills/deploy-verify/scripts/check-cwv.js +139 -0
- package/framework/skills/deploy-verify/scripts/project-detect.sh +84 -0
- package/framework/skills/deploy-verify/scripts/verify.sh +548 -0
- package/framework/skills/design-quieter/SKILL.md +130 -0
- package/framework/skills/distill/SKILL.md +149 -0
- package/framework/skills/docs-lookup/SKILL.md +78 -0
- package/framework/skills/fcm-notifications/SKILL.md +125 -0
- package/framework/skills/financial-ledger/SKILL.md +1039 -0
- package/framework/skills/frontend-master/NOTICE.md +4 -0
- package/framework/skills/frontend-master/SKILL.md +127 -0
- package/framework/skills/frontend-master/reference/color-and-contrast.md +132 -0
- package/framework/skills/frontend-master/reference/interaction-design.md +123 -0
- package/framework/skills/frontend-master/reference/motion-design.md +99 -0
- package/framework/skills/frontend-master/reference/responsive-design.md +114 -0
- package/framework/skills/frontend-master/reference/spatial-design.md +100 -0
- package/framework/skills/frontend-master/reference/typography.md +131 -0
- package/framework/skills/frontend-master/reference/ux-writing.md +107 -0
- package/framework/skills/harden/SKILL.md +357 -0
- package/framework/skills/i18n-rtl/SKILL.md +752 -0
- package/framework/skills/learn/SKILL.md +71 -0
- package/framework/skills/memory/SKILL.md +50 -0
- package/framework/skills/mobile-expo/SKILL.md +864 -0
- package/framework/skills/mobile-expo/references/store-checklist.md +550 -0
- package/framework/skills/nestjs-backend/README.md +73 -0
- package/framework/skills/nestjs-backend/SKILL.md +446 -0
- package/framework/skills/nestjs-backend/references/templates.md +1173 -0
- package/framework/skills/normalize/SKILL.md +79 -0
- package/framework/skills/onboard/SKILL.md +242 -0
- package/framework/skills/polish/SKILL.md +209 -0
- package/framework/skills/pr/SKILL.md +66 -0
- package/framework/skills/qualia/SKILL.md +153 -0
- package/framework/skills/qualia-add-todo/SKILL.md +68 -0
- package/framework/skills/qualia-audit-milestone/SKILL.md +92 -0
- package/framework/skills/qualia-check-todos/SKILL.md +55 -0
- package/framework/skills/qualia-complete-milestone/SKILL.md +108 -0
- package/framework/skills/qualia-debug/SKILL.md +149 -0
- package/framework/skills/qualia-design/SKILL.md +203 -0
- package/framework/skills/qualia-discuss-phase/SKILL.md +72 -0
- package/framework/skills/qualia-execute-phase/SKILL.md +86 -0
- package/framework/skills/qualia-help/SKILL.md +67 -0
- package/framework/skills/qualia-idk/SKILL.md +352 -0
- package/framework/skills/qualia-list-phase-assumptions/SKILL.md +67 -0
- package/framework/skills/qualia-new-milestone/SKILL.md +72 -0
- package/framework/skills/qualia-new-project/SKILL.md +92 -0
- package/framework/skills/qualia-optimize/SKILL.md +417 -0
- package/framework/skills/qualia-pause-work/SKILL.md +96 -0
- package/framework/skills/qualia-plan-milestone-gaps/SKILL.md +57 -0
- package/framework/skills/qualia-plan-phase/SKILL.md +101 -0
- package/framework/skills/qualia-progress/SKILL.md +53 -0
- package/framework/skills/qualia-quick/SKILL.md +89 -0
- package/framework/skills/qualia-research-phase/SKILL.md +88 -0
- package/framework/skills/qualia-resume-work/SKILL.md +62 -0
- package/framework/skills/qualia-review/SKILL.md +263 -0
- package/framework/skills/qualia-start/SKILL.md +182 -0
- package/framework/skills/qualia-verify-work/SKILL.md +105 -0
- package/framework/skills/qualia-workflow/SKILL.md +130 -0
- package/framework/skills/rag/SKILL.md +750 -0
- package/framework/skills/responsive/SKILL.md +231 -0
- package/framework/skills/retro/SKILL.md +284 -0
- package/framework/skills/sakani-conventions/SKILL.md +136 -0
- package/framework/skills/sakani-conventions/evals/evals.json +23 -0
- package/framework/skills/sakani-conventions/references/entities.md +365 -0
- package/framework/skills/sakani-conventions/references/error-codes.md +95 -0
- package/framework/skills/seo-master/SKILL.md +490 -0
- package/framework/skills/seo-master/references/checklist.md +199 -0
- package/framework/skills/seo-master/references/structured-data.md +609 -0
- package/framework/skills/ship/SKILL.md +202 -0
- package/framework/skills/stack-researcher/SKILL.md +215 -0
- package/framework/skills/status/SKILL.md +154 -0
- package/framework/skills/status/scripts/health-check.sh +562 -0
- package/framework/skills/subscription-payments/SKILL.md +250 -0
- package/framework/skills/supabase/SKILL.md +973 -0
- package/framework/skills/supabase/references/templates.md +159 -0
- package/framework/skills/team/SKILL.md +67 -0
- package/framework/skills/test-runner/SKILL.md +202 -0
- package/framework/skills/voice-agent/SKILL.md +407 -0
- package/framework/skills/zoho-workflow/SKILL.md +51 -0
- package/framework/statusline-command.sh +117 -0
- package/package.json +24 -0
- package/profiles/fawzi.json +16 -0
- package/profiles/hasan.json +16 -0
- package/profiles/moayad.json +16 -0
- package/templates/CLAUDE-owner.md +52 -0
- package/templates/CLAUDE.md.hbs +58 -0
- package/templates/env.claude.template +12 -0
- package/templates/settings.json +141 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: browser-qa
|
|
3
|
+
description: "Browser QA using Playwright. Navigates routes affected by git diff, captures console errors, broken links, and HTTP failures with screenshots. Web projects only."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
tags: [qa, testing, playwright, browser, screenshots]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Browser QA
|
|
9
|
+
|
|
10
|
+
Automated browser testing with Playwright. Detects changed routes from git diff, navigates them, and reports runtime issues with screenshot evidence.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
- `/qa` -- Run QA on affected routes (from git diff against current branch's base)
|
|
15
|
+
- `/qa --all` -- Run QA on all known routes (full scan)
|
|
16
|
+
- `/qa --url <url>` -- Run QA on a specific URL
|
|
17
|
+
|
|
18
|
+
## Step 1: Project Type Check
|
|
19
|
+
|
|
20
|
+
Before anything else, determine if this is a web project. Run:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Check for web project indicators
|
|
24
|
+
HAS_WEB=false
|
|
25
|
+
[ -f "package.json" ] && grep -qE '"next"|"react"|"vue"|"nuxt"|"astro"|"vite"|"gatsby"' package.json && HAS_WEB=true
|
|
26
|
+
[ -f "index.html" ] && HAS_WEB=true
|
|
27
|
+
[ -d "src/app" ] || [ -d "pages" ] || [ -d "src/pages" ] && HAS_WEB=true
|
|
28
|
+
|
|
29
|
+
echo "HAS_WEB=$HAS_WEB"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**If HAS_WEB=false:** Stop immediately and output:
|
|
33
|
+
|
|
34
|
+
> Browser QA skipped: This project is not a web application. /qa is designed for projects with a browser-rendered frontend (Next.js, React, Vue, etc.). Voice agents, Python agents, and CLI tools do not have browser routes to test.
|
|
35
|
+
|
|
36
|
+
**If HAS_WEB=true:** Continue to Step 2.
|
|
37
|
+
|
|
38
|
+
### Known Non-Web Projects (skip without checking)
|
|
39
|
+
|
|
40
|
+
These projects are known to not be web apps:
|
|
41
|
+
- **sigatalachana** -- Python agent (invoice OCR + POS)
|
|
42
|
+
- **armenius** -- Cloudflare Workers webhook (no frontend routes)
|
|
43
|
+
- Any project in `~/Projects/aiagents/` with no `package.json` containing a web framework
|
|
44
|
+
- Any project with only `.py` files and no web framework (Flask/Django with templates are web)
|
|
45
|
+
|
|
46
|
+
If the current directory name matches a known non-web project, skip immediately.
|
|
47
|
+
|
|
48
|
+
## Step 2: Detect Affected Routes
|
|
49
|
+
|
|
50
|
+
Determine which routes to test based on git changes.
|
|
51
|
+
|
|
52
|
+
### Automatic Route Detection
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Get changed files relative to the merge base
|
|
56
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
57
|
+
BASE=$(git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/null || echo "HEAD~5")
|
|
58
|
+
CHANGED_FILES=$(git diff --name-only "$BASE"...HEAD 2>/dev/null || git diff --name-only HEAD~5)
|
|
59
|
+
|
|
60
|
+
echo "Changed files:"
|
|
61
|
+
echo "$CHANGED_FILES"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Route Mapping Rules
|
|
65
|
+
|
|
66
|
+
Map changed files to browser routes:
|
|
67
|
+
|
|
68
|
+
| File Pattern | Route |
|
|
69
|
+
|-------------|-------|
|
|
70
|
+
| `src/app/<path>/page.tsx` | `/<path>` |
|
|
71
|
+
| `src/app/page.tsx` | `/` |
|
|
72
|
+
| `pages/<path>.tsx` | `/<path>` |
|
|
73
|
+
| `pages/index.tsx` | `/` |
|
|
74
|
+
| `src/app/api/**` | Skip (API routes, not pages) |
|
|
75
|
+
| `src/components/**` | Find which pages import this component |
|
|
76
|
+
| `src/lib/**`, `src/utils/**` | Test `/` (shared code, test homepage) |
|
|
77
|
+
| `public/**` | Test `/` (static assets) |
|
|
78
|
+
| `styles/**`, `*.css` | Test all layout-level pages (`/`, plus any page using the stylesheet) |
|
|
79
|
+
| `src/app/layout.tsx` | Test `/` and one subpage |
|
|
80
|
+
|
|
81
|
+
### Component-to-Page Resolution
|
|
82
|
+
|
|
83
|
+
When a component file changes, find which pages use it:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# For a changed component, find importing pages
|
|
87
|
+
COMPONENT_NAME=$(basename "$CHANGED_FILE" .tsx)
|
|
88
|
+
grep -rl "$COMPONENT_NAME" src/app/*/page.tsx pages/*.tsx 2>/dev/null
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Fallback
|
|
92
|
+
|
|
93
|
+
If no routes can be derived (e.g., only config files changed), test the homepage (`/`).
|
|
94
|
+
|
|
95
|
+
If `--all` flag is used, discover all routes:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Next.js App Router
|
|
99
|
+
find src/app -name "page.tsx" -o -name "page.jsx" | sed 's|src/app||;s|/page\.[tj]sx||;s|^$|/|'
|
|
100
|
+
|
|
101
|
+
# Next.js Pages Router
|
|
102
|
+
find pages -name "*.tsx" -o -name "*.jsx" | grep -v "_app\|_document\|api/" | sed 's|pages||;s|\.[tj]sx||;s|/index$|/|'
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Step 3: Run Playwright QA
|
|
106
|
+
|
|
107
|
+
### Prerequisites
|
|
108
|
+
|
|
109
|
+
Verify Playwright is available:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx playwright --version
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If not installed or browsers not downloaded:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm init -y 2>/dev/null # Only if no package.json
|
|
119
|
+
npx playwright install chromium
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Dev Server Detection
|
|
123
|
+
|
|
124
|
+
Check if a dev server is already running:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Check common ports
|
|
128
|
+
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null
|
|
129
|
+
curl -s -o /dev/null -w "%{http_code}" http://localhost:5173 2>/dev/null
|
|
130
|
+
curl -s -o /dev/null -w "%{http_code}" http://localhost:4321 2>/dev/null
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If no server is running, start one:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Detect and start the dev server
|
|
137
|
+
if grep -q '"dev"' package.json; then
|
|
138
|
+
npm run dev &
|
|
139
|
+
DEV_PID=$!
|
|
140
|
+
sleep 5 # Wait for server startup
|
|
141
|
+
fi
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Record the base URL (e.g., `http://localhost:3000`).
|
|
145
|
+
|
|
146
|
+
### QA Script
|
|
147
|
+
|
|
148
|
+
For each route, run this Playwright script:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npx playwright test --config=- <<'PLAYWRIGHT_CONFIG'
|
|
152
|
+
import { test, expect } from '@playwright/test';
|
|
153
|
+
|
|
154
|
+
const BASE_URL = process.env.QA_BASE_URL || 'http://localhost:3000';
|
|
155
|
+
const ROUTES = process.env.QA_ROUTES?.split(',') || ['/'];
|
|
156
|
+
const SCREENSHOT_DIR = '.qa-screenshots';
|
|
157
|
+
|
|
158
|
+
test.describe('Browser QA', () => {
|
|
159
|
+
for (const route of ROUTES) {
|
|
160
|
+
test(`QA: ${route}`, async ({ page }) => {
|
|
161
|
+
const errors: string[] = [];
|
|
162
|
+
const failedRequests: { url: string; status: number }[] = [];
|
|
163
|
+
|
|
164
|
+
// Capture console errors
|
|
165
|
+
page.on('console', msg => {
|
|
166
|
+
if (msg.type() === 'error') {
|
|
167
|
+
errors.push(`Console error: ${msg.text()}`);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Capture failed HTTP requests
|
|
172
|
+
page.on('response', response => {
|
|
173
|
+
if (response.status() >= 400) {
|
|
174
|
+
failedRequests.push({ url: response.url(), status: response.status() });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Navigate
|
|
179
|
+
await page.goto(`${BASE_URL}${route}`, { waitUntil: 'networkidle', timeout: 30000 });
|
|
180
|
+
|
|
181
|
+
// Screenshot
|
|
182
|
+
await page.screenshot({ path: `${SCREENSHOT_DIR}${route === '/' ? '/index' : route}.png`, fullPage: true });
|
|
183
|
+
|
|
184
|
+
// Check for broken links
|
|
185
|
+
const links = await page.$$eval('a[href]', anchors =>
|
|
186
|
+
anchors.map(a => a.getAttribute('href')).filter(href => href && !href.startsWith('#') && !href.startsWith('mailto:') && !href.startsWith('tel:'))
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
for (const link of links.slice(0, 20)) { // Cap at 20 links per page
|
|
190
|
+
if (link.startsWith('/') || link.startsWith(BASE_URL)) {
|
|
191
|
+
const fullUrl = link.startsWith('/') ? `${BASE_URL}${link}` : link;
|
|
192
|
+
const resp = await page.request.get(fullUrl);
|
|
193
|
+
if (resp.status() >= 400) {
|
|
194
|
+
failedRequests.push({ url: fullUrl, status: resp.status() });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Report findings (test passes but logs findings)
|
|
200
|
+
if (errors.length > 0 || failedRequests.length > 0) {
|
|
201
|
+
console.log(JSON.stringify({ route, errors, failedRequests }));
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
PLAYWRIGHT_CONFIG
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Alternative approach (simpler, no config file needed):**
|
|
210
|
+
|
|
211
|
+
Instead of the Playwright test runner, use a Node.js script with Playwright library directly:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
QA_BASE_URL="http://localhost:3000" QA_ROUTES="/" node -e "
|
|
215
|
+
const { chromium } = require('playwright');
|
|
216
|
+
(async () => {
|
|
217
|
+
const browser = await chromium.launch();
|
|
218
|
+
const baseUrl = process.env.QA_BASE_URL;
|
|
219
|
+
const routes = process.env.QA_ROUTES.split(',');
|
|
220
|
+
const findings = [];
|
|
221
|
+
|
|
222
|
+
for (const route of routes) {
|
|
223
|
+
const context = await browser.newContext();
|
|
224
|
+
const page = await context.newPage();
|
|
225
|
+
const errors = [];
|
|
226
|
+
const failedRequests = [];
|
|
227
|
+
|
|
228
|
+
page.on('console', msg => { if (msg.type() === 'error') errors.push(msg.text()); });
|
|
229
|
+
page.on('response', resp => { if (resp.status() >= 400) failedRequests.push({ url: resp.url(), status: resp.status() }); });
|
|
230
|
+
|
|
231
|
+
await page.goto(baseUrl + route, { waitUntil: 'networkidle', timeout: 30000 });
|
|
232
|
+
await page.screenshot({ path: '.qa-screenshots' + (route === '/' ? '/index' : route) + '.png', fullPage: true });
|
|
233
|
+
|
|
234
|
+
// Check internal links
|
|
235
|
+
const links = await page.$$eval('a[href]', as => as.map(a => a.href).filter(h => h.startsWith(baseUrl) || h.startsWith('/')));
|
|
236
|
+
for (const link of links.slice(0, 20)) {
|
|
237
|
+
const url = link.startsWith('/') ? baseUrl + link : link;
|
|
238
|
+
try { const r = await page.request.get(url); if (r.status() >= 400) failedRequests.push({ url, status: r.status() }); } catch(e) { failedRequests.push({ url, status: 0 }); }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (errors.length || failedRequests.length) findings.push({ route, errors, failedRequests });
|
|
242
|
+
await context.close();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.log(JSON.stringify(findings, null, 2));
|
|
246
|
+
await browser.close();
|
|
247
|
+
})();
|
|
248
|
+
"
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Screenshot Capture
|
|
252
|
+
|
|
253
|
+
Every page navigated gets a full-page screenshot saved to `.qa-screenshots/`:
|
|
254
|
+
|
|
255
|
+
- Route `/` -> `.qa-screenshots/index.png`
|
|
256
|
+
- Route `/about` -> `.qa-screenshots/about.png`
|
|
257
|
+
- Route `/dashboard/settings` -> `.qa-screenshots/dashboard/settings.png`
|
|
258
|
+
|
|
259
|
+
Create the directory before running:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
mkdir -p .qa-screenshots
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
When a finding is reported, reference the screenshot path in the report:
|
|
266
|
+
|
|
267
|
+
> Finding: Console error "TypeError: Cannot read property 'map' of undefined" on /dashboard
|
|
268
|
+
> Screenshot: `.qa-screenshots/dashboard.png`
|
|
269
|
+
|
|
270
|
+
## Step 4: Generate QA Report
|
|
271
|
+
|
|
272
|
+
After all routes are tested, produce a structured report.
|
|
273
|
+
|
|
274
|
+
### Report Format
|
|
275
|
+
|
|
276
|
+
```markdown
|
|
277
|
+
# QA Report -- {project-name}
|
|
278
|
+
**Date:** {ISO timestamp}
|
|
279
|
+
**Routes tested:** {N}
|
|
280
|
+
**Findings:** {N} ({errors} console errors, {broken_links} broken links, {http_failures} HTTP failures)
|
|
281
|
+
**Screenshots:** .qa-screenshots/
|
|
282
|
+
|
|
283
|
+
## Findings
|
|
284
|
+
|
|
285
|
+
### Finding 1: {type} on {route}
|
|
286
|
+
- **Type:** console-error | broken-link | http-failure
|
|
287
|
+
- **Route:** {route}
|
|
288
|
+
- **Detail:** {error message or URL + status code}
|
|
289
|
+
- **Screenshot:** .qa-screenshots/{route}.png
|
|
290
|
+
- **False positive:** no (set to yes if confirmed false positive)
|
|
291
|
+
- **Severity:** critical | warning | info
|
|
292
|
+
|
|
293
|
+
### Finding 2: ...
|
|
294
|
+
|
|
295
|
+
## Summary
|
|
296
|
+
|
|
297
|
+
| Route | Status | Console Errors | Broken Links | HTTP Failures |
|
|
298
|
+
|-------|--------|----------------|--------------|---------------|
|
|
299
|
+
| / | PASS/FAIL | 0 | 0 | 0 |
|
|
300
|
+
|
|
301
|
+
## Routes Tested
|
|
302
|
+
{list of routes with pass/fail status}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Severity Classification
|
|
306
|
+
|
|
307
|
+
- **critical:** Page fails to load, unhandled exceptions, 500 errors
|
|
308
|
+
- **warning:** 404 broken links, non-critical console errors, slow responses (>3s)
|
|
309
|
+
- **info:** Console warnings (not errors), deprecation notices
|
|
310
|
+
|
|
311
|
+
### Clean Run
|
|
312
|
+
|
|
313
|
+
If zero findings:
|
|
314
|
+
|
|
315
|
+
> QA PASSED: {N} routes tested, zero findings. Screenshots saved to .qa-screenshots/
|
|
316
|
+
|
|
317
|
+
## Step 5: Cleanup
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
# Kill dev server if we started one
|
|
321
|
+
[ -n "$DEV_PID" ] && kill $DEV_PID 2>/dev/null
|
|
322
|
+
|
|
323
|
+
# Keep screenshots for evidence (don't delete)
|
|
324
|
+
echo "Screenshots saved to .qa-screenshots/"
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Add `.qa-screenshots/` to `.gitignore` if not already present:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
grep -q ".qa-screenshots" .gitignore 2>/dev/null || echo ".qa-screenshots/" >> .gitignore
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Step 6: Bug Fix Workflow (BQA-03)
|
|
334
|
+
|
|
335
|
+
When QA findings reveal real bugs, fix each one with an atomic commit and a regression test.
|
|
336
|
+
|
|
337
|
+
### Process Per Finding
|
|
338
|
+
|
|
339
|
+
For each confirmed finding (not a false positive):
|
|
340
|
+
|
|
341
|
+
#### 1. Create a regression test FIRST (Red)
|
|
342
|
+
|
|
343
|
+
Write a test that reproduces the exact issue found:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
# Example: Console error on /dashboard
|
|
347
|
+
cat > __tests__/qa-regression/dashboard-console-error.test.ts << 'EOF'
|
|
348
|
+
import { test, expect } from '@playwright/test';
|
|
349
|
+
|
|
350
|
+
test('dashboard should not have console errors', async ({ page }) => {
|
|
351
|
+
const errors: string[] = [];
|
|
352
|
+
page.on('console', msg => {
|
|
353
|
+
if (msg.type() === 'error') errors.push(msg.text());
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
await page.goto('http://localhost:3000/dashboard');
|
|
357
|
+
await page.waitForLoadState('networkidle');
|
|
358
|
+
|
|
359
|
+
expect(errors).toHaveLength(0);
|
|
360
|
+
});
|
|
361
|
+
EOF
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
# Example: Broken link on /about
|
|
366
|
+
cat > __tests__/qa-regression/about-broken-links.test.ts << 'EOF'
|
|
367
|
+
import { test, expect } from '@playwright/test';
|
|
368
|
+
|
|
369
|
+
test('about page should have no broken internal links', async ({ page }) => {
|
|
370
|
+
await page.goto('http://localhost:3000/about');
|
|
371
|
+
const links = await page.$$eval('a[href^="/"]', as => as.map(a => a.getAttribute('href')));
|
|
372
|
+
|
|
373
|
+
for (const link of links) {
|
|
374
|
+
const response = await page.request.get(`http://localhost:3000${link}`);
|
|
375
|
+
expect(response.status()).toBeLessThan(400);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
EOF
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### 2. Fix the bug
|
|
382
|
+
|
|
383
|
+
Apply the minimal fix. The fix should be scoped to the exact issue -- no drive-by refactoring.
|
|
384
|
+
|
|
385
|
+
#### 3. Verify the test passes (Green)
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
npx playwright test __tests__/qa-regression/ --reporter=list
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### 4. Atomic commit
|
|
392
|
+
|
|
393
|
+
Each bug fix gets its own commit:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
git add <fixed-files> __tests__/qa-regression/<test-file>
|
|
397
|
+
git commit -m "fix(qa): resolve {finding-type} on {route}
|
|
398
|
+
|
|
399
|
+
QA finding: {brief description}
|
|
400
|
+
Regression test: __tests__/qa-regression/{test-file}
|
|
401
|
+
"
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Naming Convention for Regression Tests
|
|
405
|
+
|
|
406
|
+
```
|
|
407
|
+
__tests__/qa-regression/{route}-{finding-type}.test.ts
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Examples:
|
|
411
|
+
- `__tests__/qa-regression/dashboard-console-error.test.ts`
|
|
412
|
+
- `__tests__/qa-regression/about-broken-links.test.ts`
|
|
413
|
+
- `__tests__/qa-regression/index-http-failure.test.ts`
|
|
414
|
+
|
|
415
|
+
### When NOT to Create Regression Tests
|
|
416
|
+
|
|
417
|
+
- **False positives** -- skip entirely
|
|
418
|
+
- **Third-party script errors** (e.g., analytics, chat widgets) -- suppress instead of fix
|
|
419
|
+
- **Environment-only issues** (e.g., dev-only CORS) -- note in report, don't test
|
|
420
|
+
|
|
421
|
+
## Step 7: False Positive Tracking (BQA-06)
|
|
422
|
+
|
|
423
|
+
Track the false positive rate across QA runs. If the cumulative rate exceeds 20%, surface a tuning warning.
|
|
424
|
+
|
|
425
|
+
### Recording Results
|
|
426
|
+
|
|
427
|
+
After each QA run, append results to `.qa-history.json` in the project root:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
# Read existing history or create empty
|
|
431
|
+
HISTORY_FILE=".qa-history.json"
|
|
432
|
+
[ -f "$HISTORY_FILE" ] || echo '{"runs":[]}' > "$HISTORY_FILE"
|
|
433
|
+
|
|
434
|
+
# Append this run's results
|
|
435
|
+
node -e "
|
|
436
|
+
const fs = require('fs');
|
|
437
|
+
const history = JSON.parse(fs.readFileSync('$HISTORY_FILE', 'utf8'));
|
|
438
|
+
history.runs.push({
|
|
439
|
+
date: new Date().toISOString(),
|
|
440
|
+
routes_tested: ${ROUTES_TESTED},
|
|
441
|
+
total_findings: ${TOTAL_FINDINGS},
|
|
442
|
+
false_positives: ${FALSE_POSITIVES},
|
|
443
|
+
true_positives: ${TRUE_POSITIVES}
|
|
444
|
+
});
|
|
445
|
+
fs.writeFileSync('$HISTORY_FILE', JSON.stringify(history, null, 2));
|
|
446
|
+
"
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Calculating the Rate
|
|
450
|
+
|
|
451
|
+
After recording, calculate the cumulative false positive rate:
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
node -e "
|
|
455
|
+
const fs = require('fs');
|
|
456
|
+
const history = JSON.parse(fs.readFileSync('.qa-history.json', 'utf8'));
|
|
457
|
+
const totals = history.runs.reduce((acc, run) => ({
|
|
458
|
+
findings: acc.findings + run.total_findings,
|
|
459
|
+
fp: acc.fp + run.false_positives
|
|
460
|
+
}), { findings: 0, fp: 0 });
|
|
461
|
+
|
|
462
|
+
const rate = totals.findings > 0 ? (totals.fp / totals.findings * 100).toFixed(1) : 0;
|
|
463
|
+
console.log('False positive rate: ' + rate + '% (' + totals.fp + '/' + totals.findings + ')');
|
|
464
|
+
|
|
465
|
+
if (parseFloat(rate) > 20) {
|
|
466
|
+
console.log('');
|
|
467
|
+
console.log('WARNING: False positive rate exceeds 20%. QA detection rules need tuning.');
|
|
468
|
+
console.log('Review common false positives:');
|
|
469
|
+
console.log(' - Third-party script errors (add to suppression list)');
|
|
470
|
+
console.log(' - Dev-only warnings (filter by environment)');
|
|
471
|
+
console.log(' - Known flaky routes (add to .qa-suppress.json)');
|
|
472
|
+
}
|
|
473
|
+
"
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Tuning Warning Output
|
|
477
|
+
|
|
478
|
+
When rate > 20%, append this to the QA report:
|
|
479
|
+
|
|
480
|
+
```markdown
|
|
481
|
+
## Tuning Warning
|
|
482
|
+
|
|
483
|
+
False positive rate is {rate}% ({fp}/{total} findings across {N} runs).
|
|
484
|
+
This exceeds the 20% threshold. Consider:
|
|
485
|
+
|
|
486
|
+
1. **Add suppressions** to `.qa-suppress.json` for known non-issues
|
|
487
|
+
2. **Filter third-party errors** -- console errors from external scripts (analytics, chat widgets) are not your bugs
|
|
488
|
+
3. **Exclude flaky routes** -- routes requiring auth or dynamic data may produce false 4xx errors
|
|
489
|
+
4. **Review detection rules** -- some console warnings are not actionable errors
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Suppression File
|
|
493
|
+
|
|
494
|
+
Create `.qa-suppress.json` to exclude known false positives from future runs:
|
|
495
|
+
|
|
496
|
+
```json
|
|
497
|
+
{
|
|
498
|
+
"suppress_console_patterns": [
|
|
499
|
+
".*third-party-analytics.*",
|
|
500
|
+
".*Failed to load resource.*favicon\\.ico.*",
|
|
501
|
+
".*DevTools.*"
|
|
502
|
+
],
|
|
503
|
+
"suppress_urls": [
|
|
504
|
+
"/api/auth/callback"
|
|
505
|
+
],
|
|
506
|
+
"suppress_routes": []
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
When running QA (Step 3), check suppressions before recording a finding:
|
|
511
|
+
|
|
512
|
+
```javascript
|
|
513
|
+
// In the Playwright script, before recording a console error:
|
|
514
|
+
const suppressions = JSON.parse(fs.readFileSync('.qa-suppress.json', 'utf8'));
|
|
515
|
+
const isSuppressed = suppressions.suppress_console_patterns.some(p => new RegExp(p).test(errorText));
|
|
516
|
+
if (!isSuppressed) {
|
|
517
|
+
// Record finding
|
|
518
|
+
}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Marking False Positives
|
|
522
|
+
|
|
523
|
+
In the QA report, each finding has a `False positive: no` field. After review:
|
|
524
|
+
|
|
525
|
+
1. Change to `False positive: yes` for confirmed false positives
|
|
526
|
+
2. Add the pattern to `.qa-suppress.json` to prevent recurrence
|
|
527
|
+
3. Update `.qa-history.json` with corrected counts
|
|
528
|
+
|
|
529
|
+
### History File in .gitignore
|
|
530
|
+
|
|
531
|
+
```bash
|
|
532
|
+
grep -q ".qa-history.json" .gitignore 2>/dev/null || echo ".qa-history.json" >> .gitignore
|
|
533
|
+
grep -q ".qa-suppress.json" .gitignore 2>/dev/null || echo "# Keep .qa-suppress.json IN git -- it's project config, not output" >> /dev/null
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Note: `.qa-suppress.json` SHOULD be committed (it's configuration). `.qa-history.json` should NOT be committed (it's local state).
|