@aplaytest/runner-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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/api-fixtures.d.ts +71 -0
- package/dist/api-fixtures.d.ts.map +1 -0
- package/dist/api-fixtures.js +162 -0
- package/dist/api-fixtures.js.map +1 -0
- package/dist/assemble.d.ts +86 -0
- package/dist/assemble.d.ts.map +1 -0
- package/dist/assemble.js +176 -0
- package/dist/assemble.js.map +1 -0
- package/dist/bind.d.ts +43 -0
- package/dist/bind.d.ts.map +1 -0
- package/dist/bind.js +112 -0
- package/dist/bind.js.map +1 -0
- package/dist/fixtures.d.ts +54 -0
- package/dist/fixtures.d.ts.map +1 -0
- package/dist/fixtures.js +210 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/reporter.d.ts +79 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +277 -0
- package/dist/reporter.js.map +1 -0
- package/dist/sidecar.d.ts +134 -0
- package/dist/sidecar.d.ts.map +1 -0
- package/dist/sidecar.js +111 -0
- package/dist/sidecar.js.map +1 -0
- package/dist/spawn.d.ts +57 -0
- package/dist/spawn.d.ts.map +1 -0
- package/dist/spawn.js +170 -0
- package/dist/spawn.js.map +1 -0
- package/dist/steps.d.ts +45 -0
- package/dist/steps.d.ts.map +1 -0
- package/dist/steps.js +106 -0
- package/dist/steps.js.map +1 -0
- package/dist-cjs/.tsbuildinfo +1 -0
- package/dist-cjs/api-fixtures.js +166 -0
- package/dist-cjs/api-fixtures.js.map +1 -0
- package/dist-cjs/assemble.js +181 -0
- package/dist-cjs/assemble.js.map +1 -0
- package/dist-cjs/bind.js +117 -0
- package/dist-cjs/bind.js.map +1 -0
- package/dist-cjs/fixtures.js +215 -0
- package/dist-cjs/fixtures.js.map +1 -0
- package/dist-cjs/index.js +64 -0
- package/dist-cjs/index.js.map +1 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/reporter.js +282 -0
- package/dist-cjs/reporter.js.map +1 -0
- package/dist-cjs/sidecar.js +116 -0
- package/dist-cjs/sidecar.js.map +1 -0
- package/dist-cjs/spawn.js +174 -0
- package/dist-cjs/spawn.js.map +1 -0
- package/dist-cjs/steps.js +112 -0
- package/dist-cjs/steps.js.map +1 -0
- package/package.json +51 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aplaytest/runner-playwright — the Playwright adapter.
|
|
3
|
+
*
|
|
4
|
+
* The only package that knows Playwright exists. Everything above it consumes
|
|
5
|
+
* EvidenceBundles and RunRecords, so swapping in a different runner means
|
|
6
|
+
* writing a sibling of this package and nothing else.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: this package must NOT depend on @aplaytest/llm. The reporter runs inside
|
|
9
|
+
* the test process; pulling an HTTP client and a model SDK into every worker
|
|
10
|
+
* would be both slow and a place a credential should never be.
|
|
11
|
+
*/
|
|
12
|
+
export { default as AtestReporter, ATEST_VERSION } from './reporter.js';
|
|
13
|
+
export type { AtestReporterOptions } from './reporter.js';
|
|
14
|
+
export { assembleBundle, toClassifiable, classifyResult } from './assemble.js';
|
|
15
|
+
export type { AssembleInput, AssembleContext, Sidecars, TestCaseLike, TestResultLike, TestErrorLike, } from './assemble.js';
|
|
16
|
+
export { parsePlaywrightError, splitCallLog, stripAnsi, joinErrors } from '@aplaytest/core';
|
|
17
|
+
export type { ParsedError, ErrorLike } from '@aplaytest/core';
|
|
18
|
+
export { atestFixtures, createCaptureFixture, test, expect } from './fixtures.js';
|
|
19
|
+
export type { CaptureOptions } from './fixtures.js';
|
|
20
|
+
export { bindPage, previewArgs, previewValue } from './bind.js';
|
|
21
|
+
export type { BoundPageObject } from './bind.js';
|
|
22
|
+
export { extractSteps, findFailingStep, parseStepTitle, domainStringArgs } from './steps.js';
|
|
23
|
+
export type { StepLike, ParsedStepTitle } from './steps.js';
|
|
24
|
+
export { SIDECAR, parseSidecar, SidecarParseError, PageSidecarSchema, NetworkSidecarSchema, ConsoleSidecarSchema, IntentSidecarSchema, CoverageSidecarSchema, } from './sidecar.js';
|
|
25
|
+
export type { SidecarName, PageSidecar, NetworkSidecar, ConsoleSidecar, IntentSidecar, CoverageSidecar, } from './sidecar.js';
|
|
26
|
+
export { runPlaywright, escapeForGrep } from './spawn.js';
|
|
27
|
+
export type { PlaywrightRunOptions, PlaywrightRunResult, SpecOutcome } from './spawn.js';
|
|
28
|
+
export { atestApiFixtures, createApiCaptureFixture, recordingContext, } from './api-fixtures.js';
|
|
29
|
+
export type { ApiCaptureOptions } from './api-fixtures.js';
|
|
30
|
+
export { runFileName } from './reporter.js';
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACxE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/E,YAAY,EACV,aAAa,EACb,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,aAAa,GACd,MAAM,eAAe,CAAC;AAIvB,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC5F,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAClF,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC7F,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,EACL,OAAO,EACP,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,cAAc,EACd,aAAa,EACb,eAAe,GAChB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzF,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aplaytest/runner-playwright — the Playwright adapter.
|
|
3
|
+
*
|
|
4
|
+
* The only package that knows Playwright exists. Everything above it consumes
|
|
5
|
+
* EvidenceBundles and RunRecords, so swapping in a different runner means
|
|
6
|
+
* writing a sibling of this package and nothing else.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: this package must NOT depend on @aplaytest/llm. The reporter runs inside
|
|
9
|
+
* the test process; pulling an HTTP client and a model SDK into every worker
|
|
10
|
+
* would be both slow and a place a credential should never be.
|
|
11
|
+
*/
|
|
12
|
+
export { default as AtestReporter, ATEST_VERSION } from './reporter.js';
|
|
13
|
+
export { assembleBundle, toClassifiable, classifyResult } from './assemble.js';
|
|
14
|
+
// Re-exported from @aplaytest/core, where the parser now lives beside the
|
|
15
|
+
// classifier that consumes it. Kept here so existing importers do not break.
|
|
16
|
+
export { parsePlaywrightError, splitCallLog, stripAnsi, joinErrors } from '@aplaytest/core';
|
|
17
|
+
export { atestFixtures, createCaptureFixture, test, expect } from './fixtures.js';
|
|
18
|
+
export { bindPage, previewArgs, previewValue } from './bind.js';
|
|
19
|
+
export { extractSteps, findFailingStep, parseStepTitle, domainStringArgs } from './steps.js';
|
|
20
|
+
export { SIDECAR, parseSidecar, SidecarParseError, PageSidecarSchema, NetworkSidecarSchema, ConsoleSidecarSchema, IntentSidecarSchema, CoverageSidecarSchema, } from './sidecar.js';
|
|
21
|
+
export { runPlaywright, escapeForGrep } from './spawn.js';
|
|
22
|
+
export { atestApiFixtures, createApiCaptureFixture, recordingContext, } from './api-fixtures.js';
|
|
23
|
+
export { runFileName } from './reporter.js';
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGxE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAU/E,0EAA0E;AAC1E,6EAA6E;AAC7E,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG5F,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAGlF,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAGhE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG7F,OAAO,EACL,OAAO,EACP,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AAUtB,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The atest Playwright reporter.
|
|
3
|
+
*
|
|
4
|
+
* INVARIANT: this reporter must never change a run's verdict. It reports no
|
|
5
|
+
* status from `onEnd`, swallows its own errors to stderr, and does its I/O
|
|
6
|
+
* after the run rather than inside it. If atest breaks, the suite still tells
|
|
7
|
+
* the truth about the application — that property is what makes adopting it a
|
|
8
|
+
* one-line, revertible change.
|
|
9
|
+
*
|
|
10
|
+
* Work is COLLECTED in `onTestEnd` (synchronous, cheap: a couple of object
|
|
11
|
+
* references) and FLUSHED in `onEnd` (asynchronous: attachment reads, JSON
|
|
12
|
+
* writes). Playwright's `onTestEnd` cannot await, and doing file I/O per test
|
|
13
|
+
* inside a parallel run is exactly the kind of overhead that would make people
|
|
14
|
+
* turn the tool off.
|
|
15
|
+
*/
|
|
16
|
+
import type { FullConfig, FullResult, Reporter, TestCase, TestResult } from '@playwright/test/reporter';
|
|
17
|
+
import { ATEST_VERSION } from '@aplaytest/core';
|
|
18
|
+
export { ATEST_VERSION };
|
|
19
|
+
export interface AtestReporterOptions {
|
|
20
|
+
/** Directory for evidence bundles. */
|
|
21
|
+
readonly evidenceDir?: string;
|
|
22
|
+
/** Directory for run records. */
|
|
23
|
+
readonly runsDir?: string;
|
|
24
|
+
readonly retainRuns?: number;
|
|
25
|
+
readonly redactKeys?: readonly string[];
|
|
26
|
+
/** Reuse an existing run id — e.g. one shared across CI shards. */
|
|
27
|
+
readonly runId?: string | (() => string);
|
|
28
|
+
/**
|
|
29
|
+
* Reuse deterministic trace ids so history rows join to app spans that
|
|
30
|
+
* already exist, instead of minting a parallel id space.
|
|
31
|
+
*/
|
|
32
|
+
readonly traceId?: (test: {
|
|
33
|
+
id: string;
|
|
34
|
+
}, retry: number) => string;
|
|
35
|
+
/** Set ATEST=0 or pass false to disable entirely. */
|
|
36
|
+
readonly enabled?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export default class AtestReporter implements Reporter {
|
|
39
|
+
private readonly options;
|
|
40
|
+
private readonly collected;
|
|
41
|
+
private readonly workerTests;
|
|
42
|
+
private config;
|
|
43
|
+
private runId;
|
|
44
|
+
private startedAt;
|
|
45
|
+
private enabled;
|
|
46
|
+
constructor(options?: AtestReporterOptions);
|
|
47
|
+
/** We add a short footer only; other reporters own the main output. */
|
|
48
|
+
printsToStdio(): boolean;
|
|
49
|
+
onBegin(config: FullConfig): void;
|
|
50
|
+
onTestEnd(test: TestCase, result: TestResult): void;
|
|
51
|
+
onEnd(_result: FullResult): Promise<void>;
|
|
52
|
+
private flush;
|
|
53
|
+
private readAttachment;
|
|
54
|
+
private readCoverage;
|
|
55
|
+
private readSidecars;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* One file per INVOCATION, not per run.
|
|
59
|
+
*
|
|
60
|
+
* Naming the file `<runId>.json` looks right until the suite is sharded, and
|
|
61
|
+
* then it silently destroys almost all of the data. Every shard shares one run
|
|
62
|
+
* id on purpose — that is how they get merged — so every shard wrote the same
|
|
63
|
+
* filename. Measured on a three-shard run: shards 1 and 2 recorded their
|
|
64
|
+
* attempts, shard 3 had no tests to run, and its empty record overwrote both.
|
|
65
|
+
* The surviving file reported `attempts: 0` for a run that passed four tests.
|
|
66
|
+
*
|
|
67
|
+
* It survives the upload too. CI downloads shard artifacts with
|
|
68
|
+
* `merge-multiple: true`, which flattens them into one directory — identical
|
|
69
|
+
* names collide there as well, so a 6-shard × 9-project matrix would keep one
|
|
70
|
+
* file in fifty-four.
|
|
71
|
+
*
|
|
72
|
+
* Shard and project both go in the name because their matrix varies both, and
|
|
73
|
+
* two jobs differing only by project would otherwise still collide.
|
|
74
|
+
*/
|
|
75
|
+
export declare function runFileName(runId: string, shard: {
|
|
76
|
+
readonly current: number;
|
|
77
|
+
readonly total: number;
|
|
78
|
+
} | null, projects: readonly string[]): string;
|
|
79
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,aAAa,EASd,MAAM,iBAAiB,CAAC;AAKzB,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB,MAAM,WAAW,oBAAoB;IACnC,sCAAsC;IACtC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iCAAiC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IACzC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACnE,qDAAqD;IACrD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AA8BD,MAAM,CAAC,OAAO,OAAO,aAAc,YAAW,QAAQ;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgD;IAC1E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAE3D,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,OAAO,CAAQ;gBAEX,OAAO,GAAE,oBAAyB;IAI9C,uEAAuE;IACvE,aAAa,IAAI,OAAO;IAIxB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAgBjC,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAS7C,KAAK,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;YAUjC,KAAK;YA8HL,cAAc;YAQd,YAAY;YAWZ,YAAY;CAU3B;AAID;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAClE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,MAAM,CASR"}
|
package/dist/reporter.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The atest Playwright reporter.
|
|
3
|
+
*
|
|
4
|
+
* INVARIANT: this reporter must never change a run's verdict. It reports no
|
|
5
|
+
* status from `onEnd`, swallows its own errors to stderr, and does its I/O
|
|
6
|
+
* after the run rather than inside it. If atest breaks, the suite still tells
|
|
7
|
+
* the truth about the application — that property is what makes adopting it a
|
|
8
|
+
* one-line, revertible change.
|
|
9
|
+
*
|
|
10
|
+
* Work is COLLECTED in `onTestEnd` (synchronous, cheap: a couple of object
|
|
11
|
+
* references) and FLUSHED in `onEnd` (asynchronous: attachment reads, JSON
|
|
12
|
+
* writes). Playwright's `onTestEnd` cannot await, and doing file I/O per test
|
|
13
|
+
* inside a parallel run is exactly the kind of overhead that would make people
|
|
14
|
+
* turn the tool off.
|
|
15
|
+
*/
|
|
16
|
+
import { readFile } from 'node:fs/promises';
|
|
17
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
18
|
+
import { dirname, join } from 'node:path';
|
|
19
|
+
import { ATEST_VERSION, EvidenceStore, RUN_SCHEMA_VERSION, isFailure, } from '@aplaytest/core';
|
|
20
|
+
import { assembleBundle, classifyResult } from './assemble.js';
|
|
21
|
+
import { SIDECAR, SidecarParseError, parseSidecar } from './sidecar.js';
|
|
22
|
+
export { ATEST_VERSION };
|
|
23
|
+
const DEFAULTS = {
|
|
24
|
+
evidenceDir: '.atest/evidence',
|
|
25
|
+
runsDir: '.atest/runs',
|
|
26
|
+
retainRuns: 50,
|
|
27
|
+
redactKeys: ['password', 'token', 'authorization', 'cookie', 'secret', 'api-key'],
|
|
28
|
+
};
|
|
29
|
+
function toOutcome(status) {
|
|
30
|
+
switch (status) {
|
|
31
|
+
case 'passed':
|
|
32
|
+
return 'passed';
|
|
33
|
+
case 'failed':
|
|
34
|
+
return 'failed';
|
|
35
|
+
case 'timedOut':
|
|
36
|
+
return 'timedOut';
|
|
37
|
+
case 'skipped':
|
|
38
|
+
return 'skipped';
|
|
39
|
+
case 'interrupted':
|
|
40
|
+
return 'interrupted';
|
|
41
|
+
default:
|
|
42
|
+
return 'failed';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function warn(message) {
|
|
46
|
+
process.stderr.write(`[atest] ${message}\n`);
|
|
47
|
+
}
|
|
48
|
+
export default class AtestReporter {
|
|
49
|
+
options;
|
|
50
|
+
collected = [];
|
|
51
|
+
workerTests = new Map();
|
|
52
|
+
config;
|
|
53
|
+
runId = '';
|
|
54
|
+
startedAt = '';
|
|
55
|
+
enabled = true;
|
|
56
|
+
constructor(options = {}) {
|
|
57
|
+
this.options = options;
|
|
58
|
+
}
|
|
59
|
+
/** We add a short footer only; other reporters own the main output. */
|
|
60
|
+
printsToStdio() {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
onBegin(config) {
|
|
64
|
+
this.config = config;
|
|
65
|
+
this.enabled = this.options.enabled ?? process.env['ATEST'] !== '0';
|
|
66
|
+
this.startedAt = new Date().toISOString();
|
|
67
|
+
const fromOptions = typeof this.options.runId === 'function' ? this.options.runId() : this.options.runId;
|
|
68
|
+
// Environment wins over config, deliberately. Every shard of a CI run must
|
|
69
|
+
// share one run id, and only the runtime knows it — a value baked into
|
|
70
|
+
// playwright.config.ts cannot vary per invocation, so config-wins would
|
|
71
|
+
// make sharded runs silently collapse into a single overwritten record.
|
|
72
|
+
this.runId =
|
|
73
|
+
process.env['ATEST_RUN_ID'] ??
|
|
74
|
+
fromOptions ??
|
|
75
|
+
`${this.startedAt.replace(/[:.]/g, '-')}-${process.pid}`;
|
|
76
|
+
}
|
|
77
|
+
onTestEnd(test, result) {
|
|
78
|
+
if (!this.enabled)
|
|
79
|
+
return;
|
|
80
|
+
// Cheap and synchronous by design — two references and a string push.
|
|
81
|
+
this.collected.push({ test, result });
|
|
82
|
+
const onWorker = this.workerTests.get(result.workerIndex) ?? [];
|
|
83
|
+
onWorker.push(test.id);
|
|
84
|
+
this.workerTests.set(result.workerIndex, onWorker);
|
|
85
|
+
}
|
|
86
|
+
async onEnd(_result) {
|
|
87
|
+
if (!this.enabled)
|
|
88
|
+
return;
|
|
89
|
+
try {
|
|
90
|
+
await this.flush();
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
// A reporting failure must never become a test failure.
|
|
94
|
+
warn(`failed to write evidence: ${error instanceof Error ? error.message : String(error)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async flush() {
|
|
98
|
+
const evidenceStore = new EvidenceStore({
|
|
99
|
+
dir: this.options.evidenceDir ?? DEFAULTS.evidenceDir,
|
|
100
|
+
redactKeys: this.options.redactKeys ?? DEFAULTS.redactKeys,
|
|
101
|
+
retainRuns: this.options.retainRuns ?? DEFAULTS.retainRuns,
|
|
102
|
+
});
|
|
103
|
+
const attempts = [];
|
|
104
|
+
let bundlesWritten = 0;
|
|
105
|
+
for (const { test, result } of this.collected) {
|
|
106
|
+
const outcome = toOutcome(result.status);
|
|
107
|
+
// Playwright names the implicit project '' when a config declares no
|
|
108
|
+
// projects. Empty string is a poor grouping key: it is part of the
|
|
109
|
+
// scoring key and the evidence id, and it reads as missing data in
|
|
110
|
+
// every report.
|
|
111
|
+
const projectName = test.parent.project()?.name;
|
|
112
|
+
const project = projectName === undefined || projectName === '' ? 'default' : projectName;
|
|
113
|
+
const traceId = this.options.traceId?.({ id: test.id }, result.retry) ?? null;
|
|
114
|
+
let failureKind = null;
|
|
115
|
+
let evidenceIdValue = null;
|
|
116
|
+
// Coverage is read for EVERY attempt, not only failures: what a passing
|
|
117
|
+
// test visited is precisely what future selection needs.
|
|
118
|
+
const coverage = await this.readCoverage(result);
|
|
119
|
+
if (isFailure(outcome)) {
|
|
120
|
+
try {
|
|
121
|
+
const sidecars = await this.readSidecars(result);
|
|
122
|
+
const bundle = assembleBundle({
|
|
123
|
+
test: {
|
|
124
|
+
id: test.id,
|
|
125
|
+
title: test.title,
|
|
126
|
+
titlePath: () => test.titlePath(),
|
|
127
|
+
location: { file: test.location.file, line: test.location.line },
|
|
128
|
+
tags: test.tags,
|
|
129
|
+
},
|
|
130
|
+
result: result,
|
|
131
|
+
sidecars,
|
|
132
|
+
context: {
|
|
133
|
+
runId: this.runId,
|
|
134
|
+
traceId,
|
|
135
|
+
project,
|
|
136
|
+
shard: this.config?.shard ?? null,
|
|
137
|
+
workers: this.config?.workers ?? 1,
|
|
138
|
+
appEnv: process.env['APP_ENV'] ?? 'unknown',
|
|
139
|
+
baseUrl: process.env['BASE_URL'] ?? '',
|
|
140
|
+
browser: project,
|
|
141
|
+
platform: process.platform,
|
|
142
|
+
commit: process.env['GITHUB_SHA'] ?? '',
|
|
143
|
+
changedPaths: [],
|
|
144
|
+
timeoutMs: test.timeout,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
await evidenceStore.write(bundle);
|
|
148
|
+
bundlesWritten += 1;
|
|
149
|
+
failureKind = bundle.failure.kind;
|
|
150
|
+
evidenceIdValue = bundle.id;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (error instanceof SidecarParseError) {
|
|
154
|
+
// Loud, named, and actionable — a drifted contract is a bug in the
|
|
155
|
+
// fixtures, not something to paper over with an empty bundle.
|
|
156
|
+
warn(error.message);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
warn(`could not build evidence for "${test.title}": ` +
|
|
160
|
+
`${error instanceof Error ? error.message : String(error)}`);
|
|
161
|
+
}
|
|
162
|
+
failureKind = classifyResult(result, EMPTY, test.timeout).kind;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
attempts.push({
|
|
166
|
+
testId: test.id,
|
|
167
|
+
title: test.title,
|
|
168
|
+
titlePath: test.titlePath(),
|
|
169
|
+
file: test.location.file,
|
|
170
|
+
line: test.location.line,
|
|
171
|
+
project,
|
|
172
|
+
tags: test.tags,
|
|
173
|
+
retry: result.retry,
|
|
174
|
+
outcome,
|
|
175
|
+
failureKind,
|
|
176
|
+
durationMs: result.duration,
|
|
177
|
+
workerIndex: result.workerIndex,
|
|
178
|
+
shard: this.config?.shard ?? null,
|
|
179
|
+
traceId,
|
|
180
|
+
evidenceId: evidenceIdValue,
|
|
181
|
+
coScheduled: (this.workerTests.get(result.workerIndex) ?? []).filter(id => id !== test.id),
|
|
182
|
+
routes: coverage,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const run = {
|
|
186
|
+
schemaVersion: RUN_SCHEMA_VERSION,
|
|
187
|
+
runId: this.runId,
|
|
188
|
+
startedAt: this.startedAt,
|
|
189
|
+
finishedAt: new Date().toISOString(),
|
|
190
|
+
commit: process.env['GITHUB_SHA'] ?? null,
|
|
191
|
+
branch: process.env['GITHUB_REF_NAME'] ?? null,
|
|
192
|
+
appEnv: process.env['APP_ENV'] ?? null,
|
|
193
|
+
ci: process.env['CI'] === 'true' || process.env['CI'] === '1',
|
|
194
|
+
workers: this.config?.workers ?? 1,
|
|
195
|
+
shard: this.config?.shard ?? null,
|
|
196
|
+
atestVersion: ATEST_VERSION,
|
|
197
|
+
playwrightVersion: this.config?.version ?? null,
|
|
198
|
+
attempts,
|
|
199
|
+
};
|
|
200
|
+
const runPath = join(this.options.runsDir ?? DEFAULTS.runsDir, runFileName(this.runId, run.shard, attempts.map(a => a.project)));
|
|
201
|
+
await mkdir(dirname(runPath), { recursive: true });
|
|
202
|
+
await writeFile(runPath, `${JSON.stringify(run, null, 2)}\n`, 'utf8');
|
|
203
|
+
await evidenceStore.prune();
|
|
204
|
+
const failures = attempts.filter(a => isFailure(a.outcome)).length;
|
|
205
|
+
if (failures > 0) {
|
|
206
|
+
warn(`${bundlesWritten}/${failures} failures captured → ${runPath}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async readAttachment(result, name) {
|
|
210
|
+
const attachment = result.attachments.find(a => a.name === name);
|
|
211
|
+
if (attachment === undefined)
|
|
212
|
+
return undefined;
|
|
213
|
+
if (attachment.body !== undefined)
|
|
214
|
+
return attachment.body.toString('utf8');
|
|
215
|
+
if (attachment.path !== undefined)
|
|
216
|
+
return readFile(attachment.path, 'utf8').catch(() => undefined);
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
async readCoverage(result) {
|
|
220
|
+
try {
|
|
221
|
+
const parsed = parseSidecar(SIDECAR.coverage, await this.readAttachment(result, SIDECAR.coverage));
|
|
222
|
+
return parsed?.routes ?? [];
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
// A drifted coverage sidecar must not cost us the attempt record.
|
|
226
|
+
warn(error instanceof Error ? error.message : String(error));
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async readSidecars(result) {
|
|
231
|
+
const read = (name) => this.readAttachment(result, name);
|
|
232
|
+
return {
|
|
233
|
+
page: parseSidecar(SIDECAR.page, await read(SIDECAR.page)),
|
|
234
|
+
network: parseSidecar(SIDECAR.network, await read(SIDECAR.network)),
|
|
235
|
+
console: parseSidecar(SIDECAR.console, await read(SIDECAR.console)),
|
|
236
|
+
intent: parseSidecar(SIDECAR.intent, await read(SIDECAR.intent)),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const EMPTY = { page: null, network: null, console: null, intent: null };
|
|
241
|
+
/**
|
|
242
|
+
* One file per INVOCATION, not per run.
|
|
243
|
+
*
|
|
244
|
+
* Naming the file `<runId>.json` looks right until the suite is sharded, and
|
|
245
|
+
* then it silently destroys almost all of the data. Every shard shares one run
|
|
246
|
+
* id on purpose — that is how they get merged — so every shard wrote the same
|
|
247
|
+
* filename. Measured on a three-shard run: shards 1 and 2 recorded their
|
|
248
|
+
* attempts, shard 3 had no tests to run, and its empty record overwrote both.
|
|
249
|
+
* The surviving file reported `attempts: 0` for a run that passed four tests.
|
|
250
|
+
*
|
|
251
|
+
* It survives the upload too. CI downloads shard artifacts with
|
|
252
|
+
* `merge-multiple: true`, which flattens them into one directory — identical
|
|
253
|
+
* names collide there as well, so a 6-shard × 9-project matrix would keep one
|
|
254
|
+
* file in fifty-four.
|
|
255
|
+
*
|
|
256
|
+
* Shard and project both go in the name because their matrix varies both, and
|
|
257
|
+
* two jobs differing only by project would otherwise still collide.
|
|
258
|
+
*/
|
|
259
|
+
export function runFileName(runId, shard, projects) {
|
|
260
|
+
const parts = [runId];
|
|
261
|
+
if (shard !== null)
|
|
262
|
+
parts.push(`shard-${shard.current}of${shard.total}`);
|
|
263
|
+
const distinct = [...new Set(projects)].sort();
|
|
264
|
+
if (distinct.length === 1 && distinct[0] !== undefined)
|
|
265
|
+
parts.push(distinct[0]);
|
|
266
|
+
else if (distinct.length > 1)
|
|
267
|
+
parts.push(shortHash(distinct.join(',')));
|
|
268
|
+
return `${parts.join('-').replace(/[^A-Za-z0-9._-]/g, '_')}.json`;
|
|
269
|
+
}
|
|
270
|
+
/** djb2, base36. Deterministic — re-ingesting the same file must be idempotent. */
|
|
271
|
+
function shortHash(input) {
|
|
272
|
+
let hash = 5381;
|
|
273
|
+
for (let i = 0; i < input.length; i += 1)
|
|
274
|
+
hash = ((hash * 33) ^ input.charCodeAt(i)) >>> 0;
|
|
275
|
+
return hash.toString(36).padStart(7, '0').slice(0, 7);
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fixture → reporter channel.
|
|
3
|
+
*
|
|
4
|
+
* A Playwright reporter runs in the main process and cannot reach into the
|
|
5
|
+
* browser. Anything only the worker knows — the ARIA snapshot, the request
|
|
6
|
+
* ledger, console output — has to travel as a test ATTACHMENT, which is the
|
|
7
|
+
* runner's own supported channel and needs no side files or IPC.
|
|
8
|
+
*
|
|
9
|
+
* Both ends import these schemas, and the reporter PARSES rather than casts.
|
|
10
|
+
* A fixture that drifts from the contract fails loudly with a named error
|
|
11
|
+
* instead of silently producing a bundle with an empty ARIA snapshot — the
|
|
12
|
+
* same reasoning behind validating mocked response bodies against wire
|
|
13
|
+
* schemas rather than trusting them.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
export declare const SIDECAR: {
|
|
17
|
+
readonly page: "atest:page";
|
|
18
|
+
readonly network: "atest:network";
|
|
19
|
+
readonly console: "atest:console";
|
|
20
|
+
readonly intent: "atest:intent";
|
|
21
|
+
readonly coverage: "atest:coverage";
|
|
22
|
+
};
|
|
23
|
+
export type SidecarName = (typeof SIDECAR)[keyof typeof SIDECAR];
|
|
24
|
+
export declare const PageSidecarSchema: z.ZodObject<{
|
|
25
|
+
url: z.ZodString;
|
|
26
|
+
title: z.ZodString;
|
|
27
|
+
ariaSnapshot: z.ZodString;
|
|
28
|
+
testIdsPresent: z.ZodArray<z.ZodString>;
|
|
29
|
+
htmlDigest: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
export type PageSidecar = z.infer<typeof PageSidecarSchema>;
|
|
32
|
+
export declare const NetworkSidecarSchema: z.ZodObject<{
|
|
33
|
+
failed: z.ZodArray<z.ZodObject<{
|
|
34
|
+
url: z.ZodString;
|
|
35
|
+
method: z.ZodString;
|
|
36
|
+
status: z.ZodNullable<z.ZodNumber>;
|
|
37
|
+
durationMs: z.ZodNumber;
|
|
38
|
+
failureText: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
39
|
+
schemaError: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
40
|
+
}, z.core.$strip>>;
|
|
41
|
+
slow: z.ZodArray<z.ZodObject<{
|
|
42
|
+
url: z.ZodString;
|
|
43
|
+
method: z.ZodString;
|
|
44
|
+
status: z.ZodNullable<z.ZodNumber>;
|
|
45
|
+
durationMs: z.ZodNumber;
|
|
46
|
+
failureText: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
47
|
+
schemaError: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
48
|
+
}, z.core.$strip>>;
|
|
49
|
+
statusCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
50
|
+
}, z.core.$strip>;
|
|
51
|
+
export type NetworkSidecar = z.infer<typeof NetworkSidecarSchema>;
|
|
52
|
+
export declare const ConsoleSidecarSchema: z.ZodObject<{
|
|
53
|
+
errors: z.ZodArray<z.ZodString>;
|
|
54
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
55
|
+
}, z.core.$strip>;
|
|
56
|
+
export type ConsoleSidecar = z.infer<typeof ConsoleSidecarSchema>;
|
|
57
|
+
export declare const IntentSidecarSchema: z.ZodObject<{
|
|
58
|
+
selector: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
59
|
+
selectorSource: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
60
|
+
file: z.ZodString;
|
|
61
|
+
line: z.ZodNumber;
|
|
62
|
+
constantPath: z.ZodString;
|
|
63
|
+
aliases: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
64
|
+
}, z.core.$strip>>>;
|
|
65
|
+
}, z.core.$strip>;
|
|
66
|
+
export type IntentSidecar = z.infer<typeof IntentSidecarSchema>;
|
|
67
|
+
/**
|
|
68
|
+
* Routes a test actually visited.
|
|
69
|
+
*
|
|
70
|
+
* The one signal that survives a fixture barrel. Static imports say every spec
|
|
71
|
+
* depends on every feature (they compose one `test` object); what a test
|
|
72
|
+
* VISITED is independent of that, and is the only thing that can narrow
|
|
73
|
+
* selection in a suite built that way.
|
|
74
|
+
*/
|
|
75
|
+
export declare const CoverageSidecarSchema: z.ZodObject<{
|
|
76
|
+
routes: z.ZodArray<z.ZodString>;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
export type CoverageSidecar = z.infer<typeof CoverageSidecarSchema>;
|
|
79
|
+
declare const SCHEMAS: {
|
|
80
|
+
readonly "atest:page": z.ZodObject<{
|
|
81
|
+
url: z.ZodString;
|
|
82
|
+
title: z.ZodString;
|
|
83
|
+
ariaSnapshot: z.ZodString;
|
|
84
|
+
testIdsPresent: z.ZodArray<z.ZodString>;
|
|
85
|
+
htmlDigest: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
86
|
+
}, z.core.$strip>;
|
|
87
|
+
readonly "atest:network": z.ZodObject<{
|
|
88
|
+
failed: z.ZodArray<z.ZodObject<{
|
|
89
|
+
url: z.ZodString;
|
|
90
|
+
method: z.ZodString;
|
|
91
|
+
status: z.ZodNullable<z.ZodNumber>;
|
|
92
|
+
durationMs: z.ZodNumber;
|
|
93
|
+
failureText: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
94
|
+
schemaError: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
95
|
+
}, z.core.$strip>>;
|
|
96
|
+
slow: z.ZodArray<z.ZodObject<{
|
|
97
|
+
url: z.ZodString;
|
|
98
|
+
method: z.ZodString;
|
|
99
|
+
status: z.ZodNullable<z.ZodNumber>;
|
|
100
|
+
durationMs: z.ZodNumber;
|
|
101
|
+
failureText: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
102
|
+
schemaError: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
103
|
+
}, z.core.$strip>>;
|
|
104
|
+
statusCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
105
|
+
}, z.core.$strip>;
|
|
106
|
+
readonly "atest:console": z.ZodObject<{
|
|
107
|
+
errors: z.ZodArray<z.ZodString>;
|
|
108
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
109
|
+
}, z.core.$strip>;
|
|
110
|
+
readonly "atest:intent": z.ZodObject<{
|
|
111
|
+
selector: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
112
|
+
selectorSource: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
113
|
+
file: z.ZodString;
|
|
114
|
+
line: z.ZodNumber;
|
|
115
|
+
constantPath: z.ZodString;
|
|
116
|
+
aliases: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
117
|
+
}, z.core.$strip>>>;
|
|
118
|
+
}, z.core.$strip>;
|
|
119
|
+
readonly "atest:coverage": z.ZodObject<{
|
|
120
|
+
routes: z.ZodArray<z.ZodString>;
|
|
121
|
+
}, z.core.$strip>;
|
|
122
|
+
};
|
|
123
|
+
export declare class SidecarParseError extends Error {
|
|
124
|
+
readonly sidecar: string;
|
|
125
|
+
readonly issues: string;
|
|
126
|
+
constructor(sidecar: string, issues: string);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Parse a sidecar payload. Absent is fine (the fixtures are optional);
|
|
130
|
+
* PRESENT-BUT-WRONG is not, and throws.
|
|
131
|
+
*/
|
|
132
|
+
export declare function parseSidecar<N extends keyof typeof SCHEMAS>(name: N, raw: string | undefined): z.infer<(typeof SCHEMAS)[N]> | null;
|
|
133
|
+
export {};
|
|
134
|
+
//# sourceMappingURL=sidecar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidecar.d.ts","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,OAAO;;;;;;CAMV,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;AAEjE,eAAO,MAAM,iBAAiB;;;;;;iBAM5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAW5D,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;iBAI/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,oBAAoB;;;iBAG/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,mBAAmB;;;;;;;;iBAW9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB;;iBAEhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,QAAA,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAMH,CAAC;AAEX,qBAAa,iBAAkB,SAAQ,KAAK;IAExC,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM;gBADd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM;CAQ1B;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,OAAO,OAAO,EACzD,IAAI,EAAE,CAAC,EACP,GAAG,EAAE,MAAM,GAAG,SAAS,GACtB,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAkBrC"}
|