@qualflare/playwright 0.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.
@@ -0,0 +1,292 @@
1
+ /**
2
+ * The Qualflare `/api/v1/collect` wire contract.
3
+ *
4
+ * These interfaces mirror `api-service/internal/core/domain/launch/launch.go`
5
+ * field-for-field (JSON key names, optionality, and string-union values) as
6
+ * verified against the live, deployed source. Do not add, rename, or change
7
+ * the optionality of a field here without re-verifying against that file —
8
+ * a mismatch either 400s the request or silently drops data server-side.
9
+ *
10
+ * Kept identical to `@qualflare/cypress`'s copy of this file — the two
11
+ * packages report to the same backend, so this file should stay in sync
12
+ * across both.
13
+ */
14
+ /** Every `duration` field in this contract is a raw integer number of
15
+ * NANOSECONDS, with no unit marker on the wire. Playwright reports
16
+ * `{seconds, nanos}` — convert directly to nanoseconds (see `./duration.ts`)
17
+ * before assigning into one of these fields. */
18
+ type NanosecondDuration = number;
19
+ type Platform = 'android' | 'ios' | 'desktop' | 'web' | 'api';
20
+ type CaseStatus = 'passed' | 'failed' | 'skipped' | 'error' | 'timeout' | 'aborted' | 'pending';
21
+ /**
22
+ * The server's oneof accepts a value named after any of the ~23 frameworks it
23
+ * auto-detects (e.g. "playwright"), not just the six coarse buckets below — a
24
+ * suite's category is meant to say exactly which tool produced it, which is
25
+ * what lets the UI show that tool's own logo. This reporter only ever emits
26
+ * 'playwright' (see suite-builder.ts), so that's the only
27
+ * tool-specific value listed here; the six buckets remain for backward
28
+ * compatibility / servers that haven't been upgraded.
29
+ */
30
+ type FrameworkCategory = 'playwright' | 'unit' | 'bdd' | 'e2e' | 'api' | 'security' | 'generic';
31
+ type CasePriority = 'low' | 'medium' | 'high' | 'critical';
32
+ type LinkType = 'issue' | 'tms' | 'custom';
33
+ interface Metadata {
34
+ version: string;
35
+ timestamp: string;
36
+ cliName: string;
37
+ }
38
+ interface Label {
39
+ /** Required, max 128 chars. Allure-style arbitrary label name — epic/feature/story/owner/severity
40
+ * are just conventional names, not separate fields. */
41
+ name: string;
42
+ /** Required, max 512 chars. */
43
+ value: string;
44
+ }
45
+ interface Link {
46
+ /** Required. A small, server-owned closed taxonomy — not open text. */
47
+ type: LinkType;
48
+ /** Optional, max 255 chars. */
49
+ name?: string;
50
+ /** Required, must be a valid URL, max 2048 chars. */
51
+ url: string;
52
+ }
53
+ interface Parameter {
54
+ name: string;
55
+ value?: string;
56
+ /** Display hint ONLY — the server does not redact this value. Do not treat
57
+ * this as real secret protection. */
58
+ masked?: boolean;
59
+ }
60
+ interface Step {
61
+ name: string;
62
+ keyword?: string;
63
+ /** Reuses the Case status vocabulary. */
64
+ status: CaseStatus | string;
65
+ /** NANOSECONDS — see `NanosecondDuration`. */
66
+ duration: NanosecondDuration;
67
+ error?: string;
68
+ location?: string;
69
+ /** 0-based index into the SAME Case's `steps[]` array identifying this
70
+ * step's parent, for nested/hierarchical steps. Omit for a root step.
71
+ * The server drops an out-of-range or cycle-forming value to root rather
72
+ * than rejecting the request — but a well-behaved client shouldn't rely
73
+ * on that. Server caps total steps per case at 1000. */
74
+ parentIndex?: number;
75
+ /** Server caps at 50 parameters per step. */
76
+ parameters?: Parameter[];
77
+ }
78
+ interface Attachment {
79
+ /** Required, max 255 chars. */
80
+ name: string;
81
+ /** Optional, max 1024 chars — informational only, never fetched server-side. */
82
+ path?: string;
83
+ /** Optional, max 255 chars. */
84
+ mimeType?: string;
85
+ /** Base64-encoded content, max 2,097,152 characters (~1.5MB decoded binary).
86
+ * Mutually exclusive with `storageKey` — used for small inline attachments. */
87
+ content?: string;
88
+ /** R2 object key from a prior `POST /api/v1/attachments/upload-url` call —
89
+ * mutually exclusive with `content`. Used for attachments too large to
90
+ * inline (video); if both are set, `storageKey` wins server-side. Max 1024
91
+ * chars. */
92
+ storageKey?: string;
93
+ /** Set when this is a video the reporter copied into the same output
94
+ * directory as the report file, rather than uploading it itself —
95
+ * `qualflare-cli` resolves this into a real `storageKey` at collect time.
96
+ * Relative to the report file's own directory. Never sent to `/collect`
97
+ * directly; mutually exclusive with `content`/`storageKey`. */
98
+ localVideoPath?: string;
99
+ /** Byte size of the object at `storageKey`. Ignored when `storageKey` is
100
+ * unset. */
101
+ fileSize?: number;
102
+ /** 0-based index into the Case's `steps[]` this attachment belongs to.
103
+ * Omit for a case-level (not step-level) attachment. */
104
+ stepIndex?: number;
105
+ }
106
+ interface Case {
107
+ /** Required. A stable per-test identifier used for flaky-history matching
108
+ * across separate runs — must stay the same for what a human would call
109
+ * "the same test" across renames you want tracked together. */
110
+ id: string;
111
+ /** Required, 1-255 chars. */
112
+ name: string;
113
+ /** Max 255 chars. */
114
+ className?: string;
115
+ /** Max 10000 chars. */
116
+ description?: string;
117
+ status: CaseStatus;
118
+ /** NANOSECONDS — see `NanosecondDuration`. */
119
+ duration: NanosecondDuration;
120
+ retryCount?: number;
121
+ isFlaky?: boolean;
122
+ /** Truncated server-side at 65536 runes, never validation-rejected — send
123
+ * the full error/stack text, don't pre-truncate. */
124
+ error?: string;
125
+ /** Unrecognized values are silently normalized/dropped server-side, never
126
+ * rejects the request. */
127
+ priority?: CasePriority;
128
+ /** Max 64 items, each max 255 chars. */
129
+ tags?: string[];
130
+ properties?: Record<string, string>;
131
+ /** Max 50 items. */
132
+ attachments?: Attachment[];
133
+ /** Server caps at 1000 items and truncates individual fields — never
134
+ * validation-rejected, send freely. */
135
+ steps?: Step[];
136
+ /** Not validated; the server drops an out-of-range value rather than
137
+ * rejecting the request. */
138
+ shardIndex?: number;
139
+ /** RFC3339. */
140
+ startedAt?: string;
141
+ /** Max 100 items. */
142
+ labels?: Label[];
143
+ /** Max 20 items. */
144
+ links?: Link[];
145
+ }
146
+ interface Suite {
147
+ /** Required, 1-255 chars. One Suite per Playwright spec file. */
148
+ name: string;
149
+ category?: FrameworkCategory;
150
+ assertions?: number;
151
+ /** NANOSECONDS — see `NanosecondDuration`. */
152
+ duration: NanosecondDuration;
153
+ /** RFC3339. */
154
+ timestamp?: string;
155
+ /** Max 64 chars. Per-suite override of the launch-level `browser` — omit
156
+ * to inherit the launch value. */
157
+ browser?: string;
158
+ /** Max 64 chars. Per-suite override of the launch-level `os`. */
159
+ os?: string;
160
+ properties?: Record<string, string>;
161
+ /** Max 5000 items. */
162
+ cases: Case[];
163
+ }
164
+ interface Collect {
165
+ /** Required, 1-100 chars. */
166
+ framework: string;
167
+ platform: Platform;
168
+ /** Required, 1-100 chars. */
169
+ os: string;
170
+ /** Max 64 chars. */
171
+ browser: string;
172
+ /** ALWAYS present as value-or-null on the wire — never omit this key. */
173
+ branch: string | null;
174
+ /** ALWAYS present as value-or-null on the wire — never omit this key. */
175
+ commit: string | null;
176
+ /** Required, 1-100 chars. Must already exist server-side (404 if not) —
177
+ * every project seeds `development`/`staging`/`production`/`qa` by
178
+ * default. */
179
+ environment: string;
180
+ /** Required, BCP47 (e.g. "en-US"). */
181
+ language: string;
182
+ /** ALWAYS present as value-or-null on the wire — never omit this key. */
183
+ milestone: number | null;
184
+ metadata: Metadata | null;
185
+ properties?: Record<string, string>;
186
+ /** Max 2000 items. */
187
+ suites: Suite[];
188
+ /** Max 64 chars. Free text, no enum — an unrecognized CI provider must
189
+ * never reject the request. */
190
+ ciProvider?: string;
191
+ /** Max 128 chars. */
192
+ ciBuildNumber?: string;
193
+ /** Must be a valid URL, max 2048 chars. */
194
+ ciRunUrl?: string;
195
+ /** Must be >= 1. */
196
+ ciPrNumber?: number;
197
+ }
198
+
199
+ /** Options for the reporter, passed as the second element of its entry in
200
+ * `playwright.config.ts`'s `reporter` array:
201
+ * `['@qualflare/playwright/reporter', { ... }]`. Every field here also has an
202
+ * environment-variable override — see the precedence table in
203
+ * `docs/CONFIGURATION.md`. */
204
+ interface QualflarePlaywrightOptions {
205
+ environment?: string;
206
+ language?: string;
207
+ milestone?: number | null;
208
+ branch?: string | null;
209
+ commit?: string | null;
210
+ platform?: Platform;
211
+ framework?: string;
212
+ os?: string;
213
+ browser?: string;
214
+ properties?: Record<string, string>;
215
+ /** Max 64 chars. Free text, no enum — an unrecognized CI provider must
216
+ * never be rejected. Auto-detected via `ci-detect.ts` when omitted. */
217
+ ciProvider?: string;
218
+ ciBuildNumber?: string;
219
+ ciRunUrl?: string;
220
+ ciPrNumber?: number;
221
+ attachScreenshots?: boolean;
222
+ /** Include Playwright's runner-internal steps — `pw:api` (every
223
+ * `page.click()`, `locator.fill()`, ...) and `fixture` (the implicit
224
+ * `browser`/`context`/`page` setup every browser test opens with) — as
225
+ * reported Steps.
226
+ *
227
+ * Off by default: a single browser test routinely produces hundreds of
228
+ * them, which buries the user-authored `test.step()`/`expect` boundaries
229
+ * that are actually legible in a report and blows through
230
+ * MAX_STEPS_PER_TEST_ATTEMPT on noise. A step that FAILED is always kept
231
+ * regardless of this setting, since a failing API call or fixture is
232
+ * usually the single most useful line in the trace. */
233
+ includeApiSteps?: boolean;
234
+ maxAttachmentBytes?: number;
235
+ maxTotalAttachmentBytes?: number;
236
+ /** Per-video byte cap, checked before the file is written. Default 50MB,
237
+ * matching the server's own hard cap. */
238
+ maxVideoBytes?: number;
239
+ debug?: boolean;
240
+ /** `false` fully disables accumulation/upload (a complete no-op) but the
241
+ * reporter still no-ops cleanly rather than throwing. */
242
+ enabled?: boolean;
243
+ /** Directory `onEnd()` writes this process's report file (and any
244
+ * video attachments) into. Default `./qualflare-results`. Always active —
245
+ * this reporter never uploads anything itself; `qualflare-cli` reads
246
+ * whatever ends up in this directory. Every JSON file this process writes
247
+ * is uniquely named, so multiple shards can safely share one `outputDir`
248
+ * without colliding — see docs/LIMITATIONS.md. */
249
+ outputDir?: string;
250
+ /** This process's 0-based position among parallel shards of the same CI
251
+ * run, stamped onto every case it reports. Purely a label: `qualflare-cli`
252
+ * merges by "every file in the directory", not by this value, so an
253
+ * unset shardIndex costs attribution, never correctness.
254
+ *
255
+ * Auto-detected, in order: `QUALFLARE_SHARD_INDEX`, then Playwright's own
256
+ * `--shard i/N`, which it exposes to reporters as `FullConfig.shard`
257
+ * ({ current, total }). Playwright's `current` is 1-BASED, so the reporter
258
+ * converts it before passing it here as `deps.detectedShardIndex`.
259
+ *
260
+ * This is the one place Playwright is markedly better than its siblings:
261
+ * Cypress has no shard concept at all, and cucumber-js hides its `--shard`
262
+ * from formatters entirely (forcing an argv scrape). Here the runner just
263
+ * tells us. */
264
+ shardIndex?: number;
265
+ }
266
+ interface ResolvedReporterConfig {
267
+ environment: string;
268
+ language: string;
269
+ milestone: number | null;
270
+ branch: string | null;
271
+ commit: string | null;
272
+ platform: Platform;
273
+ framework: string;
274
+ os?: string;
275
+ browser?: string;
276
+ properties?: Record<string, string>;
277
+ ciProvider?: string;
278
+ ciBuildNumber?: string;
279
+ ciRunUrl?: string;
280
+ ciPrNumber?: number;
281
+ attachScreenshots: boolean;
282
+ includeApiSteps: boolean;
283
+ maxAttachmentBytes: number;
284
+ maxTotalAttachmentBytes: number;
285
+ maxVideoBytes: number;
286
+ debug: boolean;
287
+ enabled: boolean;
288
+ outputDir: string;
289
+ shardIndex?: number;
290
+ }
291
+
292
+ export type { Attachment as A, CasePriority as C, FrameworkCategory as F, LinkType as L, Metadata as M, NanosecondDuration as N, Parameter as P, QualflarePlaywrightOptions as Q, ResolvedReporterConfig as R, Step as S, Case as a, CaseStatus as b, Collect as c, Label as d, Link as e, Platform as f, Suite as g };
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@qualflare/playwright",
3
+ "version": "0.1.0",
4
+ "description": "Native Playwright reporter for the Qualflare test-management platform.",
5
+ "keywords": [
6
+ "qualflare",
7
+ "playwright",
8
+ "playwright-reporter",
9
+ "test-reporter",
10
+ "e2e",
11
+ "test-results",
12
+ "flaky-tests",
13
+ "test-reporting"
14
+ ],
15
+ "homepage": "https://qualflare.com",
16
+ "bugs": {
17
+ "url": "https://github.com/Qualflare/qualflare-playwright/issues"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/Qualflare/qualflare-playwright.git"
22
+ },
23
+ "license": "Apache-2.0",
24
+ "author": "Qualflare",
25
+ "type": "module",
26
+ "main": "./dist/index.cjs",
27
+ "module": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js",
33
+ "require": "./dist/index.cjs"
34
+ },
35
+ "./reporter": {
36
+ "types": "./dist/reporter/index.d.ts",
37
+ "import": "./dist/reporter/index.js",
38
+ "require": "./dist/reporter/index.cjs"
39
+ },
40
+ "./package.json": "./package.json"
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "README.md",
45
+ "LICENSE"
46
+ ],
47
+ "engines": {
48
+ "node": ">=18"
49
+ },
50
+ "publishConfig": {
51
+ "access": "public",
52
+ "provenance": true
53
+ },
54
+ "peerDependencies": {
55
+ "@playwright/test": ">=1.40.0"
56
+ },
57
+ "dependencies": {
58
+ "ci-info": "^4.0.0"
59
+ },
60
+ "devDependencies": {
61
+ "@playwright/test": "^1.56.0",
62
+ "@types/node": "^18.19.50",
63
+ "eslint": "^9.9.1",
64
+ "execa": "^9.3.1",
65
+ "prettier": "^3.3.3",
66
+ "tsup": "^8.2.4",
67
+ "typescript": "^5.6.2",
68
+ "typescript-eslint": "^8.5.0",
69
+ "vitest": "^2.0.5"
70
+ },
71
+ "scripts": {
72
+ "build": "tsup",
73
+ "dev": "tsup --watch",
74
+ "typecheck": "tsc --noEmit",
75
+ "lint": "eslint .",
76
+ "test": "vitest run test/unit",
77
+ "test:integration": "vitest run test/integration --pool=forks --testTimeout=120000",
78
+ "test:all": "npm run test && npm run test:integration",
79
+ "prepublishOnly": "npm run build"
80
+ }
81
+ }