@jterrazz/test 9.2.1 → 10.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/README.md +72 -20
- package/dist/ambiguity.js +126 -0
- package/dist/appium.adapter.js +386 -0
- package/dist/index.d.ts +434 -14
- package/dist/index.js +651 -46
- package/dist/oxlint.cjs +111 -59
- package/dist/oxlint.d.cts +1 -1
- package/dist/oxlint.d.ts +1 -1
- package/dist/oxlint.js +111 -59
- package/dist/playwright.adapter.js +92 -17
- package/package.json +14 -6
package/dist/index.d.ts
CHANGED
|
@@ -497,15 +497,53 @@ interface InterceptContract {
|
|
|
497
497
|
declare function defineContract(contract: InterceptContract): InterceptContract;
|
|
498
498
|
//#endregion
|
|
499
499
|
//#region src/core/ports/browser.port.d.ts
|
|
500
|
+
/**
|
|
501
|
+
* The landmark roles — the standard set of page regions, and the only
|
|
502
|
+
* containers a scope can name. Closed on purpose: ARIA defines exactly these,
|
|
503
|
+
* so `within()` never becomes a second selector language.
|
|
504
|
+
*/
|
|
505
|
+
type LandmarkKind = 'banner' | 'complementary' | 'contentinfo' | 'form' | 'main' | 'navigation' | 'region' | 'search';
|
|
506
|
+
/** The interactive//textual element kinds — what a visitor actually acts on. */
|
|
507
|
+
type ElementKind = 'button' | 'field' | 'heading' | 'link' | 'testId' | 'text';
|
|
500
508
|
/**
|
|
501
509
|
* A user-facing element descriptor — pure data, built by the element
|
|
502
510
|
* vocabulary (`button()`, `link()`, `field()`, …) and translated into
|
|
503
511
|
* concrete locators by the browser integration. CSS/XPath selectors are
|
|
504
512
|
* deliberately not expressible: user-facing elements are the only surface.
|
|
513
|
+
*
|
|
514
|
+
* A descriptor must designate exactly ONE element at action time; see
|
|
515
|
+
* {@link ElementMatch} and CONVENTIONS W3.
|
|
505
516
|
*/
|
|
506
517
|
interface ElementRef {
|
|
507
|
-
|
|
508
|
-
|
|
518
|
+
/**
|
|
519
|
+
* Match the accessible name as a whole string rather than a substring.
|
|
520
|
+
* Default (`false`) mirrors playwright: `link('Articles')` also matches
|
|
521
|
+
* "Read Articles".
|
|
522
|
+
*/
|
|
523
|
+
exact?: boolean;
|
|
524
|
+
kind: ElementKind | LandmarkKind;
|
|
525
|
+
/** Landmarks may be anonymous (`main()`, `banner()`); everything else is named. */
|
|
526
|
+
name?: string;
|
|
527
|
+
/**
|
|
528
|
+
* Restrict the search to the elements of another descriptor — built by
|
|
529
|
+
* `within(scope, target)`. Chains: a scope may itself carry a scope.
|
|
530
|
+
*/
|
|
531
|
+
scope?: ElementRef;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* One candidate captured when a descriptor matched more than one element —
|
|
535
|
+
* the evidence the ambiguity error enumerates so the author can disambiguate
|
|
536
|
+
* without opening a browser.
|
|
537
|
+
*/
|
|
538
|
+
interface ElementMatch {
|
|
539
|
+
/** Nearest landmark ancestor (`nav`, `footer`, `main`…), when there is one. */
|
|
540
|
+
context?: string;
|
|
541
|
+
/** The attribute that disambiguates most — `href` for links, `name` for fields. */
|
|
542
|
+
detail?: string;
|
|
543
|
+
/** Tag name, lower-cased. */
|
|
544
|
+
tag: string;
|
|
545
|
+
/** Text content, whitespace-collapsed and truncated. */
|
|
546
|
+
text: string;
|
|
509
547
|
}
|
|
510
548
|
/** A `<link>` element captured from the rendered document's head. */
|
|
511
549
|
interface BrowserLinkElement {
|
|
@@ -553,6 +591,11 @@ interface Visitor {
|
|
|
553
591
|
type VisitScenario = (visitor: Visitor) => Promise<void>;
|
|
554
592
|
/** Per-visit options forwarded to the browser context. */
|
|
555
593
|
interface BrowserOpenOptions {
|
|
594
|
+
/**
|
|
595
|
+
* Extra origins the `external: 'block'` policy lets through — the
|
|
596
|
+
* declared stub backend the page legitimately fetches from.
|
|
597
|
+
*/
|
|
598
|
+
allowedOrigins?: string[];
|
|
556
599
|
/**
|
|
557
600
|
* Base URL of the site under test — the origin `goto()` resolves against
|
|
558
601
|
* and the boundary of the `external` policy.
|
|
@@ -606,6 +649,107 @@ interface BrowserPort {
|
|
|
606
649
|
open: (url: string, options: BrowserOpenOptions) => Promise<BrowserPage>;
|
|
607
650
|
}
|
|
608
651
|
//#endregion
|
|
652
|
+
//#region src/core/ports/device.port.d.ts
|
|
653
|
+
/**
|
|
654
|
+
* The element kinds a mobile screen can designate — the structural subset of
|
|
655
|
+
* {@link ElementRef} kinds that map onto the XCUITest accessibility tree.
|
|
656
|
+
* There is ONE element vocabulary across facets: `button('Bookmark')` works
|
|
657
|
+
* in a visit scenario and a mobile scenario alike. Landmarks have no iOS
|
|
658
|
+
* analog — passing one to a mobile verb refuses at runtime.
|
|
659
|
+
*/
|
|
660
|
+
type MobileElementKind = 'button' | 'field' | 'testId' | 'text';
|
|
661
|
+
/**
|
|
662
|
+
* A user-facing element descriptor for mobile scenarios — pure data, built by
|
|
663
|
+
* the shared element vocabulary (`button()`, `field()`, `content()`,
|
|
664
|
+
* `testId()`, `within()`) and translated into iOS predicate strings by the
|
|
665
|
+
* device integration. Structurally the same ref type as the browser's, so the
|
|
666
|
+
* vocabulary stays single; kinds outside {@link MobileElementKind} (the ARIA
|
|
667
|
+
* landmarks) are refused at runtime with a message naming the boundary.
|
|
668
|
+
*
|
|
669
|
+
* A descriptor must designate exactly ONE element when a verb ACTS on it
|
|
670
|
+
* (`tap`, `fill`) — see {@link MobileElementMatch} and CONVENTIONS W3.
|
|
671
|
+
* `see()` acts on nothing, so any visible match satisfies it: XCUITest trees
|
|
672
|
+
* legitimately duplicate a label across container and child.
|
|
673
|
+
*/
|
|
674
|
+
type MobileElementRef = ElementRef;
|
|
675
|
+
/**
|
|
676
|
+
* One candidate captured when a descriptor matched more than one element —
|
|
677
|
+
* the evidence the ambiguity error enumerates so the author can disambiguate
|
|
678
|
+
* without opening the simulator.
|
|
679
|
+
*/
|
|
680
|
+
interface MobileElementMatch {
|
|
681
|
+
/** The accessibility identifier (`testId()` target), when one is set. */
|
|
682
|
+
identifier?: string;
|
|
683
|
+
/** The accessible label, whitespace-collapsed and truncated. */
|
|
684
|
+
label?: string;
|
|
685
|
+
/** Element type without the `XCUIElementType` prefix — `Button`, `StaticText`. */
|
|
686
|
+
type: string;
|
|
687
|
+
/** The element value (text-field content, adjustable value), when present. */
|
|
688
|
+
value?: string;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* The visitor — the interaction vocabulary handed to a mobile scenario.
|
|
692
|
+
* Every verb auto-waits by polling until a visible match exists; acting
|
|
693
|
+
* verbs then enforce exactly-one (W3), while `see()` — the single
|
|
694
|
+
* synchronization primitive — is satisfied by any visible match. There is
|
|
695
|
+
* no sleep and no conditional helper.
|
|
696
|
+
*/
|
|
697
|
+
interface MobileVisitor {
|
|
698
|
+
/** Fill a text field with a value. */
|
|
699
|
+
fill: (element: MobileElementRef, value: string) => Promise<void>;
|
|
700
|
+
/** Wait until the element is visible — the only synchronization primitive. */
|
|
701
|
+
see: (element: MobileElementRef) => Promise<void>;
|
|
702
|
+
/** Tap the element. */
|
|
703
|
+
tap: (element: MobileElementRef) => Promise<void>;
|
|
704
|
+
}
|
|
705
|
+
/** The behavior of an open — the When of the spec; assertions stay in the Then. */
|
|
706
|
+
type MobileScenario = (visitor: MobileVisitor) => Promise<void>;
|
|
707
|
+
/**
|
|
708
|
+
* One node of the projected accessibility tree — the XCUITest page source
|
|
709
|
+
* with its noise collapsed: unlabeled, identifier-less, valueless wrapper
|
|
710
|
+
* nodes are dropped and their children hoisted, so the projection stays
|
|
711
|
+
* stable and golden-friendly. Type names lose the `XCUIElementType` prefix.
|
|
712
|
+
*/
|
|
713
|
+
interface ScreenNode {
|
|
714
|
+
children?: ScreenNode[];
|
|
715
|
+
/** The accessibility identifier, only when it differs from the label. */
|
|
716
|
+
identifier?: string;
|
|
717
|
+
label?: string;
|
|
718
|
+
/** Element type without the `XCUIElementType` prefix — `Button`, `StaticText`. */
|
|
719
|
+
type: string;
|
|
720
|
+
value?: string;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* The screen captured by a device open — the FINAL state when a scenario
|
|
724
|
+
* ran. The tree is the projected page source; `texts` are the visible
|
|
725
|
+
* labels/values in document order, consecutive duplicates collapsed.
|
|
726
|
+
*/
|
|
727
|
+
interface DeviceScreen {
|
|
728
|
+
texts: string[];
|
|
729
|
+
tree: ScreenNode;
|
|
730
|
+
}
|
|
731
|
+
/** Per-open options forwarded to the device session. */
|
|
732
|
+
interface DeviceOpenOptions {
|
|
733
|
+
/** The app under test — terminated and relaunched for a fresh, deterministic state. */
|
|
734
|
+
bundleId: string;
|
|
735
|
+
/** Deep link applied after the relaunch (`news://events`); absent opens the app plainly. */
|
|
736
|
+
deepLink?: string;
|
|
737
|
+
/** The interaction scenario to run after launch; the capture reflects the final state. */
|
|
738
|
+
scenario?: MobileScenario;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Abstract device interface for the mobile specification runner.
|
|
742
|
+
* One implementation lives in `integrations/appium/` — a single driver
|
|
743
|
+
* session per runner, created on the first `open()` and reused; each open
|
|
744
|
+
* terminates and relaunches the app for a deterministic fresh state.
|
|
745
|
+
*/
|
|
746
|
+
interface DevicePort {
|
|
747
|
+
/** End the driver session (idempotent). */
|
|
748
|
+
close: () => Promise<void>;
|
|
749
|
+
/** Relaunch the app, apply the deep link, run the scenario, capture the final screen. */
|
|
750
|
+
open: (options: DeviceOpenOptions) => Promise<DeviceScreen>;
|
|
751
|
+
}
|
|
752
|
+
//#endregion
|
|
609
753
|
//#region src/core/ports/isolation.port.d.ts
|
|
610
754
|
/**
|
|
611
755
|
* Strategy for isolating service state across parallel test workers.
|
|
@@ -733,6 +877,27 @@ declare class HttpResult extends BaseResult {
|
|
|
733
877
|
get status(): number;
|
|
734
878
|
}
|
|
735
879
|
//#endregion
|
|
880
|
+
//#region src/core/specification/mobile/result.d.ts
|
|
881
|
+
/** Result from a `.open()` action — the screen as the device saw it, final state. */
|
|
882
|
+
declare class ScreenResult extends BaseResult {
|
|
883
|
+
private readonly capturedScreen;
|
|
884
|
+
constructor(options: {
|
|
885
|
+
config: SpecificationConfig;
|
|
886
|
+
screen: DeviceScreen;
|
|
887
|
+
testDir: string;
|
|
888
|
+
});
|
|
889
|
+
/**
|
|
890
|
+
* The visible screen texts as one stream, in reading order — the scalpel
|
|
891
|
+
* for a targeted probe: `expect(result.content).toContain('Bookmarked')`.
|
|
892
|
+
*/
|
|
893
|
+
get content(): TextAccessor;
|
|
894
|
+
/**
|
|
895
|
+
* The projected accessibility tree as a JSON accessor — the one golden
|
|
896
|
+
* per screen: `expect(result.screen).toMatch('events.screen.json')`.
|
|
897
|
+
*/
|
|
898
|
+
get screen(): JsonAccessor;
|
|
899
|
+
}
|
|
900
|
+
//#endregion
|
|
736
901
|
//#region src/core/specification/website/result.d.ts
|
|
737
902
|
/** A raw HTTP exchange captured by `.fetch()` — redirects are NOT followed. */
|
|
738
903
|
interface FetchExchange {
|
|
@@ -810,6 +975,78 @@ declare class PageResult extends BaseResult {
|
|
|
810
975
|
get url(): string;
|
|
811
976
|
}
|
|
812
977
|
//#endregion
|
|
978
|
+
//#region src/core/http-files/intercept-file.d.ts
|
|
979
|
+
/** The declared request half of an exchange — the matching trigger. */
|
|
980
|
+
interface ExchangeRequest {
|
|
981
|
+
/** Declared header subset — names case-insensitive, values may carry `{{token}}`s. */
|
|
982
|
+
headers: Record<string, string>;
|
|
983
|
+
method: string;
|
|
984
|
+
/** Path as written, query string included (`/articles?event_id=…`). */
|
|
985
|
+
path: string;
|
|
986
|
+
}
|
|
987
|
+
/** The declared response half of an exchange — the stubbed reply. */
|
|
988
|
+
interface ExchangeResponse {
|
|
989
|
+
/** Parsed JSON body — or the raw text when the body is not valid JSON. */
|
|
990
|
+
body: unknown;
|
|
991
|
+
/** True when a body section is present. */
|
|
992
|
+
hasBody: boolean;
|
|
993
|
+
headers: Record<string, string>;
|
|
994
|
+
status: number;
|
|
995
|
+
}
|
|
996
|
+
/** One declared request/response pair of an `intercepts/*.http` file. */
|
|
997
|
+
interface InterceptExchange {
|
|
998
|
+
request: ExchangeRequest;
|
|
999
|
+
response: ExchangeResponse;
|
|
1000
|
+
}
|
|
1001
|
+
//#endregion
|
|
1002
|
+
//#region src/core/specification/shared/stub-backend.d.ts
|
|
1003
|
+
/** Options for the stub backend server. */
|
|
1004
|
+
interface StubBackendOptions {
|
|
1005
|
+
/**
|
|
1006
|
+
* Fixed port — pins a stable stub URL across runs (a bundler that inlines
|
|
1007
|
+
* the URL at serve time survives warm). Default: a free OS-assigned port.
|
|
1008
|
+
*/
|
|
1009
|
+
port?: number;
|
|
1010
|
+
}
|
|
1011
|
+
declare class StubBackend {
|
|
1012
|
+
private consumed;
|
|
1013
|
+
private entries;
|
|
1014
|
+
/** Guarded once the current chain declared at least one intercept. */
|
|
1015
|
+
private guarded;
|
|
1016
|
+
private readonly options;
|
|
1017
|
+
private server;
|
|
1018
|
+
private readonly unmatchedRequests;
|
|
1019
|
+
private url;
|
|
1020
|
+
constructor(options?: StubBackendOptions);
|
|
1021
|
+
/** Start the server and resolve with its base URL. */
|
|
1022
|
+
start(): Promise<string>;
|
|
1023
|
+
/** Stop the server (idempotent). */
|
|
1024
|
+
stop(): Promise<void>;
|
|
1025
|
+
/**
|
|
1026
|
+
* Arm the stub for one chain: the declared exchanges replace the previous
|
|
1027
|
+
* chain's wholesale, consumption restarts, and the unmatched log clears —
|
|
1028
|
+
* the reset-between-chains databases already follow.
|
|
1029
|
+
*/
|
|
1030
|
+
beginChain(exchanges: InterceptExchange[]): void;
|
|
1031
|
+
/**
|
|
1032
|
+
* The strict failure for the chain, or null — non-null when the chain
|
|
1033
|
+
* declared at least one intercept AND an unmatched request was recorded.
|
|
1034
|
+
* Enumerates every unmatched request (method, path, count) plus the
|
|
1035
|
+
* declared routes, so the missing exchange writes itself.
|
|
1036
|
+
*/
|
|
1037
|
+
violation(): Error | null;
|
|
1038
|
+
private corsHeaders;
|
|
1039
|
+
private describeRoutes;
|
|
1040
|
+
private handle;
|
|
1041
|
+
private record;
|
|
1042
|
+
/**
|
|
1043
|
+
* FIFO with a sticky tail: the first unconsumed matching exchange is
|
|
1044
|
+
* consumed; when every matching exchange is spent, the LAST one keeps
|
|
1045
|
+
* replying.
|
|
1046
|
+
*/
|
|
1047
|
+
private take;
|
|
1048
|
+
}
|
|
1049
|
+
//#endregion
|
|
813
1050
|
//#region src/core/specification/shared/builder.d.ts
|
|
814
1051
|
/** A named job that can be triggered via jobs.trigger(). */
|
|
815
1052
|
interface JobHandle {
|
|
@@ -829,6 +1066,16 @@ interface DockerSpecConfig {
|
|
|
829
1066
|
}
|
|
830
1067
|
/** Adapter configuration passed to the specification facets at setup time. */
|
|
831
1068
|
interface SpecificationConfig {
|
|
1069
|
+
/**
|
|
1070
|
+
* The declared stub backend (website/mobile facets) — armed with the
|
|
1071
|
+
* chain's `.http` intercept exchanges before every terminal action.
|
|
1072
|
+
*/
|
|
1073
|
+
backend?: StubBackend;
|
|
1074
|
+
/**
|
|
1075
|
+
* Base URL of the running stub backend — allow-listed through the
|
|
1076
|
+
* browser's `external: 'block'` policy so client-side fetches reach it.
|
|
1077
|
+
*/
|
|
1078
|
+
backendUrl?: string;
|
|
832
1079
|
/** Base URL of the website under test (website facet only). */
|
|
833
1080
|
baseUrl?: string;
|
|
834
1081
|
/**
|
|
@@ -843,6 +1090,8 @@ interface SpecificationConfig {
|
|
|
843
1090
|
* of strict intercepts.
|
|
844
1091
|
*/
|
|
845
1092
|
external?: 'allow' | 'block';
|
|
1093
|
+
/** Bundle id of the app under test (mobile facet only) — the app `.open()` relaunches. */
|
|
1094
|
+
bundleId?: string;
|
|
846
1095
|
command?: CliPort;
|
|
847
1096
|
database?: DatabasePort;
|
|
848
1097
|
/**
|
|
@@ -852,6 +1101,13 @@ interface SpecificationConfig {
|
|
|
852
1101
|
*/
|
|
853
1102
|
databaseKeys?: string[];
|
|
854
1103
|
databases?: Map<string, DatabasePort>;
|
|
1104
|
+
/**
|
|
1105
|
+
* Lazy device accessor (mobile facet only). The first `.open()` creates
|
|
1106
|
+
* the shared driver session; the appium/webdriverio integration stays a
|
|
1107
|
+
* lazy import so the optional peer is only loaded when a spec opens the
|
|
1108
|
+
* app.
|
|
1109
|
+
*/
|
|
1110
|
+
device?: () => Promise<DevicePort>;
|
|
855
1111
|
dockerConfig?: DockerSpecConfig;
|
|
856
1112
|
/**
|
|
857
1113
|
* Unique id shared by every spec from this runner instance.
|
|
@@ -891,8 +1147,8 @@ interface SpecificationConfig {
|
|
|
891
1147
|
interface ApiSpecification<DatabaseKey extends string = string> {
|
|
892
1148
|
/** Set HTTP headers for the request. Multiple calls merge. */
|
|
893
1149
|
headers: (headers: Record<string, string>) => ApiSpecification<DatabaseKey>;
|
|
894
|
-
/** Intercept an outgoing HTTP call — a contract, an array of contracts, or a trigger + response pair. */
|
|
895
|
-
intercept: ((contractOrContracts: InterceptContract | InterceptContract[]) => ApiSpecification<DatabaseKey>) & ((trigger: InterceptTrigger, response: InterceptResponder | InterceptResponse | string) => ApiSpecification<DatabaseKey>);
|
|
1150
|
+
/** Intercept an outgoing HTTP call — a contract, an array of contracts, an `intercepts/*.http` file, or a trigger + response pair. */
|
|
1151
|
+
intercept: ((contractOrContracts: InterceptContract | InterceptContract[] | string) => ApiSpecification<DatabaseKey>) & ((trigger: InterceptTrigger, response: InterceptResponder | InterceptResponse | string) => ApiSpecification<DatabaseKey>);
|
|
896
1152
|
/** Queue a SQL seed file from `seeds/` to run before the action. */
|
|
897
1153
|
seed: (file: string, options?: {
|
|
898
1154
|
database?: DatabaseKey;
|
|
@@ -913,8 +1169,8 @@ interface ApiSpecification<DatabaseKey extends string = string> {
|
|
|
913
1169
|
* Jobs run in-process by definition (CONVENTIONS A5/A8).
|
|
914
1170
|
*/
|
|
915
1171
|
interface JobsSpecification<DatabaseKey extends string = string> {
|
|
916
|
-
/** Intercept an outgoing HTTP call — a contract, an array of contracts, or a trigger + response pair. */
|
|
917
|
-
intercept: ((contractOrContracts: InterceptContract | InterceptContract[]) => JobsSpecification<DatabaseKey>) & ((trigger: InterceptTrigger, response: InterceptResponder | InterceptResponse | string) => JobsSpecification<DatabaseKey>);
|
|
1172
|
+
/** Intercept an outgoing HTTP call — a contract, an array of contracts, an `intercepts/*.http` file, or a trigger + response pair. */
|
|
1173
|
+
intercept: ((contractOrContracts: InterceptContract | InterceptContract[] | string) => JobsSpecification<DatabaseKey>) & ((trigger: InterceptTrigger, response: InterceptResponder | InterceptResponse | string) => JobsSpecification<DatabaseKey>);
|
|
918
1174
|
/** Queue a SQL seed file from `seeds/` to run before the action. */
|
|
919
1175
|
seed: (file: string, options?: {
|
|
920
1176
|
database?: DatabaseKey;
|
|
@@ -960,6 +1216,12 @@ interface CliSpecification<DatabaseKey extends string = string> {
|
|
|
960
1216
|
interface WebsiteSpecification {
|
|
961
1217
|
/** Set HTTP headers for the exchange (incl. User-Agent overrides). Multiple calls merge. */
|
|
962
1218
|
headers: (headers: Record<string, string>) => WebsiteSpecification;
|
|
1219
|
+
/**
|
|
1220
|
+
* Declare the chain's backend exchanges from an `intercepts/<name>.http`
|
|
1221
|
+
* file — served by the declared stub backend (requires the runner's
|
|
1222
|
+
* `backend` option). Multiple calls layer in order.
|
|
1223
|
+
*/
|
|
1224
|
+
intercept: (file: string) => WebsiteSpecification;
|
|
963
1225
|
/** Perform one raw HTTP GET — redirects surface as 3xx results, never followed. */
|
|
964
1226
|
fetch: (path: string) => Promise<FetchResult>;
|
|
965
1227
|
/**
|
|
@@ -969,6 +1231,26 @@ interface WebsiteSpecification {
|
|
|
969
1231
|
*/
|
|
970
1232
|
visit: (path: string, scenario?: VisitScenario) => Promise<PageResult>;
|
|
971
1233
|
}
|
|
1234
|
+
/**
|
|
1235
|
+
* The `mobile` facet — screen chain entry handed out by
|
|
1236
|
+
* `specification.mobile()`. `.open()` is the single, terminal action: it
|
|
1237
|
+
* terminates and relaunches the app (deterministic fresh state), applies the
|
|
1238
|
+
* deep link, runs the scenario, and captures the final screen.
|
|
1239
|
+
*/
|
|
1240
|
+
interface MobileSpecification {
|
|
1241
|
+
/**
|
|
1242
|
+
* Declare the chain's backend exchanges from an `intercepts/<name>.http`
|
|
1243
|
+
* file — served by the declared stub backend (requires the runner's
|
|
1244
|
+
* `backend` option). Multiple calls layer in order.
|
|
1245
|
+
*/
|
|
1246
|
+
intercept: (file: string) => MobileSpecification;
|
|
1247
|
+
/**
|
|
1248
|
+
* Relaunch the app and resolve with the captured screen. With a deep
|
|
1249
|
+
* link, the app opens on it; with a scenario, the visitor interacts
|
|
1250
|
+
* first (the When) and the capture reflects the FINAL screen state.
|
|
1251
|
+
*/
|
|
1252
|
+
open: (deepLink?: string, scenario?: MobileScenario) => Promise<ScreenResult>;
|
|
1253
|
+
}
|
|
972
1254
|
//#endregion
|
|
973
1255
|
//#region src/core/specification/cli/result.d.ts
|
|
974
1256
|
/**
|
|
@@ -1320,6 +1602,72 @@ interface JobsHandle<DatabaseKey extends string = string> {
|
|
|
1320
1602
|
}
|
|
1321
1603
|
declare function startJobs<Services extends ServiceRecord>(options: JobsSpecificationOptions<Services>): Promise<JobsHandle<DatabaseKeys<Services>>>;
|
|
1322
1604
|
//#endregion
|
|
1605
|
+
//#region src/core/specification/mobile/start-mobile.d.ts
|
|
1606
|
+
/**
|
|
1607
|
+
* The declared stub backend behind the app under test. The framework owns
|
|
1608
|
+
* the simulator and appium but NOT the JS bundler (Metro belongs to the
|
|
1609
|
+
* caller's repo, like `next build` belongs to a website's) — so nothing is
|
|
1610
|
+
* injected: the handle exposes `backendUrl` and the CALLER wires it into its
|
|
1611
|
+
* own bundler env.
|
|
1612
|
+
*/
|
|
1613
|
+
interface MobileBackendOptions {
|
|
1614
|
+
/**
|
|
1615
|
+
* Fixed port — pins a stable stub URL across runs. Metro inlines
|
|
1616
|
+
* `EXPO_PUBLIC_*` values at bundle-serve time; a stable port lets a warm
|
|
1617
|
+
* Metro survive between runs. Default: a free OS-assigned port.
|
|
1618
|
+
*/
|
|
1619
|
+
port?: number;
|
|
1620
|
+
}
|
|
1621
|
+
/** Options for {@link startMobile | specification.mobile}. */
|
|
1622
|
+
interface MobileSpecificationOptions {
|
|
1623
|
+
/** The app under test — terminated and relaunched by every `.open()`. */
|
|
1624
|
+
app: {
|
|
1625
|
+
/** Bundle id of the installed app (`com.example.app`). */
|
|
1626
|
+
bundleId: string;
|
|
1627
|
+
};
|
|
1628
|
+
/**
|
|
1629
|
+
* Declared stub backend: started with the runner, serving the exchanges
|
|
1630
|
+
* each chain declares via `.intercept('<name>.http')`. The handle gains
|
|
1631
|
+
* `backendUrl` — inject it into your bundler env yourself.
|
|
1632
|
+
*/
|
|
1633
|
+
backend?: MobileBackendOptions;
|
|
1634
|
+
/** The simulator to run on — resolved through `xcrun simctl`. */
|
|
1635
|
+
device: {
|
|
1636
|
+
/** Simulator name, matched exactly (`iPhone 17`). */
|
|
1637
|
+
name: string;
|
|
1638
|
+
/** OS narrowing when the name exists on several runtimes — `'26.5'` or `'iOS 26.5'`. */
|
|
1639
|
+
os?: string;
|
|
1640
|
+
/** Explicit UDID — skips name/os resolution entirely. */
|
|
1641
|
+
udid?: string;
|
|
1642
|
+
};
|
|
1643
|
+
/**
|
|
1644
|
+
* Project-root override (CONVENTIONS A9): where the appium binary is
|
|
1645
|
+
* resolved from (`node_modules/.bin`). Auto-discovered from the calling
|
|
1646
|
+
* file when absent.
|
|
1647
|
+
*/
|
|
1648
|
+
root?: string;
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* The record returned by {@link startMobile | specification.mobile}.
|
|
1652
|
+
* Destructure with the canonical names (CONVENTIONS A3):
|
|
1653
|
+
*
|
|
1654
|
+
* const { mobile, cleanup, udid } = await specification.mobile(…);
|
|
1655
|
+
*/
|
|
1656
|
+
interface MobileHandle {
|
|
1657
|
+
/**
|
|
1658
|
+
* Base URL of the declared stub backend — present only with the
|
|
1659
|
+
* `backend` option. The caller injects it into its own bundler env
|
|
1660
|
+
* (e.g. `EXPO_PUBLIC_API_URL`); the framework never touches the bundler.
|
|
1661
|
+
*/
|
|
1662
|
+
backendUrl?: string;
|
|
1663
|
+
/** End the driver session, stop the appium server and the stub backend. */
|
|
1664
|
+
cleanup: () => Promise<void>;
|
|
1665
|
+
mobile: MobileSpecification;
|
|
1666
|
+
/** The resolved simulator UDID the specs run against. */
|
|
1667
|
+
udid: string;
|
|
1668
|
+
}
|
|
1669
|
+
declare function startMobile(options: MobileSpecificationOptions): Promise<MobileHandle>;
|
|
1670
|
+
//#endregion
|
|
1323
1671
|
//#region src/core/specification/website/serve.adapter.d.ts
|
|
1324
1672
|
/** Options for the local server started by `specification.website()`. */
|
|
1325
1673
|
interface ServeOptions {
|
|
@@ -1334,11 +1682,25 @@ interface ServeOptions {
|
|
|
1334
1682
|
}
|
|
1335
1683
|
//#endregion
|
|
1336
1684
|
//#region src/core/specification/website/start-website.d.ts
|
|
1685
|
+
/**
|
|
1686
|
+
* The declared stub backend behind the site under test — started before the
|
|
1687
|
+
* server command, torn down with the runner. Its URL is injected into the
|
|
1688
|
+
* server child's environment under `env`; the chain's
|
|
1689
|
+
* `.intercept('<name>.http')` exchanges are what it serves.
|
|
1690
|
+
*/
|
|
1691
|
+
interface WebsiteBackendOptions {
|
|
1692
|
+
/** Env var receiving the stub's URL in the server child (e.g. `'API_URL'`). */
|
|
1693
|
+
env: string;
|
|
1694
|
+
/** Fixed port — pins a stable stub URL across runs. Default: a free OS-assigned port. */
|
|
1695
|
+
port?: number;
|
|
1696
|
+
}
|
|
1337
1697
|
/**
|
|
1338
1698
|
* Options for {@link startWebsite | specification.website}. `server` (start
|
|
1339
1699
|
* the site locally) and `url` (target a running site) are mutually
|
|
1340
1700
|
* exclusive BY TYPE — the union makes the invalid combinations
|
|
1341
|
-
* inexpressible rather than runtime-checked.
|
|
1701
|
+
* inexpressible rather than runtime-checked. `backend` requires `server`
|
|
1702
|
+
* mode for the same reason: a deployed site cannot be pointed at a local
|
|
1703
|
+
* stub.
|
|
1342
1704
|
*/
|
|
1343
1705
|
type WebsiteSpecificationOptions = {
|
|
1344
1706
|
/**
|
|
@@ -1353,6 +1715,12 @@ type WebsiteSpecificationOptions = {
|
|
|
1353
1715
|
*/
|
|
1354
1716
|
root?: string;
|
|
1355
1717
|
} & ({
|
|
1718
|
+
/**
|
|
1719
|
+
* Declared stub backend: started BEFORE the server command, its
|
|
1720
|
+
* URL injected into the child env under `backend.env`. Serves the
|
|
1721
|
+
* exchanges each chain declares via `.intercept('<name>.http')`.
|
|
1722
|
+
*/
|
|
1723
|
+
backend?: WebsiteBackendOptions;
|
|
1356
1724
|
/**
|
|
1357
1725
|
* Start the site locally: a shell command receiving a free port
|
|
1358
1726
|
* as `PORT`, polled on `ready` (default `/`) until it answers.
|
|
@@ -1360,6 +1728,7 @@ type WebsiteSpecificationOptions = {
|
|
|
1360
1728
|
server: ServeOptions;
|
|
1361
1729
|
url?: never;
|
|
1362
1730
|
} | {
|
|
1731
|
+
backend?: never;
|
|
1363
1732
|
server?: never;
|
|
1364
1733
|
/** Target an already-running site (a deployed or preview URL). */
|
|
1365
1734
|
url: string;
|
|
@@ -1381,7 +1750,7 @@ declare function startWebsite(options: WebsiteSpecificationOptions): Promise<Web
|
|
|
1381
1750
|
//#endregion
|
|
1382
1751
|
//#region src/core/specification/shared/specification.d.ts
|
|
1383
1752
|
/**
|
|
1384
|
-
* The
|
|
1753
|
+
* The five specification constructors (CONVENTIONS A2) — created in a
|
|
1385
1754
|
* `*.specification.ts` file under `specs/`, destructured with canonical
|
|
1386
1755
|
* names, and cleaned up via `afterAll(cleanup)` (A1/A3/A4).
|
|
1387
1756
|
*
|
|
@@ -1424,6 +1793,13 @@ declare const specification: {
|
|
|
1424
1793
|
* server, no mode. `.trigger(name)` is the terminal action.
|
|
1425
1794
|
*/
|
|
1426
1795
|
jobs: typeof startJobs;
|
|
1796
|
+
/**
|
|
1797
|
+
* Test a native app on the iOS simulator. `device` names the simulator
|
|
1798
|
+
* (resolved and booted via `simctl`); `app` names the bundle under
|
|
1799
|
+
* test. `.open(deepLink?, scenario?)` relaunches the app fresh, runs
|
|
1800
|
+
* the scenario, and captures the final screen.
|
|
1801
|
+
*/
|
|
1802
|
+
mobile: typeof startMobile;
|
|
1427
1803
|
/**
|
|
1428
1804
|
* Test a deployed or locally-served website. `server` starts the site
|
|
1429
1805
|
* (a shell command receiving `PORT`); `url` targets a running one.
|
|
@@ -1468,19 +1844,63 @@ interface ContainerPort {
|
|
|
1468
1844
|
* Reads like English (`click(link('Articles'))`), translates to
|
|
1469
1845
|
* accessibility-first locators in the browser integration. CSS/XPath is
|
|
1470
1846
|
* deliberately not expressible; `testId()` is the single escape hatch.
|
|
1847
|
+
*
|
|
1848
|
+
* A descriptor must designate exactly ONE element (CONVENTIONS W3). Two knobs
|
|
1849
|
+
* narrow it, in this order of preference:
|
|
1850
|
+
*
|
|
1851
|
+
* 1. `within(scope, target)` — search inside a landmark, the way a person
|
|
1852
|
+
* would say "the Articles link *in the nav*";
|
|
1853
|
+
* 2. `{ exact: true }` — match the accessible name whole rather than as a
|
|
1854
|
+
* substring, when two names genuinely overlap.
|
|
1471
1855
|
*/
|
|
1856
|
+
/** Options accepted by every named descriptor. */
|
|
1857
|
+
interface ElementOptions {
|
|
1858
|
+
/**
|
|
1859
|
+
* Match the accessible name as a whole string. Default is substring —
|
|
1860
|
+
* `link('Articles')` also matches "Read Articles".
|
|
1861
|
+
*/
|
|
1862
|
+
exact?: boolean;
|
|
1863
|
+
}
|
|
1472
1864
|
/** A button (or element with the button role), by accessible name. */
|
|
1473
|
-
declare const button: (name: string) => ElementRef;
|
|
1865
|
+
declare const button: (name: string, options?: ElementOptions) => ElementRef;
|
|
1474
1866
|
/** A form field, by label. */
|
|
1475
|
-
declare const field: (
|
|
1867
|
+
declare const field: (name: string, options?: ElementOptions) => ElementRef;
|
|
1476
1868
|
/** A heading, by accessible name. */
|
|
1477
|
-
declare const heading: (name: string) => ElementRef;
|
|
1869
|
+
declare const heading: (name: string, options?: ElementOptions) => ElementRef;
|
|
1478
1870
|
/** A link, by accessible name. */
|
|
1479
|
-
declare const link: (name: string) => ElementRef;
|
|
1871
|
+
declare const link: (name: string, options?: ElementOptions) => ElementRef;
|
|
1480
1872
|
/** An element containing the given text. */
|
|
1481
|
-
declare const content: (
|
|
1873
|
+
declare const content: (name: string, options?: ElementOptions) => ElementRef;
|
|
1482
1874
|
/** The escape hatch: an element by `data-testid`. Prefer user-facing elements. */
|
|
1483
1875
|
declare const testId: (id: string) => ElementRef;
|
|
1876
|
+
/** The `banner` landmark — the page header. */
|
|
1877
|
+
declare const banner: (name?: string, options?: ElementOptions) => ElementRef;
|
|
1878
|
+
/** The `complementary` landmark — an `<aside>`, a sidebar. */
|
|
1879
|
+
declare const complementary: (name?: string, options?: ElementOptions) => ElementRef;
|
|
1880
|
+
/** The `contentinfo` landmark — the page footer. */
|
|
1881
|
+
declare const contentinfo: (name?: string, options?: ElementOptions) => ElementRef;
|
|
1882
|
+
/** The `form` landmark — a form carrying an accessible name. */
|
|
1883
|
+
declare const form: (name?: string, options?: ElementOptions) => ElementRef;
|
|
1884
|
+
/** The `main` landmark — the primary content of the document. */
|
|
1885
|
+
declare const main: (name?: string, options?: ElementOptions) => ElementRef;
|
|
1886
|
+
/** The `navigation` landmark — a `<nav>`. Name it when a page has several. */
|
|
1887
|
+
declare const navigation: (name?: string, options?: ElementOptions) => ElementRef;
|
|
1888
|
+
/** The `region` landmark — a `<section>` carrying an accessible name. */
|
|
1889
|
+
declare const region: (name?: string, options?: ElementOptions) => ElementRef;
|
|
1890
|
+
/** The `search` landmark. */
|
|
1891
|
+
declare const search: (name?: string, options?: ElementOptions) => ElementRef;
|
|
1892
|
+
/**
|
|
1893
|
+
* Restrict a descriptor to the inside of another — the answer to ambiguity,
|
|
1894
|
+
* and the one the framework prefers over a test id.
|
|
1895
|
+
*
|
|
1896
|
+
* click(within(navigation(), link('Articles')))
|
|
1897
|
+
*
|
|
1898
|
+
* Composes outside-in: the scope may itself be scoped, so a deeply nested
|
|
1899
|
+
* target reads `within(main(), within(region('Series'), link('Part 2')))`.
|
|
1900
|
+
* Any descriptor works as a scope, including `testId()` when a container has
|
|
1901
|
+
* no landmark role to stand on.
|
|
1902
|
+
*/
|
|
1903
|
+
declare const within: (scope: ElementRef, target: ElementRef) => ElementRef;
|
|
1484
1904
|
//#endregion
|
|
1485
1905
|
//#region src/integrations/postgres/postgres.d.ts
|
|
1486
1906
|
interface PostgresOptions {
|
|
@@ -1809,4 +2229,4 @@ declare module 'vitest' {
|
|
|
1809
2229
|
}
|
|
1810
2230
|
}
|
|
1811
2231
|
//#endregion
|
|
1812
|
-
export { type ApiHandle, type ApiSpecification, type ApiSpecificationOptions, BaseResult, type BrowserConsoleMessage, type BrowserLinkElement, type BrowserMetaElement, type BrowserOpenOptions, type BrowserPage, type BrowserPort, type CaptureScope, type CliEnv, type CliHandle, type CliOutput, type CliPort, CliResult, type CliSpecification, type CliSpecificationOptions, ContainerAccessor, type ContainerPort, type DatabaseKeys, type DatabasePort, DirectoryAccessor, type DockerSpecConfig, type ElementRef, type ExecOptions, FetchResult, type FileAccessor, FilesystemAccessor, type HonoApp, HttpResult, type InterceptContract, type InterceptEntry, type InterceptResponder, type InterceptResponse, type InterceptResponseValue, type InterceptTrigger, type IsolationStrategy, type JobHandle, type JobsHandle, type JobsSpecification, type JobsSpecificationOptions, JsonAccessor, type MatchFixtureOptions, type MatchableRequest, Matcher, type MatcherKind, type MockDatePort, type MockPort, Orchestrator, PageResult, type PostgresOptions, type RedisOptions, ResponseAccessor, type ServeOptions, type ServerPort, type ServerResponse, type ServiceHandle, type ServiceRecord, type SpecificationConfig, type SpecificationMode, type SqliteOptions, TableAccessor, TextAccessor, type VisitScenario, type Visitor, type WebsiteHandle, type WebsiteSpecification, type WebsiteSpecificationOptions, anthropic, button, content, defineContract, field, findContainersByLabel, heading, http, inspectContainer, link, match, mockOf, mockOfDate, openai, postgres, redis, removeContainers, specification, sqlite, testId, text };
|
|
2232
|
+
export { type ApiHandle, type ApiSpecification, type ApiSpecificationOptions, BaseResult, type BrowserConsoleMessage, type BrowserLinkElement, type BrowserMetaElement, type BrowserOpenOptions, type BrowserPage, type BrowserPort, type CaptureScope, type CliEnv, type CliHandle, type CliOutput, type CliPort, CliResult, type CliSpecification, type CliSpecificationOptions, ContainerAccessor, type ContainerPort, type DatabaseKeys, type DatabasePort, type DeviceOpenOptions, type DevicePort, type DeviceScreen, DirectoryAccessor, type DockerSpecConfig, type ElementKind, type ElementMatch, type ElementOptions, type ElementRef, type ExecOptions, FetchResult, type FileAccessor, FilesystemAccessor, type HonoApp, HttpResult, type InterceptContract, type InterceptEntry, type InterceptResponder, type InterceptResponse, type InterceptResponseValue, type InterceptTrigger, type IsolationStrategy, type JobHandle, type JobsHandle, type JobsSpecification, type JobsSpecificationOptions, JsonAccessor, type LandmarkKind, type MatchFixtureOptions, type MatchableRequest, Matcher, type MatcherKind, type MobileBackendOptions, type MobileElementKind, type MobileElementMatch, type MobileElementRef, type MobileHandle, type MobileScenario, type MobileSpecification, type MobileSpecificationOptions, type MobileVisitor, type MockDatePort, type MockPort, Orchestrator, PageResult, type PostgresOptions, type RedisOptions, ResponseAccessor, type ScreenNode, ScreenResult, type ServeOptions, type ServerPort, type ServerResponse, type ServiceHandle, type ServiceRecord, type SpecificationConfig, type SpecificationMode, type SqliteOptions, TableAccessor, TextAccessor, type VisitScenario, type Visitor, type WebsiteBackendOptions, type WebsiteHandle, type WebsiteSpecification, type WebsiteSpecificationOptions, anthropic, banner, button, complementary, content, contentinfo, defineContract, field, findContainersByLabel, form, heading, http, inspectContainer, link, main, match, mockOf, mockOfDate, navigation, openai, postgres, redis, region, removeContainers, search, specification, sqlite, testId, text, within };
|