import { Optional } from './dataTypes'; import { ComponentDriver } from './drivers/ComponentDriver'; import { ListComponentDriver, ListComponentDriverSpecificOption } from './drivers/ListComponentDriver'; import { WaitForOption } from './drivers/WaitForOption'; import { Interactor } from './interactor'; import type { LocatorRelativePosition } from './locators/LocatorRelativePosition'; import { PartLocator } from './locators/PartLocator'; export type PartName = keyof T; /** * Constructor type for a {@link ComponentDriver}, exported publicly so packages * can reference it without importing from internal paths. It carries both the * construct signature and the static portal hooks every driver class inherits, so * concrete driver classes structurally match it. * * The `any` in the constraint `T extends ComponentDriver` is load-bearing: * it lets the type accept any concrete driver regardless of its ScenePart type * parameter. The option position, by contrast, is honestly typed to * {@link ScenePart} — every driver's parts ultimately satisfy that constraint. */ export type ComponentDriverCtor> = { new (locator: PartLocator, interactor: Interactor, option?: Partial>): T; overriddenParentLocator(option?: Partial>): Optional; overrideLocatorRelativePosition(option?: Partial>): Optional; }; export interface ComponentPartDefinition { /** * The locator of the part */ locator: PartLocator; /** * The class of driver which is used to interact with the element */ driver: { new (locator: PartLocator, interactor: Interactor, option?: Partial>): ComponentDriver; }; option?: Partial>; } /** * Definition for a list component part. The `any` in `ItemT extends ComponentDriver` * is necessary because ItemT represents the item driver type, and we need to accept * any item driver regardless of its ScenePart type parameter. */ export interface ListComponentPartDefinition> { /** * The locator of the part */ locator: PartLocator; /** * The class of driver which is used to interact with the element */ driver: | typeof ListComponentDriver | (new ( locator: PartLocator, interactor: Interactor, option: ListComponentDriverSpecificOption & Partial> ) => ListComponentDriver); /** * Option for the driver */ option: ListComponentDriverSpecificOption & Partial>; } /** * The forms a named part in a {@link ScenePart} may take. * * A component whose interior is caller-supplied is an ordinary * {@link ComponentPartDefinition}; its interior is reached at call time through * {@link ComponentDriver.within} rather than declared as a second parts channel * (ADR-019). */ export type ScenePartDefinition = | ComponentPartDefinition | ListComponentPartDefinition>; /** * Part name to driver definition map */ export interface ScenePart extends Record {} export type ScenePartDriver = { [partName in keyof T]: InstanceType; }; /** * The driver-constructor shape a composite driver must present to be placeable in * a parent {@link ScenePart}: the `driver` field of {@link ComponentPartDefinition}. * * Exposed so a package can lock its composite drivers against the contravariant * authoring rule documented on {@link ComponentDriver} without copying a full * type-test fixture — see {@link AssertScenePlaceableDriver}. */ export type ScenePlaceableDriverCtor = ComponentPartDefinition['driver']; /** * Compile-time lock for the composite-driver authoring rule. Resolves to `Ctor` * when it satisfies {@link ScenePlaceableDriverCtor} and errors at the type * argument otherwise, so a package turns the rule into a build error in one line: * * ```ts * // MyCompositeDriver must stay scene-placeable * type _Lock = AssertScenePlaceableDriver; * ``` * * Because constructor parameters are checked contravariantly, a driver whose * option parameter is typed the "natural" `Partial>` way fails this assertion — exactly the trap the rule guards against * (see {@link ComponentDriver}). This is the reusable form of the type-test that * `@atomic-testing/component-driver-html` ships as precedent. */ export type AssertScenePlaceableDriver = Ctor; export interface IComponentDriverOption { parts: T; } /** * The shared, component-agnostic slice of an {@link IComponentDriverOption} that * flows unchanged down the driver tree — every field EXCEPT the component-specific * `parts`. A parent driver hands this to the constructors of the children it * creates dynamically (the list helpers, `ComponentDriver.commutableOption`), so * it deliberately carries no `parts`: each child owns its own. * * Today {@link IComponentDriverOption} has only `parts`, so this resolves to an * empty object — which is exactly the honest shape, and why * `ComponentDriver.commutableOption` no longer fakes a `parts: {} as T` payload. * It is the single home for future universal options (mirroring * {@link ITestEngineOption}'s intent), which it will pick up automatically. */ export type CommutableComponentDriverOption = Omit; /** * Shared base option for the framework adapters' `createTestEngine` entry points. * Defined once in core so every adapter option type stays in lockstep and future * universal bootstrap options have a single home. */ export interface ITestEngineOption extends IComponentDriverOption { /** * Element to host the rendered subject. Defaults to `document.body` for the * in-DOM adapters (DOM/React/Vue); ignored by out-of-process adapters such as * Playwright. */ rootElement?: Element; } export interface IComponentDriver { /** * Return driver instance of all the named parts */ readonly parts: ScenePartDriver; /** * The locator which helps locate the root of the component */ readonly locator: PartLocator; /** * Get the combined text content of the component * @returns If the component exists and has content, it should return the text or otherwise undefined */ getText(): Promise>; /** * Whether the component exists/attached to the DOM * @returns true if the component is attached to the DOM, false otherwise */ exists(): Promise; /** * Whether the component is visible. Visibility is defined * that the component does not have the CSS property `display: none`, * `visibility: hidden`, or `opacity: 0`. However this does not * check whether the component is within the viewport. * * @returns true if the component is visible, false otherwise */ isVisible(): Promise; /** * Wait until the component is in the expected state such as * the component's visibility or existence. If the component has * not reached the expected state within the timeout, it will throw * an error. * * By default it waits until the component is attached to the DOM * within 30 seconds. * * @param option The option to configure the wait behavior */ waitUntilComponentState(option?: Partial>): Promise; } export interface ITestEngine extends IComponentDriver { cleanUp(): Promise; }