qa-intelligence 1.1.3 → 1.1.4
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 +30 -146
- package/dist/cli/index.js +39 -0
- package/dist/cli/init.d.ts +10 -0
- package/dist/cli/init.js +99 -0
- package/package.json +4 -2
- package/templates/github/workflows/qa-intelligence.yml +129 -0
- package/templates/playwright/.env.example +8 -0
- package/templates/playwright/package.json +11 -0
- package/templates/playwright/playwright.config.ts +18 -0
- package/templates/playwright/tests/example.spec.ts +6 -0
- package/templates/playwright/tsconfig.json +12 -0
package/README.md
CHANGED
|
@@ -12,158 +12,49 @@ The intelligence engine for Playwright CI pipelines — install it into any proj
|
|
|
12
12
|
|
|
13
13
|
Use it **without the framework template** — install the package into your existing app repo.
|
|
14
14
|
|
|
15
|
-
**Recommended layout:** create a `playwright/` subfolder at the repo root and keep all E2E tooling there, separate from your main app code. Full step-by-step guide (folder layout, `Dockerfile`, CI path changes): **[qa-intelligence-framework README](https://github.com/ardithaqi/qa-intelligence-framework#add-to-an-existing-project-npm-package)**.
|
|
16
15
|
|
|
17
16
|
---
|
|
18
17
|
|
|
19
|
-
##
|
|
18
|
+
## Quick start
|
|
20
19
|
|
|
21
|
-
Run
|
|
20
|
+
Run from your **repo root** (not inside `playwright/`). You do **not** need a `playwright/` folder beforehand — `init` creates it.
|
|
22
21
|
|
|
23
22
|
```bash
|
|
23
|
+
npx qa-intelligence init
|
|
24
24
|
cd playwright
|
|
25
|
-
|
|
25
|
+
cp .env.example .env # set BASE_URL
|
|
26
|
+
npm install # installs qa-intelligence + @playwright/test (see playwright/package.json)
|
|
27
|
+
npx playwright install
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
`init` scaffolds:
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
## Setup
|
|
33
|
-
|
|
34
|
-
All paths below are relative to `playwright/` when using the recommended subfolder layout.
|
|
35
|
-
|
|
36
|
-
### 1. Environment (`.env`)
|
|
37
|
-
|
|
38
|
-
```env
|
|
39
|
-
BASE_URL=https://your-app.example.com
|
|
40
|
-
HEADLESS=true
|
|
41
|
-
PW_WORKERS=2
|
|
42
|
-
PW_RETRIES=1
|
|
43
|
-
```
|
|
32
|
+
- `playwright/` — `.env.example`, `package.json`, `tsconfig.json`, `playwright.config.ts`, example test
|
|
33
|
+
- `.github/workflows/qa-intelligence.yml` — PR diff, history, and comment (skip with `--no-ci`)
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```json
|
|
48
|
-
{
|
|
49
|
-
"compilerOptions": {
|
|
50
|
-
"target": "ES2022",
|
|
51
|
-
"module": "Node16",
|
|
52
|
-
"moduleResolution": "Node16",
|
|
53
|
-
"strict": true,
|
|
54
|
-
"esModuleInterop": true,
|
|
55
|
-
"types": ["node"],
|
|
56
|
-
"skipLibCheck": true
|
|
57
|
-
},
|
|
58
|
-
"include": ["tests/**/*", "playwright.config.ts"]
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
> `module` and `moduleResolution` must both be `"Node16"` so TypeScript resolves the package `exports` map.
|
|
35
|
+
**Then:** add `OPENAI_API_KEY` to GitHub secrets and set `BASE_URL` in the workflow file.
|
|
63
36
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
```ts
|
|
67
|
-
import { defineConfig } from "@playwright/test";
|
|
68
|
-
import { env } from "qa-intelligence/config/env";
|
|
69
|
-
|
|
70
|
-
export default defineConfig({
|
|
71
|
-
testDir: "./tests",
|
|
72
|
-
retries: env.PW_RETRIES,
|
|
73
|
-
workers: env.PW_WORKERS,
|
|
74
|
-
globalSetup: require.resolve("qa-intelligence/playwright/globalSetup"),
|
|
75
|
-
globalTeardown: require.resolve("qa-intelligence/playwright/globalTeardown"),
|
|
76
|
-
use: {
|
|
77
|
-
baseURL: env.BASE_URL,
|
|
78
|
-
headless: env.HEADLESS,
|
|
79
|
-
trace: "on-first-retry",
|
|
80
|
-
screenshot: "only-on-failure",
|
|
81
|
-
video: "retain-on-failure",
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 4. Write tests
|
|
87
|
-
|
|
88
|
-
**Important** — always import `test` and `expect` from the package:
|
|
37
|
+
**Tests** — always import from the package, not Playwright directly:
|
|
89
38
|
|
|
90
39
|
```ts
|
|
91
40
|
import { test, expect } from "qa-intelligence/playwright";
|
|
92
41
|
```
|
|
93
42
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
// ❌ No AI artifacts, no PR diff, no flaky detection
|
|
98
|
-
import { test, expect } from "@playwright/test";
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Using `qa-intelligence/playwright` enables:
|
|
102
|
-
|
|
103
|
-
- AI failure analysis (`meta.json` → `ai.txt`)
|
|
104
|
-
- Artifact generation on failure
|
|
105
|
-
- Flaky detection (retry-aware)
|
|
106
|
-
- CI diff intelligence and PR comments
|
|
107
|
-
|
|
108
|
-
Example test:
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
import { test, expect } from "qa-intelligence/playwright";
|
|
112
|
-
|
|
113
|
-
test("user can login", async ({ page }) => {
|
|
114
|
-
await page.goto("/");
|
|
115
|
-
await expect(page).toHaveTitle(/My App/);
|
|
116
|
-
});
|
|
117
|
-
```
|
|
43
|
+
### `init` options
|
|
118
44
|
|
|
119
|
-
|
|
45
|
+
By default, `init` skips any file that already exists so it won't overwrite your work.
|
|
120
46
|
|
|
121
|
-
|
|
47
|
+
| Flag | What it does |
|
|
48
|
+
|------|----------------|
|
|
49
|
+
| `--no-ci` | Only scaffold `playwright/` (config, env, tests). Does **not** create `.github/workflows/qa-intelligence.yml`. Use this if you already have CI or use GitLab/Jenkins. |
|
|
50
|
+
| `--force` | Overwrite existing scaffold files. Use when re-running `init` and you want a fresh copy from the templates. |
|
|
122
51
|
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
|
|
52
|
+
```bash
|
|
53
|
+
npx qa-intelligence init # playwright/ + GitHub workflow
|
|
54
|
+
npx qa-intelligence init --no-ci # playwright/ only
|
|
55
|
+
npx qa-intelligence init --force # overwrite files that already exist
|
|
126
56
|
```
|
|
127
57
|
|
|
128
|
-
### 5. What you provide
|
|
129
|
-
|
|
130
|
-
| You write | Package provides |
|
|
131
|
-
|-----------|------------------|
|
|
132
|
-
| `playwright/tests/` | Test hooks, artifact capture |
|
|
133
|
-
| `playwright/.env` | Env validation |
|
|
134
|
-
| `playwright/playwright.config.ts` | `globalSetup`, `globalTeardown`, AI teardown |
|
|
135
|
-
| `playwright/Dockerfile` | — (copy from [framework](https://github.com/ardithaqi/qa-intelligence-framework/blob/master/Dockerfile)) |
|
|
136
|
-
| `.github/workflows/ci.yml` | `qa-intelligence-diff`, `qa-intelligence-history`, `qa-intelligence-comment` |
|
|
137
|
-
| Page objects (optional) | `BasePage`, `step()` |
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## CI setup (GitHub Actions)
|
|
142
|
-
|
|
143
|
-
Copy [`.github/workflows/ci.yml`](https://github.com/ardithaqi/qa-intelligence-framework/blob/master/.github/workflows/ci.yml) and [`Dockerfile`](https://github.com/ardithaqi/qa-intelligence-framework/blob/master/Dockerfile) from the framework repo.
|
|
144
|
-
|
|
145
|
-
When using the `playwright/` subfolder layout, apply the CI path changes documented in the [framework adoption guide](https://github.com/ardithaqi/qa-intelligence-framework#add-to-an-existing-project-npm-package) (`working-directory: playwright`, artifact paths, etc.).
|
|
146
|
-
|
|
147
|
-
The workflow includes:
|
|
148
|
-
|
|
149
|
-
- Docker test execution
|
|
150
|
-
- Artifact upload on every run
|
|
151
|
-
- Baseline download from `main` on pull requests
|
|
152
|
-
- `qa-intelligence-diff`, `qa-intelligence-history`, `qa-intelligence-comment`
|
|
153
|
-
- PR blocking on new non-flaky failures
|
|
154
|
-
|
|
155
|
-
Then update:
|
|
156
|
-
|
|
157
|
-
- `BASE_URL` in the `docker run` step
|
|
158
|
-
- GitHub secrets (see below)
|
|
159
|
-
|
|
160
|
-
### Required GitHub secrets
|
|
161
|
-
|
|
162
|
-
- `OPENAI_API_KEY` — enables AI failure analysis in teardown
|
|
163
|
-
- `TEST_USERNAME` / `TEST_PASSWORD` — if your tests need credentials
|
|
164
|
-
|
|
165
|
-
Set `AI_ANALYSIS=true` in CI when running tests inside Docker.
|
|
166
|
-
|
|
167
58
|
---
|
|
168
59
|
|
|
169
60
|
## PR behavior
|
|
@@ -177,20 +68,21 @@ Set `AI_ANALYSIS=true` in CI when running tests inside Docker.
|
|
|
177
68
|
|
|
178
69
|
---
|
|
179
70
|
|
|
180
|
-
## CLI
|
|
71
|
+
## CLI
|
|
181
72
|
|
|
182
73
|
| Command | Purpose |
|
|
183
74
|
|---------|---------|
|
|
75
|
+
| `qa-intelligence init` | Scaffold project files |
|
|
184
76
|
| `qa-intelligence-diff` | Compare baseline vs current failures |
|
|
185
|
-
| `qa-intelligence-history` |
|
|
186
|
-
| `qa-intelligence-comment` | Post/update PR summary
|
|
77
|
+
| `qa-intelligence-history` | Recurrence tracking |
|
|
78
|
+
| `qa-intelligence-comment` | Post/update PR summary |
|
|
187
79
|
|
|
188
80
|
---
|
|
189
81
|
|
|
190
82
|
## Package exports
|
|
191
83
|
|
|
192
|
-
| Import
|
|
193
|
-
|
|
84
|
+
| Import | What you get |
|
|
85
|
+
|--------|--------------|
|
|
194
86
|
| `qa-intelligence/playwright` | `test`, `expect`, `env` |
|
|
195
87
|
| `qa-intelligence/playwright/globalSetup` | Artifact run setup |
|
|
196
88
|
| `qa-intelligence/playwright/globalTeardown` | AI failure analysis |
|
|
@@ -200,19 +92,11 @@ Set `AI_ANALYSIS=true` in CI when running tests inside Docker.
|
|
|
200
92
|
|
|
201
93
|
---
|
|
202
94
|
|
|
203
|
-
##
|
|
204
|
-
|
|
205
|
-
If you prefer a ready-made project with examples, Docker, and CI pre-wired:
|
|
206
|
-
|
|
207
|
-
**[qa-intelligence-framework](https://github.com/ardithaqi/qa-intelligence-framework)** — click "Use this template".
|
|
208
|
-
|
|
209
|
-
The template uses this package under the hood.
|
|
210
|
-
|
|
211
|
-
---
|
|
95
|
+
## Full template (optional)
|
|
212
96
|
|
|
213
|
-
|
|
97
|
+
Prefer a ready-made project with Docker, example tests, and CI pre-wired? Use the **[qa-intelligence-framework](https://github.com/ardithaqi/qa-intelligence-framework)** template — click "Use this template".
|
|
214
98
|
|
|
215
|
-
|
|
99
|
+
For adding to an existing repo with a `playwright/` subfolder, see the [framework adoption guide](https://github.com/ardithaqi/qa-intelligence-framework#add-to-an-existing-project-npm-package) (`Dockerfile`, CI path changes).
|
|
216
100
|
|
|
217
101
|
---
|
|
218
102
|
|
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
3
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
37
|
const child_process_1 = require("child_process");
|
|
5
38
|
function getArg(name) {
|
|
@@ -9,6 +42,12 @@ function getArg(name) {
|
|
|
9
42
|
return process.argv[idx + 1];
|
|
10
43
|
}
|
|
11
44
|
async function main() {
|
|
45
|
+
const subcommand = process.argv[2];
|
|
46
|
+
if (subcommand === "init") {
|
|
47
|
+
const { main: runInit } = await Promise.resolve().then(() => __importStar(require("./init")));
|
|
48
|
+
await runInit();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
12
51
|
const baseline = getArg("baseline") ?? "baseline-artifacts";
|
|
13
52
|
const current = getArg("current") ?? "artifacts";
|
|
14
53
|
const repo = getArg("repo");
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.runInit = runInit;
|
|
8
|
+
exports.main = main;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const PLAYWRIGHT_SCAFFOLD = [
|
|
12
|
+
{ template: "playwright/.env.example", dest: "playwright/.env.example" },
|
|
13
|
+
{ template: "playwright/tsconfig.json", dest: "playwright/tsconfig.json" },
|
|
14
|
+
{
|
|
15
|
+
template: "playwright/playwright.config.ts",
|
|
16
|
+
dest: "playwright/playwright.config.ts",
|
|
17
|
+
},
|
|
18
|
+
{ template: "playwright/package.json", dest: "playwright/package.json" },
|
|
19
|
+
{
|
|
20
|
+
template: "playwright/tests/example.spec.ts",
|
|
21
|
+
dest: "playwright/tests/example.spec.ts",
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
const CI_SCAFFOLD = [
|
|
25
|
+
{
|
|
26
|
+
template: "github/workflows/qa-intelligence.yml",
|
|
27
|
+
dest: ".github/workflows/qa-intelligence.yml",
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
function hasFlag(name) {
|
|
31
|
+
return process.argv.includes(`--${name}`);
|
|
32
|
+
}
|
|
33
|
+
function templatesDir() {
|
|
34
|
+
return path_1.default.join(__dirname, "..", "..", "templates");
|
|
35
|
+
}
|
|
36
|
+
function copyScaffold(root, entries, force) {
|
|
37
|
+
const written = [];
|
|
38
|
+
const skipped = [];
|
|
39
|
+
const sourceRoot = templatesDir();
|
|
40
|
+
for (const { template, dest } of entries) {
|
|
41
|
+
const source = path_1.default.join(sourceRoot, template);
|
|
42
|
+
const target = path_1.default.join(root, dest);
|
|
43
|
+
if (!fs_1.default.existsSync(source)) {
|
|
44
|
+
throw new Error(`Missing template: ${template}`);
|
|
45
|
+
}
|
|
46
|
+
if (fs_1.default.existsSync(target) && !force) {
|
|
47
|
+
skipped.push(dest);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
fs_1.default.mkdirSync(path_1.default.dirname(target), { recursive: true });
|
|
51
|
+
fs_1.default.copyFileSync(source, target);
|
|
52
|
+
written.push(dest);
|
|
53
|
+
}
|
|
54
|
+
return { written, skipped };
|
|
55
|
+
}
|
|
56
|
+
function runInit(options) {
|
|
57
|
+
const root = path_1.default.resolve(options?.root ?? process.cwd());
|
|
58
|
+
const force = options?.force ?? false;
|
|
59
|
+
const withCi = options?.withCi ?? true;
|
|
60
|
+
const entries = [...PLAYWRIGHT_SCAFFOLD];
|
|
61
|
+
if (withCi) {
|
|
62
|
+
entries.push(...CI_SCAFFOLD);
|
|
63
|
+
}
|
|
64
|
+
return copyScaffold(root, entries, force);
|
|
65
|
+
}
|
|
66
|
+
async function main() {
|
|
67
|
+
const force = hasFlag("force");
|
|
68
|
+
const noCi = hasFlag("no-ci");
|
|
69
|
+
console.log("Scaffolding qa-intelligence in:", process.cwd());
|
|
70
|
+
const { written, skipped } = runInit({
|
|
71
|
+
force,
|
|
72
|
+
withCi: !noCi,
|
|
73
|
+
});
|
|
74
|
+
for (const file of written) {
|
|
75
|
+
console.log(` created ${file}`);
|
|
76
|
+
}
|
|
77
|
+
for (const file of skipped) {
|
|
78
|
+
console.log(` skipped ${file} (exists — use --force to overwrite)`);
|
|
79
|
+
}
|
|
80
|
+
if (written.length === 0 && skipped.length > 0) {
|
|
81
|
+
console.log("\nNothing new was written.");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.log("\nNext steps:");
|
|
85
|
+
console.log(" 1. cd playwright && cp .env.example .env # set BASE_URL");
|
|
86
|
+
console.log(" 2. cd playwright && npm install");
|
|
87
|
+
console.log(" 3. npx playwright install");
|
|
88
|
+
console.log(" 4. Add OPENAI_API_KEY to GitHub repo secrets (for CI)");
|
|
89
|
+
if (!noCi) {
|
|
90
|
+
console.log(" 5. Update BASE_URL in .github/workflows/qa-intelligence.yml");
|
|
91
|
+
}
|
|
92
|
+
console.log(" 6. Write tests — import from qa-intelligence/playwright");
|
|
93
|
+
}
|
|
94
|
+
if (require.main === module) {
|
|
95
|
+
main().catch((e) => {
|
|
96
|
+
console.error(e);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
});
|
|
99
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qa-intelligence",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=18"
|
|
6
6
|
},
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
},
|
|
58
58
|
"bin": {
|
|
59
59
|
"qa-intelligence": "dist/cli/index.js",
|
|
60
|
+
"qa-intelligence-init": "dist/cli/init.js",
|
|
60
61
|
"qa-intelligence-diff": "dist/cli/diff.js",
|
|
61
62
|
"qa-intelligence-history": "dist/cli/history.js",
|
|
62
63
|
"qa-intelligence-comment": "dist/cli/postComment.js"
|
|
@@ -82,6 +83,7 @@
|
|
|
82
83
|
"typescript": "^5.9.3"
|
|
83
84
|
},
|
|
84
85
|
"files": [
|
|
85
|
-
"dist"
|
|
86
|
+
"dist",
|
|
87
|
+
"templates"
|
|
86
88
|
]
|
|
87
89
|
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
name: QA Intelligence CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
actions: read
|
|
12
|
+
issues: write
|
|
13
|
+
pull-requests: write
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
defaults:
|
|
19
|
+
run:
|
|
20
|
+
working-directory: playwright
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- uses: actions/setup-node@v4
|
|
26
|
+
with:
|
|
27
|
+
node-version: 20
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: npm install
|
|
31
|
+
|
|
32
|
+
- name: Install Playwright browsers
|
|
33
|
+
run: npx playwright install --with-deps chromium
|
|
34
|
+
|
|
35
|
+
- name: Clean artifacts
|
|
36
|
+
if: always()
|
|
37
|
+
run: rm -rf artifacts
|
|
38
|
+
|
|
39
|
+
- name: Run Playwright tests
|
|
40
|
+
env:
|
|
41
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
42
|
+
AI_ANALYSIS: true
|
|
43
|
+
BASE_URL: https://your-app.example.com
|
|
44
|
+
HEADLESS: true
|
|
45
|
+
PW_WORKERS: 2
|
|
46
|
+
PW_RETRIES: 1
|
|
47
|
+
TEST_USERNAME: ${{ secrets.TEST_USERNAME }}
|
|
48
|
+
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
|
|
49
|
+
run: |
|
|
50
|
+
set +e
|
|
51
|
+
npx playwright test
|
|
52
|
+
EXIT=$?
|
|
53
|
+
if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then exit 0; fi
|
|
54
|
+
exit $EXIT
|
|
55
|
+
|
|
56
|
+
- name: Upload Playwright HTML report
|
|
57
|
+
if: always()
|
|
58
|
+
uses: actions/upload-artifact@v4
|
|
59
|
+
with:
|
|
60
|
+
name: playwright-report
|
|
61
|
+
path: playwright/playwright-report/
|
|
62
|
+
|
|
63
|
+
- name: Upload AI failure artifacts
|
|
64
|
+
if: always()
|
|
65
|
+
uses: actions/upload-artifact@v4
|
|
66
|
+
with:
|
|
67
|
+
name: ai-failure-artifacts
|
|
68
|
+
path: playwright/artifacts/
|
|
69
|
+
|
|
70
|
+
- name: Parse AI failure reports
|
|
71
|
+
if: always()
|
|
72
|
+
run: |
|
|
73
|
+
echo "Parsing AI failure reports..."
|
|
74
|
+
if [ -d "artifacts" ]; then
|
|
75
|
+
find artifacts -name "ai.txt" | while read file; do
|
|
76
|
+
echo "-----"
|
|
77
|
+
echo "File: $file"
|
|
78
|
+
json=$(awk '/^{/{flag=1} flag' "$file")
|
|
79
|
+
echo "$json" | jq .
|
|
80
|
+
done
|
|
81
|
+
else
|
|
82
|
+
echo "No artifacts directory found."
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
- name: Download baseline artifacts from main
|
|
86
|
+
if: always() && github.event_name == 'pull_request'
|
|
87
|
+
uses: dawidd6/action-download-artifact@v6
|
|
88
|
+
with:
|
|
89
|
+
workflow: qa-intelligence.yml
|
|
90
|
+
branch: ${{ github.base_ref }}
|
|
91
|
+
name: ai-failure-artifacts
|
|
92
|
+
path: baseline-artifacts
|
|
93
|
+
if_no_artifact_found: warn
|
|
94
|
+
|
|
95
|
+
- name: Debug baseline contents
|
|
96
|
+
if: always() && github.event_name == 'pull_request'
|
|
97
|
+
run: |
|
|
98
|
+
echo "Baseline:"
|
|
99
|
+
ls -la ../baseline-artifacts || true
|
|
100
|
+
echo "Current:"
|
|
101
|
+
ls -la artifacts || true
|
|
102
|
+
|
|
103
|
+
- name: Compute failure diff
|
|
104
|
+
if: always() && github.event_name == 'pull_request'
|
|
105
|
+
run: npx qa-intelligence-diff --baseline ../baseline-artifacts --current artifacts
|
|
106
|
+
|
|
107
|
+
- name: Restore failure history cache
|
|
108
|
+
if: always() && github.event_name == 'pull_request'
|
|
109
|
+
uses: actions/cache@v4
|
|
110
|
+
with:
|
|
111
|
+
path: playwright/.cache
|
|
112
|
+
key: failure-history-${{ github.repository }}-${{ github.sha }}
|
|
113
|
+
restore-keys: |
|
|
114
|
+
failure-history-${{ github.repository }}-
|
|
115
|
+
|
|
116
|
+
- name: Update failure history (recurrence)
|
|
117
|
+
if: always() && github.event_name == 'pull_request'
|
|
118
|
+
run: npx qa-intelligence-history
|
|
119
|
+
|
|
120
|
+
- name: Comment PR with AI summary
|
|
121
|
+
if: always() && github.event_name == 'pull_request'
|
|
122
|
+
env:
|
|
123
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
124
|
+
run: |
|
|
125
|
+
npx qa-intelligence-comment \
|
|
126
|
+
--diff failure-diff.json \
|
|
127
|
+
--repo ${{ github.repository }} \
|
|
128
|
+
--pr ${{ github.event.pull_request.number }} \
|
|
129
|
+
--token $GITHUB_TOKEN
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig } from "@playwright/test";
|
|
2
|
+
import { env } from "qa-intelligence/config/env";
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
testDir: "./tests",
|
|
6
|
+
retries: env.PW_RETRIES,
|
|
7
|
+
workers: env.PW_WORKERS,
|
|
8
|
+
globalSetup: require.resolve("qa-intelligence/playwright/globalSetup"),
|
|
9
|
+
globalTeardown: require.resolve("qa-intelligence/playwright/globalTeardown"),
|
|
10
|
+
use: {
|
|
11
|
+
baseURL: env.BASE_URL,
|
|
12
|
+
headless: env.HEADLESS,
|
|
13
|
+
trace: "on-first-retry",
|
|
14
|
+
screenshot: "only-on-failure",
|
|
15
|
+
video: "retain-on-failure",
|
|
16
|
+
},
|
|
17
|
+
reporter: [["html", { open: "never" }]],
|
|
18
|
+
});
|