/** * SPDX-License-Identifier: EUPL-1.2 * SPDX-FileCopyrightText: 2026 Conduction B.V. * * Types for `@conduction/nextcloud-vue/testing/playwright`. * * `page` is typed structurally rather than as `import('@playwright/test').Page` * on purpose: the runtime module has no dependency on `@playwright/test` (see * the module docblock in `playwright.js`), and importing the type here would * reintroduce exactly that coupling for TypeScript consumers — a spec repo * without Playwright installed would fail to type-check a module it can * legitimately use with `playwright-core`. The structural type covers every * method the helpers actually call, so a real `Page` satisfies it. */ /** Minimal structural stand-in for Playwright's `Locator`. */ export interface CnTestLocator { first(): CnTestLocator click(options?: Record): Promise waitFor(options?: Record): Promise isVisible(options?: Record): Promise getByRole(role: string, options?: Record): CnTestLocator } /** Minimal structural stand-in for Playwright's `Page`. */ export interface CnTestPage { locator(selector: string): CnTestLocator evaluate(pageFunction: string | ((...args: any[]) => R), arg?: any): Promise addInitScript(script: ((...args: any[]) => void) | string, arg?: any): Promise keyboard: { press(key: string): Promise } } /** * Minimal structural stand-in for Playwright's `BrowserContext`. * * Accepted by the seeding helpers so a seed can cover every page the context * opens AND survive into `storageState()` — which the page-scoped match-all * form cannot do. See {@link seedFirstVisitOverlaysSeen}. */ export interface CnTestBrowserContext { addInitScript(script: ((...args: any[]) => void) | string, arg?: any): Promise pages(): CnTestPage[] newPage(): Promise } /** Anything the seeding helpers can be pointed at. */ export type CnSeedTarget = CnTestPage | CnTestBrowserContext /** Options for {@link appDialog}. */ export interface CnAppDialogOptions { /** Extra selectors treated as chrome, added to `CHROME_DIALOG_SELECTORS`. */ exclude?: string[] /** Return the full match set instead of `.first()`. */ all?: boolean } /** Outcome of {@link retireFirstRunWizard}. */ export interface CnWizardRetirement { /** HTTP status of the DELETE, or `-1` when the request itself threw. */ status: number /** * False when the firstrunwizard app is not installed (404); `null` when the * status could not tell us — a 401 is answered by Nextcloud's auth layer * before the wizard app is ever consulted, so both booleans would be * inventions. */ installed: boolean | null /** * True when no wizard will block clicks — a 2xx, a 404, or a guest surface * where the per-user wizard could never have rendered at all. */ cleared: boolean /** * True when the wizard could not exist on this surface, so nothing was * dismissed and nothing needed to be. Assert `false` when a spec means to * prove a REAL dismissal happened. */ notApplicable: boolean /** Why, when `notApplicable` — currently `'no-user-session'`. */ reason: string | null } /** Outcome of {@link dismissFirstVisitOverlays}. */ export interface CnOverlayDismissal { /** * True on a Nextcloud GUEST surface: nc-vue mounts `CnWalkthrough` and * `CnSupportDialog` from `CnAppRoot`, and a logged-out page has no app root, * so there is nothing to clear. The call short-circuits instead of spending * two timeouts polling for elements that cannot appear. */ notApplicable: boolean /** Why, when `notApplicable` — currently `'guest-surface'`. */ reason: string | null /** True when a walkthrough tour was actually closed. */ walkthroughDismissed: boolean /** How many support dialogs were actually closed (nested roots raise more). */ supportDialogsDismissed: number } /** What kind of Nextcloud surface a page is — see {@link guestSurfaceStatus}. */ export interface CnSurfaceStatus { /** A Nextcloud page, with no user session and no mounted `CnAppRoot`. */ guest: boolean /** The logged-in user id, or `null`. */ user: string | null /** Whether the page carries a Nextcloud request token at all. */ isNextcloudPage: boolean /** `appId` of every mounted `CnAppRoot`, outer shell first. */ appRoots: string[] } /** One mounted component instance, reduced to a JSON-safe shape. */ export interface CnMountedComponent { /** The component's `name` option, or the `