@aplaytest/core 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/config/schema.d.ts +247 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +279 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/evidence/id.d.ts +17 -0
- package/dist/evidence/id.d.ts.map +1 -0
- package/dist/evidence/id.js +20 -0
- package/dist/evidence/id.js.map +1 -0
- package/dist/evidence/redact.d.ts +22 -0
- package/dist/evidence/redact.d.ts.map +1 -0
- package/dist/evidence/redact.js +97 -0
- package/dist/evidence/redact.js.map +1 -0
- package/dist/evidence/store.d.ts +51 -0
- package/dist/evidence/store.d.ts.map +1 -0
- package/dist/evidence/store.js +148 -0
- package/dist/evidence/store.js.map +1 -0
- package/dist/evidence/types.d.ts +183 -0
- package/dist/evidence/types.d.ts.map +1 -0
- package/dist/evidence/types.js +21 -0
- package/dist/evidence/types.js.map +1 -0
- package/dist/history/ingest-playwright.d.ts +26 -0
- package/dist/history/ingest-playwright.d.ts.map +1 -0
- package/dist/history/ingest-playwright.js +73 -0
- package/dist/history/ingest-playwright.js.map +1 -0
- package/dist/history/ingest.d.ts +29 -0
- package/dist/history/ingest.d.ts.map +1 -0
- package/dist/history/ingest.js +86 -0
- package/dist/history/ingest.js.map +1 -0
- package/dist/history/memory-index.d.ts +88 -0
- package/dist/history/memory-index.d.ts.map +1 -0
- package/dist/history/memory-index.js +236 -0
- package/dist/history/memory-index.js.map +1 -0
- package/dist/history/playwright-report.d.ts +102 -0
- package/dist/history/playwright-report.d.ts.map +1 -0
- package/dist/history/playwright-report.js +274 -0
- package/dist/history/playwright-report.js.map +1 -0
- package/dist/history/store.d.ts +73 -0
- package/dist/history/store.d.ts.map +1 -0
- package/dist/history/store.js +243 -0
- package/dist/history/store.js.map +1 -0
- package/dist/history/types.d.ts +69 -0
- package/dist/history/types.d.ts.map +1 -0
- package/dist/history/types.js +18 -0
- package/dist/history/types.js.map +1 -0
- package/dist/history/url.d.ts +79 -0
- package/dist/history/url.d.ts.map +1 -0
- package/dist/history/url.js +121 -0
- package/dist/history/url.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/locator/stability.d.ts +63 -0
- package/dist/locator/stability.d.ts.map +1 -0
- package/dist/locator/stability.js +127 -0
- package/dist/locator/stability.js.map +1 -0
- package/dist/taxonomy/classify.d.ts +63 -0
- package/dist/taxonomy/classify.d.ts.map +1 -0
- package/dist/taxonomy/classify.js +344 -0
- package/dist/taxonomy/classify.js.map +1 -0
- package/dist/taxonomy/kinds.d.ts +49 -0
- package/dist/taxonomy/kinds.d.ts.map +1 -0
- package/dist/taxonomy/kinds.js +139 -0
- package/dist/taxonomy/kinds.js.map +1 -0
- package/dist/taxonomy/parse-error.d.ts +85 -0
- package/dist/taxonomy/parse-error.d.ts.map +1 -0
- package/dist/taxonomy/parse-error.js +179 -0
- package/dist/taxonomy/parse-error.js.map +1 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/dist-cjs/.tsbuildinfo +1 -0
- package/dist-cjs/config/schema.js +283 -0
- package/dist-cjs/config/schema.js.map +1 -0
- package/dist-cjs/evidence/id.js +24 -0
- package/dist-cjs/evidence/id.js.map +1 -0
- package/dist-cjs/evidence/redact.js +103 -0
- package/dist-cjs/evidence/redact.js.map +1 -0
- package/dist-cjs/evidence/store.js +156 -0
- package/dist-cjs/evidence/store.js.map +1 -0
- package/dist-cjs/evidence/types.js +25 -0
- package/dist-cjs/evidence/types.js.map +1 -0
- package/dist-cjs/history/ingest-playwright.js +76 -0
- package/dist-cjs/history/ingest-playwright.js.map +1 -0
- package/dist-cjs/history/ingest.js +89 -0
- package/dist-cjs/history/ingest.js.map +1 -0
- package/dist-cjs/history/memory-index.js +242 -0
- package/dist-cjs/history/memory-index.js.map +1 -0
- package/dist-cjs/history/playwright-report.js +277 -0
- package/dist-cjs/history/playwright-report.js.map +1 -0
- package/dist-cjs/history/store.js +247 -0
- package/dist-cjs/history/store.js.map +1 -0
- package/dist-cjs/history/types.js +23 -0
- package/dist-cjs/history/types.js.map +1 -0
- package/dist-cjs/history/url.js +127 -0
- package/dist-cjs/history/url.js.map +1 -0
- package/dist-cjs/index.js +88 -0
- package/dist-cjs/index.js.map +1 -0
- package/dist-cjs/locator/stability.js +134 -0
- package/dist-cjs/locator/stability.js.map +1 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/taxonomy/classify.js +348 -0
- package/dist-cjs/taxonomy/classify.js.map +1 -0
- package/dist-cjs/taxonomy/kinds.js +145 -0
- package/dist-cjs/taxonomy/kinds.js.map +1 -0
- package/dist-cjs/taxonomy/parse-error.js +186 -0
- package/dist-cjs/taxonomy/parse-error.js.map +1 -0
- package/dist-cjs/version.js +13 -0
- package/dist-cjs/version.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run and attempt records — the unit of test history.
|
|
3
|
+
*
|
|
4
|
+
* The reporter writes these as JSON; the history store ingests them
|
|
5
|
+
* into SQLite. Keeping the reporter's output a plain file rather than a
|
|
6
|
+
* database write matters: the reporter runs INSIDE the test process, where a
|
|
7
|
+
* native SQLite binding, a migration, or a lock contention bug would become a
|
|
8
|
+
* test failure. The run path stays dependency-light on purpose.
|
|
9
|
+
*/
|
|
10
|
+
import type { FailureKind } from '../taxonomy/kinds.js';
|
|
11
|
+
import type { EvidenceId } from '../evidence/types.js';
|
|
12
|
+
export type Outcome = 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';
|
|
13
|
+
export interface AttemptRecord {
|
|
14
|
+
/** Playwright's stable test id — survives a title rename. */
|
|
15
|
+
readonly testId: string;
|
|
16
|
+
readonly title: string;
|
|
17
|
+
readonly titlePath: readonly string[];
|
|
18
|
+
readonly file: string;
|
|
19
|
+
readonly line: number;
|
|
20
|
+
readonly project: string;
|
|
21
|
+
readonly tags: readonly string[];
|
|
22
|
+
readonly retry: number;
|
|
23
|
+
readonly outcome: Outcome;
|
|
24
|
+
readonly failureKind: FailureKind | null;
|
|
25
|
+
readonly durationMs: number;
|
|
26
|
+
readonly workerIndex: number;
|
|
27
|
+
readonly shard: {
|
|
28
|
+
readonly current: number;
|
|
29
|
+
readonly total: number;
|
|
30
|
+
} | null;
|
|
31
|
+
/** Joins to OpenTelemetry. Null when the consumer supplies no identity hook. */
|
|
32
|
+
readonly traceId: string | null;
|
|
33
|
+
readonly evidenceId: EvidenceId | null;
|
|
34
|
+
/**
|
|
35
|
+
* Other tests that ran on the same worker. Powers the co-scheduling lift
|
|
36
|
+
* signal used to distinguish test pollution from ordinary flakiness.
|
|
37
|
+
*/
|
|
38
|
+
readonly coScheduled: readonly string[];
|
|
39
|
+
/**
|
|
40
|
+
* Routes this test actually visited.
|
|
41
|
+
*
|
|
42
|
+
* Independent of the import graph, and the only signal that can narrow
|
|
43
|
+
* selection in a suite whose specs all import one composed fixture.
|
|
44
|
+
*/
|
|
45
|
+
readonly routes: readonly string[];
|
|
46
|
+
}
|
|
47
|
+
export interface RunRecord {
|
|
48
|
+
readonly schemaVersion: 1;
|
|
49
|
+
readonly runId: string;
|
|
50
|
+
readonly startedAt: string;
|
|
51
|
+
readonly finishedAt: string | null;
|
|
52
|
+
readonly commit: string | null;
|
|
53
|
+
readonly branch: string | null;
|
|
54
|
+
readonly appEnv: string | null;
|
|
55
|
+
readonly ci: boolean;
|
|
56
|
+
readonly workers: number;
|
|
57
|
+
readonly shard: {
|
|
58
|
+
readonly current: number;
|
|
59
|
+
readonly total: number;
|
|
60
|
+
} | null;
|
|
61
|
+
readonly atestVersion: string;
|
|
62
|
+
readonly playwrightVersion: string | null;
|
|
63
|
+
readonly attempts: readonly AttemptRecord[];
|
|
64
|
+
}
|
|
65
|
+
export declare const RUN_SCHEMA_VERSION: 1;
|
|
66
|
+
export declare function isFailure(outcome: Outcome): boolean;
|
|
67
|
+
/** Skipped and interrupted attempts are not evidence about a test's behaviour. */
|
|
68
|
+
export declare function isConclusive(outcome: Outcome): boolean;
|
|
69
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/history/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,aAAa,CAAC;AAEnF,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5E,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;CAC7C;AAED,eAAO,MAAM,kBAAkB,EAAG,CAAU,CAAC;AAE7C,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEnD;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEtD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run and attempt records — the unit of test history.
|
|
3
|
+
*
|
|
4
|
+
* The reporter writes these as JSON; the history store ingests them
|
|
5
|
+
* into SQLite. Keeping the reporter's output a plain file rather than a
|
|
6
|
+
* database write matters: the reporter runs INSIDE the test process, where a
|
|
7
|
+
* native SQLite binding, a migration, or a lock contention bug would become a
|
|
8
|
+
* test failure. The run path stays dependency-light on purpose.
|
|
9
|
+
*/
|
|
10
|
+
export const RUN_SCHEMA_VERSION = 1;
|
|
11
|
+
export function isFailure(outcome) {
|
|
12
|
+
return outcome === 'failed' || outcome === 'timedOut';
|
|
13
|
+
}
|
|
14
|
+
/** Skipped and interrupted attempts are not evidence about a test's behaviour. */
|
|
15
|
+
export function isConclusive(outcome) {
|
|
16
|
+
return outcome === 'passed' || outcome === 'failed' || outcome === 'timedOut';
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/history/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuDH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAU,CAAC;AAE7C,MAAM,UAAU,SAAS,CAAC,OAAgB;IACxC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,UAAU,CAAC;AACxD,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,UAAU,CAAC;AAChF,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where history lives, expressed as one string.
|
|
3
|
+
*
|
|
4
|
+
* `--db` was already the only knob naming the store, and every consumer — the
|
|
5
|
+
* CLI, the generated workflows, `atest.config.ts` — passes it around as an
|
|
6
|
+
* opaque path. Making it a URL rather than adding `--history-account`,
|
|
7
|
+
* `--history-container` and `--history-prefix` means the blob backend needs no
|
|
8
|
+
* new flag anywhere, and a repo switches from a local file to Azure by
|
|
9
|
+
* changing one repository variable.
|
|
10
|
+
*
|
|
11
|
+
* Parsing lives in core because it is pure string work that the CLI, the
|
|
12
|
+
* Azure driver and `doctor` all need, and because a typo in a container name
|
|
13
|
+
* should fail with a readable message before any credential is acquired. The
|
|
14
|
+
* network code that acts on the result lives in `@aplaytest/store-azure`.
|
|
15
|
+
*/
|
|
16
|
+
export interface AzureBlobTarget {
|
|
17
|
+
readonly kind: 'azure-blob';
|
|
18
|
+
/** Base URL for `BlobServiceClient` — origin plus, on the emulator, the account. */
|
|
19
|
+
readonly serviceUrl: string;
|
|
20
|
+
readonly account: string;
|
|
21
|
+
readonly container: string;
|
|
22
|
+
/** Key prefix inside the container. Empty, or normalised to end with `/`. */
|
|
23
|
+
readonly prefix: string;
|
|
24
|
+
/**
|
|
25
|
+
* Days of history to read. Bounds the download: a driver that fetches every
|
|
26
|
+
* record ever written gets slower every week until someone disables it.
|
|
27
|
+
*/
|
|
28
|
+
readonly windowDays: number | null;
|
|
29
|
+
/**
|
|
30
|
+
* Score against the store; never write to it. `?readonly=1`.
|
|
31
|
+
*
|
|
32
|
+
* This is the pull-request configuration, and it is a first-class mode
|
|
33
|
+
* rather than an accident of RBAC. Azure already refuses the write — the PR
|
|
34
|
+
* identity holds Reader — but "refused" arrives as a 403 per shard file
|
|
35
|
+
* after four retries each, which turns a correct policy into a slow job and
|
|
36
|
+
* a log full of red herrings. Saying it up front also documents the intent
|
|
37
|
+
* where somebody reading the workflow will find it: a flake baseline
|
|
38
|
+
* describes trunk, and a branch whose test code may itself be broken must
|
|
39
|
+
* not enter it before anyone has decided to merge.
|
|
40
|
+
*/
|
|
41
|
+
readonly readOnly: boolean;
|
|
42
|
+
}
|
|
43
|
+
export type HistoryTarget = {
|
|
44
|
+
readonly kind: 'memory';
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: 'sqlite';
|
|
47
|
+
readonly path: string;
|
|
48
|
+
} | AzureBlobTarget;
|
|
49
|
+
export interface ParseHistoryUrlOptions {
|
|
50
|
+
/**
|
|
51
|
+
* DNS suffix for the `azblob://` shorthand. Sovereign clouds differ
|
|
52
|
+
* (`blob.core.usgovcloudapi.net`, `blob.core.chinacloudapi.cn`), and hard
|
|
53
|
+
* coding the commercial one would silently resolve to a host that does not
|
|
54
|
+
* exist rather than failing with a name anyone can act on.
|
|
55
|
+
*/
|
|
56
|
+
readonly endpointSuffix?: string | undefined;
|
|
57
|
+
}
|
|
58
|
+
export declare const DEFAULT_BLOB_ENDPOINT_SUFFIX = "blob.core.windows.net";
|
|
59
|
+
export declare class HistoryUrlError extends Error {
|
|
60
|
+
constructor(message: string);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Accepted forms:
|
|
64
|
+
*
|
|
65
|
+
* :memory: throwaway (not the config default)
|
|
66
|
+
* .atest/history.sqlite a local file
|
|
67
|
+
* azblob://<account>/<container>[/<prefix>] Azure, via the DNS suffix
|
|
68
|
+
* https://<account>.blob.core.windows.net/<c>/<p> Azure, fully qualified
|
|
69
|
+
* http://127.0.0.1:10000/<account>/<c>/<p> Azurite, account in path
|
|
70
|
+
*
|
|
71
|
+
* Anything unrecognised is a FILE PATH, not an error. `--db` has always taken
|
|
72
|
+
* one, and a Windows path like `C:\atest\history.sqlite` parses as a URL with
|
|
73
|
+
* scheme `c:` — rejecting unknown schemes would break the case the flag was
|
|
74
|
+
* built for.
|
|
75
|
+
*/
|
|
76
|
+
export declare function parseHistoryUrl(url: string, options?: ParseHistoryUrlOptions): HistoryTarget;
|
|
77
|
+
/** Short, log-safe description. Used by `doctor` and every "what am I reading?" line. */
|
|
78
|
+
export declare function describeHistoryTarget(target: HistoryTarget): string;
|
|
79
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/history/url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,oFAAoF;IACpF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClD,eAAe,CAAC;AAEpB,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9C;AAED,eAAO,MAAM,4BAA4B,0BAA0B,CAAC;AAMpE,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAuDD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B,GAAG,aAAa,CAoChG;AAED,yFAAyF;AACzF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAYnE"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where history lives, expressed as one string.
|
|
3
|
+
*
|
|
4
|
+
* `--db` was already the only knob naming the store, and every consumer — the
|
|
5
|
+
* CLI, the generated workflows, `atest.config.ts` — passes it around as an
|
|
6
|
+
* opaque path. Making it a URL rather than adding `--history-account`,
|
|
7
|
+
* `--history-container` and `--history-prefix` means the blob backend needs no
|
|
8
|
+
* new flag anywhere, and a repo switches from a local file to Azure by
|
|
9
|
+
* changing one repository variable.
|
|
10
|
+
*
|
|
11
|
+
* Parsing lives in core because it is pure string work that the CLI, the
|
|
12
|
+
* Azure driver and `doctor` all need, and because a typo in a container name
|
|
13
|
+
* should fail with a readable message before any credential is acquired. The
|
|
14
|
+
* network code that acts on the result lives in `@aplaytest/store-azure`.
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_BLOB_ENDPOINT_SUFFIX = 'blob.core.windows.net';
|
|
17
|
+
/** Azure's rule, enforced here so the failure names the field. */
|
|
18
|
+
const CONTAINER_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
19
|
+
const ACCOUNT_NAME = /^[a-z0-9]{3,24}$/;
|
|
20
|
+
export class HistoryUrlError extends Error {
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = 'HistoryUrlError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function parseWindowDays(params) {
|
|
27
|
+
const raw = params.get('window');
|
|
28
|
+
if (raw === null)
|
|
29
|
+
return null;
|
|
30
|
+
const days = Number.parseInt(raw, 10);
|
|
31
|
+
if (!Number.isInteger(days) || days < 1) {
|
|
32
|
+
throw new HistoryUrlError(`?window= must be a positive integer number of days, got "${raw}".`);
|
|
33
|
+
}
|
|
34
|
+
return days;
|
|
35
|
+
}
|
|
36
|
+
function normalisePrefix(segments) {
|
|
37
|
+
const joined = segments.filter(s => s !== '').join('/');
|
|
38
|
+
return joined === '' ? '' : `${joined}/`;
|
|
39
|
+
}
|
|
40
|
+
function azureTarget(url, serviceUrl, account, segments) {
|
|
41
|
+
const container = segments[0];
|
|
42
|
+
if (container === undefined || container === '') {
|
|
43
|
+
throw new HistoryUrlError(`"${url.href}" names no container. Expected azblob://<account>/<container>[/<prefix>].`);
|
|
44
|
+
}
|
|
45
|
+
if (container.length < 3 || container.length > 63 || !CONTAINER_NAME.test(container)) {
|
|
46
|
+
throw new HistoryUrlError(`"${container}" is not a valid container name — 3-63 chars, lowercase letters, digits and single dashes.`);
|
|
47
|
+
}
|
|
48
|
+
if (!ACCOUNT_NAME.test(account)) {
|
|
49
|
+
throw new HistoryUrlError(`"${account}" is not a valid storage account name — 3-24 lowercase alphanumerics.`);
|
|
50
|
+
}
|
|
51
|
+
const readOnly = url.searchParams.get('readonly');
|
|
52
|
+
return {
|
|
53
|
+
kind: 'azure-blob',
|
|
54
|
+
serviceUrl,
|
|
55
|
+
account,
|
|
56
|
+
container,
|
|
57
|
+
prefix: normalisePrefix(segments.slice(1)),
|
|
58
|
+
windowDays: parseWindowDays(url.searchParams),
|
|
59
|
+
// Present-but-empty (`?readonly`) counts as on, because that is what
|
|
60
|
+
// somebody typing it means. Only an explicit falsy value turns it off.
|
|
61
|
+
readOnly: readOnly !== null && readOnly !== '0' && readOnly !== 'false',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Accepted forms:
|
|
66
|
+
*
|
|
67
|
+
* :memory: throwaway (not the config default)
|
|
68
|
+
* .atest/history.sqlite a local file
|
|
69
|
+
* azblob://<account>/<container>[/<prefix>] Azure, via the DNS suffix
|
|
70
|
+
* https://<account>.blob.core.windows.net/<c>/<p> Azure, fully qualified
|
|
71
|
+
* http://127.0.0.1:10000/<account>/<c>/<p> Azurite, account in path
|
|
72
|
+
*
|
|
73
|
+
* Anything unrecognised is a FILE PATH, not an error. `--db` has always taken
|
|
74
|
+
* one, and a Windows path like `C:\atest\history.sqlite` parses as a URL with
|
|
75
|
+
* scheme `c:` — rejecting unknown schemes would break the case the flag was
|
|
76
|
+
* built for.
|
|
77
|
+
*/
|
|
78
|
+
export function parseHistoryUrl(url, options = {}) {
|
|
79
|
+
const trimmed = url.trim();
|
|
80
|
+
if (trimmed === ':memory:' || trimmed === '')
|
|
81
|
+
return { kind: 'memory' };
|
|
82
|
+
if (trimmed.startsWith('azblob://')) {
|
|
83
|
+
const suffix = options.endpointSuffix ?? DEFAULT_BLOB_ENDPOINT_SUFFIX;
|
|
84
|
+
const parsed = new URL(trimmed);
|
|
85
|
+
// `azblob://acct/container` puts the account in the authority, so it is
|
|
86
|
+
// already lowercased and stripped of a path by the URL parser.
|
|
87
|
+
const account = parsed.hostname;
|
|
88
|
+
const segments = parsed.pathname.split('/').filter(s => s !== '');
|
|
89
|
+
return azureTarget(parsed, `https://${account}.${suffix}`, account, segments);
|
|
90
|
+
}
|
|
91
|
+
if (trimmed.startsWith('https://') || trimmed.startsWith('http://')) {
|
|
92
|
+
const parsed = new URL(trimmed);
|
|
93
|
+
const segments = parsed.pathname.split('/').filter(s => s !== '');
|
|
94
|
+
const subdomain = /^([a-z0-9]{3,24})\.blob\./.exec(parsed.hostname);
|
|
95
|
+
if (subdomain !== null && subdomain[1] !== undefined) {
|
|
96
|
+
return azureTarget(parsed, parsed.origin, subdomain[1], segments);
|
|
97
|
+
}
|
|
98
|
+
// Emulator convention: the account is the first path segment because
|
|
99
|
+
// Azurite serves every account from one host.
|
|
100
|
+
const account = segments[0];
|
|
101
|
+
if (account === undefined) {
|
|
102
|
+
throw new HistoryUrlError(`"${trimmed}" is not a blob endpoint. Expected <account>.${DEFAULT_BLOB_ENDPOINT_SUFFIX}/<container>, ` +
|
|
103
|
+
`or an emulator URL with the account as the first path segment.`);
|
|
104
|
+
}
|
|
105
|
+
return azureTarget(parsed, `${parsed.origin}/${account}`, account, segments.slice(1));
|
|
106
|
+
}
|
|
107
|
+
return { kind: 'sqlite', path: trimmed };
|
|
108
|
+
}
|
|
109
|
+
/** Short, log-safe description. Used by `doctor` and every "what am I reading?" line. */
|
|
110
|
+
export function describeHistoryTarget(target) {
|
|
111
|
+
switch (target.kind) {
|
|
112
|
+
case 'memory':
|
|
113
|
+
return 'in-memory (discarded when the process exits)';
|
|
114
|
+
case 'sqlite':
|
|
115
|
+
return `sqlite · ${target.path}`;
|
|
116
|
+
case 'azure-blob':
|
|
117
|
+
return (`azure blob · ${target.account}/${target.container}/${target.prefix}` +
|
|
118
|
+
(target.readOnly ? ' (read-only)' : ''));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/history/url.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aplaytest/core — types, failure taxonomy, locator ranking, and configuration.
|
|
3
|
+
*
|
|
4
|
+
* This package has no Playwright dependency, makes no network calls, and never
|
|
5
|
+
* touches a model. Everything here must be unit-testable in isolation; that
|
|
6
|
+
* constraint is what keeps the engines above it testable too.
|
|
7
|
+
*/
|
|
8
|
+
export { FAILURE_KINDS, NEVER_HEAL, ROUTING, healEligibility, isHealable, countsTowardFlakeStats, } from './taxonomy/kinds.js';
|
|
9
|
+
export type { FailureKind, HealEligibility, FlakeRelevance, KindRouting, } from './taxonomy/kinds.js';
|
|
10
|
+
export { classify, listRules } from './taxonomy/classify.js';
|
|
11
|
+
export type { Classification, Confidence } from './taxonomy/classify.js';
|
|
12
|
+
export { parsePlaywrightError, splitCallLog, stripAnsi, stripCodeFrame, joinErrors, } from './taxonomy/parse-error.js';
|
|
13
|
+
export type { ParsedError, ErrorLike } from './taxonomy/parse-error.js';
|
|
14
|
+
export { LOCATOR_STRATEGIES, STABILITY_RANK, MAX_STABILITY_RANK, parseLocator, stabilityRankOf, stabilityDelta, testIdDistance, } from './locator/stability.js';
|
|
15
|
+
export type { LocatorStrategy, ParsedLocator } from './locator/stability.js';
|
|
16
|
+
export { ATEST_VERSION } from './version.js';
|
|
17
|
+
export { EVIDENCE_SCHEMA_VERSION, formatFailingStep } from './evidence/types.js';
|
|
18
|
+
export { evidenceId, isEvidenceId } from './evidence/id.js';
|
|
19
|
+
export type { EvidenceIdParts } from './evidence/id.js';
|
|
20
|
+
export { redact, redactString, redactUrl, REDACTED } from './evidence/redact.js';
|
|
21
|
+
export { EvidenceStore, SchemaVersionError, loadRunBundles, parseEvidenceBundle, isEvidenceBundle, } from './evidence/store.js';
|
|
22
|
+
export type { EvidenceStoreOptions, LoadRunBundlesResult, SkippedBundle } from './evidence/store.js';
|
|
23
|
+
export type { EvidenceBundle, EvidenceId, ClassifiableFailure, LocatorCandidate, StepRecord, SelectorSource, RequestRecord, AppSpanRecord, Rect, } from './evidence/types.js';
|
|
24
|
+
export { RUN_SCHEMA_VERSION, isFailure, isConclusive } from './history/types.js';
|
|
25
|
+
export type { RunRecord, AttemptRecord, Outcome } from './history/types.js';
|
|
26
|
+
export { SqliteHistoryStore } from './history/store.js';
|
|
27
|
+
export type { HistoryStore, HistoricalAttempt, HistoryQuery, TestKey } from './history/store.js';
|
|
28
|
+
export { HistoryIndex, MemoryHistoryStore, shardKeyOf } from './history/memory-index.js';
|
|
29
|
+
export { parseHistoryUrl, describeHistoryTarget, HistoryUrlError, DEFAULT_BLOB_ENDPOINT_SUFFIX, } from './history/url.js';
|
|
30
|
+
export type { HistoryTarget, AzureBlobTarget, ParseHistoryUrlOptions, } from './history/url.js';
|
|
31
|
+
export { ingestDirectory } from './history/ingest.js';
|
|
32
|
+
export { ingestPlaywrightJson } from './history/ingest-playwright.js';
|
|
33
|
+
export type { IngestPlaywrightJsonOptions } from './history/ingest-playwright.js';
|
|
34
|
+
export { mapPlaywrightReport } from './history/playwright-report.js';
|
|
35
|
+
export type { JsonReport, JsonSuite, JsonSpec, JsonTest, JsonResult, MappedReport, MapReportOptions, RunIdentity, } from './history/playwright-report.js';
|
|
36
|
+
export type { IngestResult } from './history/ingest.js';
|
|
37
|
+
export { defineAtestConfig, AtestConfigSchema, ExecutionModeSchema, AggressivenessSchema, AGGRESSIVENESS_PRESETS, } from './config/schema.js';
|
|
38
|
+
export type { AtestConfig, AtestConfigInput, AtestUserConfig, ResolvedAtestConfig, AtestIdentity, ExecutionMode, Aggressiveness, } from './config/schema.js';
|
|
39
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aplaytest/core — types, failure taxonomy, locator ranking, and configuration.
|
|
3
|
+
*
|
|
4
|
+
* This package has no Playwright dependency, makes no network calls, and never
|
|
5
|
+
* touches a model. Everything here must be unit-testable in isolation; that
|
|
6
|
+
* constraint is what keeps the engines above it testable too.
|
|
7
|
+
*/
|
|
8
|
+
// Taxonomy
|
|
9
|
+
export { FAILURE_KINDS, NEVER_HEAL, ROUTING, healEligibility, isHealable, countsTowardFlakeStats, } from './taxonomy/kinds.js';
|
|
10
|
+
export { classify, listRules } from './taxonomy/classify.js';
|
|
11
|
+
export { parsePlaywrightError, splitCallLog, stripAnsi, stripCodeFrame, joinErrors, } from './taxonomy/parse-error.js';
|
|
12
|
+
// Locators
|
|
13
|
+
export { LOCATOR_STRATEGIES, STABILITY_RANK, MAX_STABILITY_RANK, parseLocator, stabilityRankOf, stabilityDelta, testIdDistance, } from './locator/stability.js';
|
|
14
|
+
// Identity
|
|
15
|
+
export { ATEST_VERSION } from './version.js';
|
|
16
|
+
// Evidence
|
|
17
|
+
export { EVIDENCE_SCHEMA_VERSION, formatFailingStep } from './evidence/types.js';
|
|
18
|
+
export { evidenceId, isEvidenceId } from './evidence/id.js';
|
|
19
|
+
export { redact, redactString, redactUrl, REDACTED } from './evidence/redact.js';
|
|
20
|
+
export { EvidenceStore, SchemaVersionError, loadRunBundles, parseEvidenceBundle, isEvidenceBundle, } from './evidence/store.js';
|
|
21
|
+
// History
|
|
22
|
+
export { RUN_SCHEMA_VERSION, isFailure, isConclusive } from './history/types.js';
|
|
23
|
+
export { SqliteHistoryStore } from './history/store.js';
|
|
24
|
+
export { HistoryIndex, MemoryHistoryStore, shardKeyOf } from './history/memory-index.js';
|
|
25
|
+
export { parseHistoryUrl, describeHistoryTarget, HistoryUrlError, DEFAULT_BLOB_ENDPOINT_SUFFIX, } from './history/url.js';
|
|
26
|
+
export { ingestDirectory } from './history/ingest.js';
|
|
27
|
+
export { ingestPlaywrightJson } from './history/ingest-playwright.js';
|
|
28
|
+
export { mapPlaywrightReport } from './history/playwright-report.js';
|
|
29
|
+
// Config
|
|
30
|
+
export { defineAtestConfig, AtestConfigSchema, ExecutionModeSchema, AggressivenessSchema, AGGRESSIVENESS_PRESETS, } from './config/schema.js';
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,WAAW;AACX,OAAO,EACL,aAAa,EACb,UAAU,EACV,OAAO,EACP,eAAe,EACf,UAAU,EACV,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAG7D,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,SAAS,EACT,cAAc,EACd,UAAU,GACX,MAAM,2BAA2B,CAAC;AAGnC,WAAW;AACX,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,cAAc,GACf,MAAM,wBAAwB,CAAC;AAGhC,WAAW;AACX,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,WAAW;AACX,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAc7B,UAAU;AACV,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,4BAA4B,GAC7B,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAEtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAarE,SAAS;AACT,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locator stability ranking and parsing.
|
|
3
|
+
*
|
|
4
|
+
* A heal that makes a test pass by moving to a WEAKER locator is not a win:
|
|
5
|
+
* replacing a test id with a text match makes the test green and makes it
|
|
6
|
+
* fragile, coupling it to copy that product can change at any time. The engine
|
|
7
|
+
* therefore treats a negative stability delta as a warning that forces human
|
|
8
|
+
* review, and refuses to generate anything below a configured floor.
|
|
9
|
+
*/
|
|
10
|
+
export declare const LOCATOR_STRATEGIES: readonly ["testid", "role", "label", "placeholder", "text", "css", "xpath"];
|
|
11
|
+
export type LocatorStrategy = (typeof LOCATOR_STRATEGIES)[number];
|
|
12
|
+
/** Lower is more durable. */
|
|
13
|
+
export declare const STABILITY_RANK: Readonly<Record<LocatorStrategy, number>>;
|
|
14
|
+
export declare const MAX_STABILITY_RANK = 6;
|
|
15
|
+
export interface ParsedLocator {
|
|
16
|
+
readonly strategy: LocatorStrategy;
|
|
17
|
+
/** The identifying value: a test id, an accessible name, a CSS selector. */
|
|
18
|
+
readonly value: string;
|
|
19
|
+
/** The original expression, verbatim. */
|
|
20
|
+
readonly raw: string;
|
|
21
|
+
/**
|
|
22
|
+
* Accessible name when the locator carries one (`getByRole('button', { name })`).
|
|
23
|
+
* Null for nameless roles — those are too vague to heal.
|
|
24
|
+
*/
|
|
25
|
+
readonly accessibleName: string | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Best-effort parse of a Playwright selector or locator expression.
|
|
29
|
+
* Returns null when nothing recognisable is present — callers must handle it
|
|
30
|
+
* rather than assuming a strategy.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseLocator(expression: string | null | undefined): ParsedLocator | null;
|
|
33
|
+
export declare function stabilityRankOf(strategy: LocatorStrategy): number;
|
|
34
|
+
/**
|
|
35
|
+
* Positive = the replacement is MORE stable. Negative = less stable, which
|
|
36
|
+
* requires human review regardless of aggressiveness setting.
|
|
37
|
+
*/
|
|
38
|
+
export declare function stabilityDelta(from: LocatorStrategy, to: LocatorStrategy): number;
|
|
39
|
+
/**
|
|
40
|
+
* Positional, prefix-weighted distance between two test ids.
|
|
41
|
+
*
|
|
42
|
+
* Test ids in a component-structured app read outside-in —
|
|
43
|
+
* `<feature>-<component>-<element>` — so the LEADING tokens identify where the
|
|
44
|
+
* element lives and the trailing token identifies what it is. Elements get
|
|
45
|
+
* renamed far more often than they move between features, so disagreement in
|
|
46
|
+
* the prefix is the stronger signal that these are different elements.
|
|
47
|
+
* Weights therefore decay from the front.
|
|
48
|
+
*
|
|
49
|
+
* gym-card-name → gym-card-title 0.17 same place, renamed ← the common heal
|
|
50
|
+
* gym-card-name → gym-card-county 0.17 same place, sibling field
|
|
51
|
+
* gym-card-name → event-card-name 0.50 different feature
|
|
52
|
+
* gym-card-name → checkout-submit 1.00 unrelated
|
|
53
|
+
*
|
|
54
|
+
* Note that a rename and a sibling field score identically. That is honest:
|
|
55
|
+
* nothing in the STRING distinguishes them. Separating the two is exactly the
|
|
56
|
+
* judgement the Tier-1 ranker exists to make, using the failing assertion's
|
|
57
|
+
* domain arguments — which is why Tier 0 hands over candidates rather than
|
|
58
|
+
* picking one.
|
|
59
|
+
*
|
|
60
|
+
* Returns 0 (identical) .. 1 (nothing in common).
|
|
61
|
+
*/
|
|
62
|
+
export declare function testIdDistance(a: string, b: string): number;
|
|
63
|
+
//# sourceMappingURL=stability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stability.d.ts","sourceRoot":"","sources":["../../src/locator/stability.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,kBAAkB,6EAQrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,6BAA6B;AAC7B,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAQpE,CAAC;AAEF,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAgBD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,aAAa,GAAG,IAAI,CA8BxF;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAEjE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,EAAE,eAAe,GAAG,MAAM,CAEjF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAmB3D"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locator stability ranking and parsing.
|
|
3
|
+
*
|
|
4
|
+
* A heal that makes a test pass by moving to a WEAKER locator is not a win:
|
|
5
|
+
* replacing a test id with a text match makes the test green and makes it
|
|
6
|
+
* fragile, coupling it to copy that product can change at any time. The engine
|
|
7
|
+
* therefore treats a negative stability delta as a warning that forces human
|
|
8
|
+
* review, and refuses to generate anything below a configured floor.
|
|
9
|
+
*/
|
|
10
|
+
export const LOCATOR_STRATEGIES = [
|
|
11
|
+
'testid',
|
|
12
|
+
'role',
|
|
13
|
+
'label',
|
|
14
|
+
'placeholder',
|
|
15
|
+
'text',
|
|
16
|
+
'css',
|
|
17
|
+
'xpath',
|
|
18
|
+
];
|
|
19
|
+
/** Lower is more durable. */
|
|
20
|
+
export const STABILITY_RANK = {
|
|
21
|
+
testid: 0,
|
|
22
|
+
role: 1,
|
|
23
|
+
label: 2,
|
|
24
|
+
placeholder: 3,
|
|
25
|
+
text: 4,
|
|
26
|
+
css: 5,
|
|
27
|
+
xpath: 6,
|
|
28
|
+
};
|
|
29
|
+
export const MAX_STABILITY_RANK = 6;
|
|
30
|
+
const ROLE_WITH_NAME = /getByRole\(\s*['"`]([^'"`]+)['"`]\s*,\s*\{[^}]*\bname:\s*['"`]([^'"`]+)['"`]/;
|
|
31
|
+
const ROLE_ONLY = /getByRole\(\s*['"`]([^'"`]+)['"`]/;
|
|
32
|
+
const PATTERNS = [
|
|
33
|
+
{ strategy: 'testid', re: /getByTestId\(\s*['"`]([^'"`]+)['"`]/ },
|
|
34
|
+
{ strategy: 'testid', re: /\[data-testid=['"]?([^\]'"]+)['"]?\]/ },
|
|
35
|
+
{ strategy: 'label', re: /getByLabel\(\s*['"`]([^'"`]+)['"`]/ },
|
|
36
|
+
{ strategy: 'placeholder', re: /getByPlaceholder\(\s*['"`]([^'"`]+)['"`]/ },
|
|
37
|
+
{ strategy: 'text', re: /getByText\(\s*['"`]([^'"`]+)['"`]/ },
|
|
38
|
+
{ strategy: 'text', re: /text=(.+)$/ },
|
|
39
|
+
{ strategy: 'xpath', re: /^\s*(?:xpath=)?(\/\/.+)$/ },
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Best-effort parse of a Playwright selector or locator expression.
|
|
43
|
+
* Returns null when nothing recognisable is present — callers must handle it
|
|
44
|
+
* rather than assuming a strategy.
|
|
45
|
+
*/
|
|
46
|
+
export function parseLocator(expression) {
|
|
47
|
+
if (expression === null || expression === undefined)
|
|
48
|
+
return null;
|
|
49
|
+
const raw = expression.trim();
|
|
50
|
+
if (raw === '')
|
|
51
|
+
return null;
|
|
52
|
+
const namedRole = ROLE_WITH_NAME.exec(raw);
|
|
53
|
+
const role = namedRole?.[1];
|
|
54
|
+
const roleName = namedRole?.[2];
|
|
55
|
+
if (role !== undefined && role !== '' && roleName !== undefined && roleName !== '') {
|
|
56
|
+
return { strategy: 'role', value: role, raw, accessibleName: roleName };
|
|
57
|
+
}
|
|
58
|
+
const bareRole = ROLE_ONLY.exec(raw);
|
|
59
|
+
const bare = bareRole?.[1];
|
|
60
|
+
if (bare !== undefined && bare !== '') {
|
|
61
|
+
return { strategy: 'role', value: bare, raw, accessibleName: null };
|
|
62
|
+
}
|
|
63
|
+
for (const { strategy, re } of PATTERNS) {
|
|
64
|
+
const match = re.exec(raw);
|
|
65
|
+
const captured = match?.[1];
|
|
66
|
+
if (captured !== undefined && captured !== '') {
|
|
67
|
+
return { strategy, value: captured, raw, accessibleName: null };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Anything left that looks like a selector is treated as CSS. This is a
|
|
71
|
+
// deliberate floor, not a guess: CSS ranks low, so an unparsed locator can
|
|
72
|
+
// never be mistaken for a durable one.
|
|
73
|
+
return { strategy: 'css', value: raw, raw, accessibleName: null };
|
|
74
|
+
}
|
|
75
|
+
export function stabilityRankOf(strategy) {
|
|
76
|
+
return STABILITY_RANK[strategy];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Positive = the replacement is MORE stable. Negative = less stable, which
|
|
80
|
+
* requires human review regardless of aggressiveness setting.
|
|
81
|
+
*/
|
|
82
|
+
export function stabilityDelta(from, to) {
|
|
83
|
+
return STABILITY_RANK[from] - STABILITY_RANK[to];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Positional, prefix-weighted distance between two test ids.
|
|
87
|
+
*
|
|
88
|
+
* Test ids in a component-structured app read outside-in —
|
|
89
|
+
* `<feature>-<component>-<element>` — so the LEADING tokens identify where the
|
|
90
|
+
* element lives and the trailing token identifies what it is. Elements get
|
|
91
|
+
* renamed far more often than they move between features, so disagreement in
|
|
92
|
+
* the prefix is the stronger signal that these are different elements.
|
|
93
|
+
* Weights therefore decay from the front.
|
|
94
|
+
*
|
|
95
|
+
* gym-card-name → gym-card-title 0.17 same place, renamed ← the common heal
|
|
96
|
+
* gym-card-name → gym-card-county 0.17 same place, sibling field
|
|
97
|
+
* gym-card-name → event-card-name 0.50 different feature
|
|
98
|
+
* gym-card-name → checkout-submit 1.00 unrelated
|
|
99
|
+
*
|
|
100
|
+
* Note that a rename and a sibling field score identically. That is honest:
|
|
101
|
+
* nothing in the STRING distinguishes them. Separating the two is exactly the
|
|
102
|
+
* judgement the Tier-1 ranker exists to make, using the failing assertion's
|
|
103
|
+
* domain arguments — which is why Tier 0 hands over candidates rather than
|
|
104
|
+
* picking one.
|
|
105
|
+
*
|
|
106
|
+
* Returns 0 (identical) .. 1 (nothing in common).
|
|
107
|
+
*/
|
|
108
|
+
export function testIdDistance(a, b) {
|
|
109
|
+
if (a === b)
|
|
110
|
+
return 0;
|
|
111
|
+
const at = a.split(/[-_.:]/).filter(Boolean);
|
|
112
|
+
const bt = b.split(/[-_.:]/).filter(Boolean);
|
|
113
|
+
if (at.length === 0 || bt.length === 0)
|
|
114
|
+
return 1;
|
|
115
|
+
const length = Math.max(at.length, bt.length);
|
|
116
|
+
let mismatchWeight = 0;
|
|
117
|
+
let totalWeight = 0;
|
|
118
|
+
for (let i = 0; i < length; i++) {
|
|
119
|
+
// Leading tokens weigh most: position 0 of a 3-token id carries 3/6.
|
|
120
|
+
const weight = length - i;
|
|
121
|
+
totalWeight += weight;
|
|
122
|
+
if (at[i] !== bt[i])
|
|
123
|
+
mismatchWeight += weight;
|
|
124
|
+
}
|
|
125
|
+
return totalWeight === 0 ? 1 : Math.min(1, mismatchWeight / totalWeight);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=stability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stability.js","sourceRoot":"","sources":["../../src/locator/stability.ts"],"names":[],"mappings":"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"}
|