@ai-qa/workflow 2.0.16 → 2.0.18
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/.opencode/qa-workflow-skill.md +232 -0
- package/PROJECT_GUIDE.md +114 -74
- package/README.md +31 -18
- package/install.js +47 -7
- package/opencode.json +6 -0
- package/package.json +9 -7
- package/playwright.config.ts +10 -0
- package/prompting_template.md +33 -264
- package/qa-dashboard/app.js +6 -2
- package/qa-dashboard/package.json +4 -1
- package/qa-dashboard/routes/index.js +26 -4
- package/qa-dashboard/routes/projects.js +25 -1
- package/qa-dashboard/routes/runs.js +38 -0
- package/qa-dashboard/routes/stories.js +18 -1
- package/qa-dashboard/routes/terminal.js +51 -0
- package/qa-dashboard/routes/test-data.js +46 -2
- package/qa-dashboard/services/cli-bridge.js +4 -0
- package/qa-dashboard/views/index.ejs +250 -238
- package/qa-dashboard/views/layouts/main.ejs +3 -2
- package/qa-dashboard/views/project.ejs +78 -51
- package/qa-dashboard/views/run.ejs +63 -3
- package/qa-dashboard/views/stories.ejs +36 -12
- package/qa-dashboard/views/story.ejs +125 -86
- package/qa-dashboard/views/terminal.ejs +101 -0
- package/qa-dashboard/views/test-data.ejs +59 -22
- package/router.md +27 -17
- package/test-results/run-2026-05-18T14-51-36/final-test-report.md +10 -2
- package/tsconfig.json +5 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<% layout = 'layouts/main' %>
|
|
2
|
+
<% title = 'Terminal' %>
|
|
3
|
+
|
|
4
|
+
<div class="page-header">
|
|
5
|
+
<h1>🖥️ Terminal</h1>
|
|
6
|
+
<div class="header-actions">
|
|
7
|
+
<% if (typeof projects !== 'undefined' && projects.length > 1) { %>
|
|
8
|
+
<select id="project-select" class="form-input" style="width:auto;" onchange="window.location='/terminal?project='+this.value">
|
|
9
|
+
<% projects.forEach(p => { %>
|
|
10
|
+
<option value="<%= p.id %>" <%= project && project.id === p.id ? 'selected' : '' %>><%= p.name %></option>
|
|
11
|
+
<% }) %>
|
|
12
|
+
</select>
|
|
13
|
+
<% } %>
|
|
14
|
+
<button class="btn btn-sm btn-danger" onclick="clearTerminal()">🗑️ Clear</button>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="card">
|
|
19
|
+
<h3>⚡ Quick Commands</h3>
|
|
20
|
+
<div style="display:flex;gap:0.5rem;flex-wrap:wrap;margin-bottom:1rem;">
|
|
21
|
+
<button class="btn btn-sm" onclick="runCommand('npx playwright test')">▶ Run All Tests</button>
|
|
22
|
+
<button class="btn btn-sm" onclick="runCommand('npm run qa:report')">📝 Generate Report</button>
|
|
23
|
+
<button class="btn btn-sm" onclick="runCommand('npx allure generate allure-results --clean -o allure-report')">📈 Generate Allure</button>
|
|
24
|
+
<button class="btn btn-sm" onclick="runCommand('npm run qa:status')">📊 Status</button>
|
|
25
|
+
<button class="btn btn-sm" onclick="runCommand('npx playwright show-report')">📋 Open HTML Report</button>
|
|
26
|
+
</div>
|
|
27
|
+
<form id="terminal-form" style="display:flex;gap:0.5rem;">
|
|
28
|
+
<input type="text" id="cmd-input" class="form-input" placeholder="Enter a command (e.g., npx playwright test)" style="flex:1;font-family:var(--font-mono);font-size:0.85rem;" autofocus>
|
|
29
|
+
<button type="submit" class="btn btn-primary" style="white-space:nowrap;">▶ Run</button>
|
|
30
|
+
</form>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="card" style="padding:0;overflow:hidden;">
|
|
34
|
+
<div style="background:var(--surface-dark);padding:0.75rem 1.25rem;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--border-strong);">
|
|
35
|
+
<span style="color:#e5e7eb;font-family:var(--font-mono);font-size:0.8rem;font-weight:600;">Console Output</span>
|
|
36
|
+
<span id="terminal-status" style="color:var(--text-disabled);font-family:var(--font-mono);font-size:0.75rem;">Ready</span>
|
|
37
|
+
</div>
|
|
38
|
+
<pre id="terminal-output" class="code-block" style="border:none;border-radius:0;max-height:500px;min-height:200px;margin:0;font-size:0.8rem;"><%= project ? `Ready — project: ${project.name}\nType a command or click a quick action above.` : 'No project configured.' %></pre>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<script>
|
|
42
|
+
document.getElementById('terminal-form').addEventListener('submit', async (e) => {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
const cmd = document.getElementById('cmd-input').value.trim();
|
|
45
|
+
if (!cmd) return;
|
|
46
|
+
runCommand(cmd);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
async function runCommand(command) {
|
|
50
|
+
const output = document.getElementById('terminal-output');
|
|
51
|
+
const status = document.getElementById('terminal-status');
|
|
52
|
+
const input = document.getElementById('cmd-input');
|
|
53
|
+
|
|
54
|
+
output.textContent += `\n\n$ ${command}\n`;
|
|
55
|
+
status.textContent = '⏳ Running...';
|
|
56
|
+
output.scrollTop = output.scrollHeight;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const res = await fetch('/terminal/run?project=<%= project ? project.id : '' %>', {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': 'application/json' },
|
|
62
|
+
body: JSON.stringify({ command }),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (!res.body) {
|
|
66
|
+
output.textContent += 'Error: No response stream\n';
|
|
67
|
+
status.textContent = '❌ Error';
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const reader = res.body.getReader();
|
|
72
|
+
const decoder = new TextDecoder('utf-8');
|
|
73
|
+
|
|
74
|
+
while (true) {
|
|
75
|
+
const { done, value } = await reader.read();
|
|
76
|
+
if (done) break;
|
|
77
|
+
output.textContent += decoder.decode(value, { stream: true });
|
|
78
|
+
output.scrollTop = output.scrollHeight;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const lastLine = output.textContent.split('\n').pop() || '';
|
|
82
|
+
if (lastLine.includes('SUCCESS')) {
|
|
83
|
+
status.textContent = '✅ Completed';
|
|
84
|
+
} else if (lastLine.includes('FAILED')) {
|
|
85
|
+
status.textContent = '❌ Failed';
|
|
86
|
+
} else {
|
|
87
|
+
status.textContent = '✅ Done';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (input) input.value = '';
|
|
91
|
+
} catch (err) {
|
|
92
|
+
output.textContent += `\nConnection error: ${err.message}\n`;
|
|
93
|
+
status.textContent = '❌ Error';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function clearTerminal() {
|
|
98
|
+
document.getElementById('terminal-output').textContent = 'Terminal cleared.';
|
|
99
|
+
document.getElementById('terminal-status').textContent = 'Ready';
|
|
100
|
+
}
|
|
101
|
+
</script>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<% title = project ? 'Test Data - ' + project.name : 'Test Data Management' %>
|
|
3
3
|
|
|
4
4
|
<div class="page-header">
|
|
5
|
-
<h1>Test Data
|
|
5
|
+
<h1>Test Data & Artifacts</h1>
|
|
6
6
|
<div class="header-actions">
|
|
7
7
|
<% if (typeof projects !== 'undefined' && projects.length > 1) { %>
|
|
8
8
|
<select id="project-select" class="form-input" style="width:auto;" onchange="window.location='/data?project='+this.value">
|
|
@@ -17,14 +17,53 @@
|
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
<% if (project) { %>
|
|
21
|
+
<!-- ─── Test Specs ─── -->
|
|
22
|
+
<% if (testSpecs && testSpecs.length > 0) { %>
|
|
23
|
+
<div class="card card-lavender">
|
|
24
|
+
<h3>🧪 Test Spec Files <span class="badge badge-project" style="margin-left:0.5rem;"><%= testSpecs.length %></span></h3>
|
|
25
|
+
<% testSpecs.forEach(spec => { %>
|
|
26
|
+
<details style="margin-bottom:0.75rem;border:1px solid var(--border);border-radius:var(--radius-md);padding:0.75rem;">
|
|
27
|
+
<summary style="cursor:pointer;font-weight:600;font-family:var(--font-mono);font-size:0.9rem;">
|
|
28
|
+
<%= spec.name %>
|
|
29
|
+
<a href="/data/spec/<%= spec.name %>?project=<%= project.id %>" class="btn btn-sm" style="margin-left:0.5rem;" download>📥 Download</a>
|
|
30
|
+
<a href="/stories?project=<%= project.id %>" class="btn btn-sm">▶ Execute</a>
|
|
31
|
+
</summary>
|
|
32
|
+
<pre class="code-block" style="margin-top:0.75rem;max-height:400px;font-size:0.75rem;"><%= spec.content %></pre>
|
|
33
|
+
</details>
|
|
34
|
+
<% }) %>
|
|
35
|
+
</div>
|
|
36
|
+
<% } else { %>
|
|
37
|
+
<div class="card">
|
|
38
|
+
<h3>🧪 Test Spec Files</h3>
|
|
39
|
+
<p style="color:var(--text-muted);">Aucun fichier de test trouvé. Générez des tests depuis la page <a href="/stories?project=<%= project.id %>">Stories</a>.</p>
|
|
40
|
+
</div>
|
|
41
|
+
<% } %>
|
|
21
42
|
|
|
43
|
+
<!-- ─── Test Plans ─── -->
|
|
44
|
+
<% if (testPlans && testPlans.length > 0) { %>
|
|
45
|
+
<div class="card card-peach">
|
|
46
|
+
<h3>📋 Test Plan Files <span class="badge badge-project" style="margin-left:0.5rem;"><%= testPlans.length %></span></h3>
|
|
47
|
+
<% testPlans.forEach(plan => { %>
|
|
48
|
+
<details style="margin-bottom:0.75rem;border:1px solid var(--border);border-radius:var(--radius-md);padding:0.75rem;">
|
|
49
|
+
<summary style="cursor:pointer;font-weight:600;font-family:var(--font-mono);font-size:0.9rem;">
|
|
50
|
+
<%= plan.name %>
|
|
51
|
+
<a href="/data/plan/<%= plan.name %>?project=<%= project.id %>" class="btn btn-sm" style="margin-left:0.5rem;" download>📥 Download</a>
|
|
52
|
+
</summary>
|
|
53
|
+
<div class="markdown-content" style="margin-top:0.75rem;font-size:0.85rem;" data-md="<%= encodeURIComponent(plan.content) %>"></div>
|
|
54
|
+
</details>
|
|
55
|
+
<% }) %>
|
|
56
|
+
</div>
|
|
57
|
+
<% } %>
|
|
58
|
+
<% } %>
|
|
59
|
+
|
|
60
|
+
<!-- ─── Generate Test Data ─── -->
|
|
22
61
|
<div class="row" style="margin-bottom:2rem;">
|
|
23
62
|
<div class="col col-40">
|
|
24
|
-
<div class="card card-
|
|
63
|
+
<div class="card card-cream">
|
|
25
64
|
<h3>⚡ Generate Test Data</h3>
|
|
26
65
|
<p style="color: var(--text-muted); font-size: 0.85rem; margin-bottom: 1.25rem;">
|
|
27
|
-
Auto-generate
|
|
66
|
+
Auto-generate synthetic mock datasets for your QA pipeline.
|
|
28
67
|
</p>
|
|
29
68
|
<form id="data-form" class="form">
|
|
30
69
|
<div class="form-group">
|
|
@@ -68,15 +107,11 @@
|
|
|
68
107
|
</div>
|
|
69
108
|
|
|
70
109
|
<div class="card">
|
|
71
|
-
<h3>ℹ️ About Test Data
|
|
110
|
+
<h3>ℹ️ About Test Data</h3>
|
|
72
111
|
<p style="color: var(--text-muted); font-size: 0.88rem; line-height:1.5;">
|
|
73
|
-
|
|
112
|
+
Test spec files (<code>.spec.ts</code>) and plans (<code>.md</code>) are read directly from your project.
|
|
113
|
+
Generated datasets are stored in <code>qa-dashboard/data/</code> as JSON files.
|
|
74
114
|
</p>
|
|
75
|
-
<ul style="color: var(--text-muted); font-size: 0.88rem; padding-left: 1.5rem; margin-top: 0.5rem; line-height:1.6;">
|
|
76
|
-
<li>Read dynamically inside Playwright test scenarios using <code>fs.readFileSync()</code></li>
|
|
77
|
-
<li>Injected directly into a localized sandbox database before test suite runs</li>
|
|
78
|
-
<li>Exported or synced for cross-team quality assurance consistency</li>
|
|
79
|
-
</ul>
|
|
80
115
|
</div>
|
|
81
116
|
|
|
82
117
|
<script>
|
|
@@ -85,18 +120,11 @@ document.getElementById('data-form').addEventListener('submit', async (e) => {
|
|
|
85
120
|
const type = document.getElementById('data-type').value;
|
|
86
121
|
const count = document.getElementById('data-count').value;
|
|
87
122
|
const output = document.getElementById('data-output');
|
|
88
|
-
|
|
89
123
|
output.style.display = 'block';
|
|
90
|
-
output.textContent = 'Generating
|
|
124
|
+
output.textContent = 'Generating...';
|
|
91
125
|
output.className = 'output-box output-running';
|
|
92
|
-
|
|
93
|
-
const res = await fetch('/data/generate', {
|
|
94
|
-
method: 'POST',
|
|
95
|
-
headers: { 'Content-Type': 'application/json' },
|
|
96
|
-
body: JSON.stringify({ type, count })
|
|
97
|
-
});
|
|
126
|
+
const res = await fetch('/data/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type, count }) });
|
|
98
127
|
const data = await res.json();
|
|
99
|
-
|
|
100
128
|
if (data.success) {
|
|
101
129
|
output.textContent = `Generated ${data.count} records → ${data.file}`;
|
|
102
130
|
output.className = 'output-box output-success';
|
|
@@ -108,10 +136,19 @@ document.getElementById('data-form').addEventListener('submit', async (e) => {
|
|
|
108
136
|
});
|
|
109
137
|
|
|
110
138
|
async function deleteFile(name) {
|
|
111
|
-
if (!confirm(`
|
|
139
|
+
if (!confirm(`Delete ${name}?`)) return;
|
|
112
140
|
const res = await fetch('/data/' + encodeURIComponent(name), { method: 'DELETE' });
|
|
113
141
|
const data = await res.json();
|
|
114
142
|
if (data.success) location.reload();
|
|
115
143
|
else alert('Delete failed');
|
|
116
144
|
}
|
|
117
|
-
|
|
145
|
+
|
|
146
|
+
// Render markdown for plans
|
|
147
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
148
|
+
document.querySelectorAll('[data-md]').forEach(el => {
|
|
149
|
+
if (typeof marked !== 'undefined') {
|
|
150
|
+
el.innerHTML = marked.parse(decodeURIComponent(el.dataset.md));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
</script>
|
package/router.md
CHANGED
|
@@ -39,38 +39,45 @@ Once the user provides a user story or testing request, follow this workflow. **
|
|
|
39
39
|
|
|
40
40
|
### Phase 1: Plan & Explore
|
|
41
41
|
User says: *"Plan tests for this user story"*
|
|
42
|
-
1. Read `.github/agents/playwright-test-planner.agent.md`
|
|
43
|
-
2.
|
|
44
|
-
3.
|
|
45
|
-
4.
|
|
46
|
-
5.
|
|
42
|
+
1. Read `.github/agents/playwright-test-planner.agent.md` (or `.opencode/agents/qa-planner.md`)
|
|
43
|
+
2. Read `.opencode/qa-workflow-skill.md` for project context and conventions
|
|
44
|
+
3. Explore the app with Playwright MCP
|
|
45
|
+
4. Write test plan to `specs/`
|
|
46
|
+
5. **STOP** — present the plan to the user
|
|
47
|
+
6. Wait for approval before continuing
|
|
47
48
|
|
|
48
49
|
### Phase 2: Generate Tests
|
|
49
50
|
User says: *"Generate tests from the plan"*
|
|
50
|
-
1. Read `.github/agents/playwright-test-generator.agent.md`
|
|
51
|
-
2. Read
|
|
52
|
-
3.
|
|
53
|
-
4.
|
|
54
|
-
5.
|
|
51
|
+
1. Read `.github/agents/playwright-test-generator.agent.md` (or `.opencode/agents/qa-generator.md`)
|
|
52
|
+
2. Read `.opencode/qa-workflow-skill.md` for Allure/Applitools/reporter setup
|
|
53
|
+
3. Read `prompts/QAe2eprompt.md` for conventions
|
|
54
|
+
4. Write Playwright test files to `tests/`
|
|
55
|
+
5. **STOP** — present the generated tests to the user
|
|
56
|
+
6. Wait for approval before executing
|
|
55
57
|
|
|
56
58
|
### Phase 3: Execute
|
|
57
59
|
User says: *"Execute the tests"*
|
|
58
60
|
1. User runs `npm run qa:execute` (or you suggest it)
|
|
59
61
|
2. Check results — did they pass or fail?
|
|
62
|
+
3. Allure results are auto-generated in `allure-results/`
|
|
63
|
+
4. View dashboard: `npm run dashboard` → `http://localhost:4000`
|
|
60
64
|
|
|
61
65
|
### Phase 4: Heal Failures
|
|
62
66
|
If tests fail, user says: *"Fix the failing tests"*
|
|
63
|
-
1. Read `.github/agents/playwright-test-healer.agent.md`
|
|
64
|
-
2.
|
|
65
|
-
3.
|
|
66
|
-
4.
|
|
67
|
-
5.
|
|
68
|
-
6.
|
|
67
|
+
1. Read `.github/agents/playwright-test-healer.agent.md` (or `.opencode/agents/qa-healer.md`)
|
|
68
|
+
2. Read `.opencode/qa-workflow-skill.md` for self-healing patterns
|
|
69
|
+
3. Debug with Playwright MCP (`test_debug`, `browser_snapshot`, etc.)
|
|
70
|
+
4. Present diagnosis to the user (root cause + proposed 1-3 line fix)
|
|
71
|
+
5. **STOP** — wait for user to approve the fix
|
|
72
|
+
6. Apply fix and re-run
|
|
73
|
+
7. If still failing, mark `test.fixme()` and classify as defect
|
|
69
74
|
|
|
70
75
|
### Phase 5: Report
|
|
71
76
|
User says: *"Generate the report"*
|
|
72
77
|
1. User runs `npm run qa:report` (or you suggest it)
|
|
73
|
-
2.
|
|
78
|
+
2. Or generate Allure report: `npm run qa:report:allure`
|
|
79
|
+
3. Or open the QA Dashboard: `npm run dashboard`
|
|
80
|
+
4. Present the report summary to the user
|
|
74
81
|
|
|
75
82
|
---
|
|
76
83
|
|
|
@@ -92,9 +99,12 @@ User says: *"Generate the report"*
|
|
|
92
99
|
|
|
93
100
|
| User intent | Read this |
|
|
94
101
|
|------------|-----------|
|
|
102
|
+
| General QA / test writing best practices | `.opencode/qa-workflow-skill.md` |
|
|
95
103
|
| "Plan tests / Explore the app / Write a test plan" | `.github/agents/playwright-test-planner.agent.md` |
|
|
96
104
|
| "Generate tests / Write test code / Create spec files" | `.github/agents/playwright-test-generator.agent.md` + `prompts/QAe2eprompt.md` |
|
|
97
105
|
| "Fix failing tests / Debug / Heal" | `.github/agents/playwright-test-healer.agent.md` |
|
|
106
|
+
| Allure / Dashboard / Reporting | `.opencode/qa-workflow-skill.md` |
|
|
107
|
+
| Applitools visual testing | `.opencode/qa-workflow-skill.md` |
|
|
98
108
|
| General QA requests | `prompts/general_prompt.md` |
|
|
99
109
|
|
|
100
110
|
---
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
# AI QA Execution Report
|
|
2
2
|
|
|
3
3
|
**Run ID**: run-2026-05-18T14-51-36
|
|
4
|
-
**Generated**: 2026-
|
|
4
|
+
**Generated**: 2026-06-09T15:03:06.572Z
|
|
5
5
|
**Status**: ✅ PASSED
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Traceability
|
|
11
|
+
|
|
12
|
+
No traceability data available.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
7
16
|
---
|
|
8
17
|
|
|
9
18
|
## Executive Summary
|
|
@@ -23,7 +32,6 @@
|
|
|
23
32
|
|
|
24
33
|
| Test File | Scenarios |
|
|
25
34
|
|-----------|-----------|
|
|
26
|
-
| us-expense-01.spec.ts | 1 |
|
|
27
35
|
|
|
28
36
|
## Self-Healing Activity
|
|
29
37
|
|