feat(ct): resolve hooksConfig import refs (#31024)

closes https://github.com/microsoft/playwright/issues/30453
This commit is contained in:
Sander 2024-05-28 21:29:52 +02:00 committed by GitHub
parent 67181c9f11
commit e047c478a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 51 additions and 58 deletions

View file

@ -102,6 +102,7 @@ async function innerMount(page: Page, componentRef: JsxComponent | ImportRef, op
const selector = await page.evaluate(async ({ component, hooksConfig }) => { const selector = await page.evaluate(async ({ component, hooksConfig }) => {
component = await window.__pwUnwrapObject(component); component = await window.__pwUnwrapObject(component);
hooksConfig = await window.__pwUnwrapObject(hooksConfig);
let rootElement = document.getElementById('root'); let rootElement = document.getElementById('root');
if (!rootElement) { if (!rootElement) {
rootElement = document.createElement('div'); rootElement = document.createElement('div');

View file

@ -14,11 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
type JsonPrimitive = string | number | boolean | null;
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
type JsonArray = JsonValue[];
export type JsonObject = { [Key in string]?: JsonValue };
export type JsxComponent = { export type JsxComponent = {
__pw_type: 'jsx', __pw_type: 'jsx',
type: any, type: any,
@ -47,10 +42,10 @@ declare global {
playwrightMount(component: Component, rootElement: Element, hooksConfig?: any): Promise<void>; playwrightMount(component: Component, rootElement: Element, hooksConfig?: any): Promise<void>;
playwrightUnmount(rootElement: Element): Promise<void>; playwrightUnmount(rootElement: Element): Promise<void>;
playwrightUpdate(rootElement: Element, component: Component): Promise<void>; playwrightUpdate(rootElement: Element, component: Component): Promise<void>;
__pw_hooks_before_mount?: (<HooksConfig extends JsonObject = JsonObject>( __pw_hooks_before_mount?: (<HooksConfig>(
params: { hooksConfig?: HooksConfig; [key: string]: any } params: { hooksConfig?: HooksConfig; [key: string]: any }
) => Promise<any>)[]; ) => Promise<any>)[];
__pw_hooks_after_mount?: (<HooksConfig extends JsonObject = JsonObject>( __pw_hooks_after_mount?: (<HooksConfig>(
params: { hooksConfig?: HooksConfig; [key: string]: any } params: { hooksConfig?: HooksConfig; [key: string]: any }
) => Promise<void>)[]; ) => Promise<void>)[];
// Can't start with __pw due to core reuse bindings logic for __pw*. // Can't start with __pw due to core reuse bindings logic for __pw*.

View file

@ -14,11 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
import type { JsonObject } from '@playwright/experimental-ct-core/types/component'; export declare function beforeMount<HooksConfig>(
export declare function beforeMount<HooksConfig extends JsonObject>(
callback: (params: { hooksConfig?: HooksConfig; App: () => JSX.Element }) => Promise<void | JSX.Element> callback: (params: { hooksConfig?: HooksConfig; App: () => JSX.Element }) => Promise<void | JSX.Element>
): void; ): void;
export declare function afterMount<HooksConfig extends JsonObject>( export declare function afterMount<HooksConfig>(
callback: (params: { hooksConfig?: HooksConfig }) => Promise<void> callback: (params: { hooksConfig?: HooksConfig }) => Promise<void>
): void; ): void;

View file

@ -15,10 +15,9 @@
*/ */
import type { Locator } from 'playwright/test'; import type { Locator } from 'playwright/test';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
import type { TestType } from '@playwright/experimental-ct-core'; import type { TestType } from '@playwright/experimental-ct-core';
export interface MountOptions<HooksConfig extends JsonObject> { export interface MountOptions<HooksConfig> {
hooksConfig?: HooksConfig; hooksConfig?: HooksConfig;
} }
@ -28,7 +27,7 @@ export interface MountResult extends Locator {
} }
export const test: TestType<{ export const test: TestType<{
mount<HooksConfig extends JsonObject>( mount<HooksConfig>(
component: JSX.Element, component: JSX.Element,
options?: MountOptions<HooksConfig> options?: MountOptions<HooksConfig>
): Promise<MountResult>; ): Promise<MountResult>;

View file

@ -14,11 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
import type { JsonObject } from '@playwright/experimental-ct-core/types/component'; export declare function beforeMount<HooksConfig>(
export declare function beforeMount<HooksConfig extends JsonObject>(
callback: (params: { hooksConfig?: HooksConfig; App: () => JSX.Element }) => Promise<void | JSX.Element> callback: (params: { hooksConfig?: HooksConfig; App: () => JSX.Element }) => Promise<void | JSX.Element>
): void; ): void;
export declare function afterMount<HooksConfig extends JsonObject>( export declare function afterMount<HooksConfig>(
callback: (params: { hooksConfig?: HooksConfig }) => Promise<void> callback: (params: { hooksConfig?: HooksConfig }) => Promise<void>
): void; ): void;

View file

@ -15,10 +15,9 @@
*/ */
import type { Locator } from 'playwright/test'; import type { Locator } from 'playwright/test';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
import type { TestType } from '@playwright/experimental-ct-core'; import type { TestType } from '@playwright/experimental-ct-core';
export interface MountOptions<HooksConfig extends JsonObject> { export interface MountOptions<HooksConfig> {
hooksConfig?: HooksConfig; hooksConfig?: HooksConfig;
} }
@ -28,7 +27,7 @@ export interface MountResult extends Locator {
} }
export const test: TestType<{ export const test: TestType<{
mount<HooksConfig extends JsonObject>( mount<HooksConfig>(
component: JSX.Element, component: JSX.Element,
options?: MountOptions<HooksConfig> options?: MountOptions<HooksConfig>
): Promise<MountResult>; ): Promise<MountResult>;

View file

@ -14,12 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */
import { JSXElement } from "solid-js"; import { JSXElement } from 'solid-js';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
export declare function beforeMount<HooksConfig extends JsonObject>( export declare function beforeMount<HooksConfig>(
callback: (params: { hooksConfig?: HooksConfig, App: () => JSXElement }) => Promise<void | JSXElement> callback: (params: { hooksConfig?: HooksConfig, App: () => JSXElement }) => Promise<void | JSXElement>
): void; ): void;
export declare function afterMount<HooksConfig extends JsonObject>( export declare function afterMount<HooksConfig>(
callback: (params: { hooksConfig?: HooksConfig }) => Promise<void> callback: (params: { hooksConfig?: HooksConfig }) => Promise<void>
): void; ): void;

View file

@ -15,10 +15,9 @@
*/ */
import type { Locator } from 'playwright/test'; import type { Locator } from 'playwright/test';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
import type { TestType } from '@playwright/experimental-ct-core'; import type { TestType } from '@playwright/experimental-ct-core';
export interface MountOptions<HooksConfig extends JsonObject> { export interface MountOptions<HooksConfig> {
hooksConfig?: HooksConfig; hooksConfig?: HooksConfig;
} }
@ -28,7 +27,7 @@ export interface MountResult extends Locator {
} }
export const test: TestType<{ export const test: TestType<{
mount<HooksConfig extends JsonObject>( mount<HooksConfig>(
component: JSX.Element, component: JSX.Element,
options?: MountOptions<HooksConfig> options?: MountOptions<HooksConfig>
): Promise<MountResult>; ): Promise<MountResult>;

View file

@ -15,15 +15,14 @@
*/ */
import type { ComponentConstructorOptions, SvelteComponent } from 'svelte'; import type { ComponentConstructorOptions, SvelteComponent } from 'svelte';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
export declare function beforeMount<HooksConfig extends JsonObject>( export declare function beforeMount<HooksConfig>(
callback: (params: { callback: (params: {
hooksConfig?: HooksConfig, hooksConfig?: HooksConfig,
App: new (options: Partial<ComponentConstructorOptions>) => SvelteComponent App: new (options: Partial<ComponentConstructorOptions>) => SvelteComponent
}) => Promise<SvelteComponent | void> }) => Promise<SvelteComponent | void>
): void; ): void;
export declare function afterMount<HooksConfig extends JsonObject>( export declare function afterMount<HooksConfig>(
callback: (params: { callback: (params: {
hooksConfig?: HooksConfig; hooksConfig?: HooksConfig;
svelteComponent: SvelteComponent; svelteComponent: SvelteComponent;

View file

@ -15,7 +15,6 @@
*/ */
import type { Locator } from 'playwright/test'; import type { Locator } from 'playwright/test';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
import type { SvelteComponent, ComponentProps } from 'svelte/types/runtime'; import type { SvelteComponent, ComponentProps } from 'svelte/types/runtime';
import type { TestType } from '@playwright/experimental-ct-core'; import type { TestType } from '@playwright/experimental-ct-core';
@ -23,7 +22,7 @@ type ComponentSlot = string | string[];
type ComponentSlots = Record<string, ComponentSlot> & { default?: ComponentSlot }; type ComponentSlots = Record<string, ComponentSlot> & { default?: ComponentSlot };
type ComponentEvents = Record<string, Function>; type ComponentEvents = Record<string, Function>;
export interface MountOptions<HooksConfig extends JsonObject, Component extends SvelteComponent> { export interface MountOptions<HooksConfig, Component extends SvelteComponent> {
props?: ComponentProps<Component>; props?: ComponentProps<Component>;
slots?: ComponentSlots; slots?: ComponentSlots;
on?: ComponentEvents; on?: ComponentEvents;
@ -39,7 +38,7 @@ export interface MountResult<Component extends SvelteComponent> extends Locator
} }
export const test: TestType<{ export const test: TestType<{
mount<HooksConfig extends JsonObject, Component extends SvelteComponent = SvelteComponent>( mount<HooksConfig, Component extends SvelteComponent = SvelteComponent>(
component: new (...args: any[]) => Component, component: new (...args: any[]) => Component,
options?: MountOptions<HooksConfig, Component> options?: MountOptions<HooksConfig, Component>
): Promise<MountResult<Component>>; ): Promise<MountResult<Component>>;

View file

@ -15,12 +15,11 @@
*/ */
import type { App, ComponentPublicInstance } from 'vue'; import type { App, ComponentPublicInstance } from 'vue';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
export declare function beforeMount<HooksConfig extends JsonObject>( export declare function beforeMount<HooksConfig>(
callback: (params: { app: App; hooksConfig?: HooksConfig }) => Promise<void> callback: (params: { app: App; hooksConfig?: HooksConfig }) => Promise<void>
): void; ): void;
export declare function afterMount<HooksConfig extends JsonObject>( export declare function afterMount<HooksConfig>(
callback: (params: { callback: (params: {
app: App; app: App;
hooksConfig?: HooksConfig; hooksConfig?: HooksConfig;

View file

@ -15,7 +15,6 @@
*/ */
import type { Locator } from 'playwright/test'; import type { Locator } from 'playwright/test';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
import type { TestType } from '@playwright/experimental-ct-core'; import type { TestType } from '@playwright/experimental-ct-core';
type ComponentSlot = string | string[]; type ComponentSlot = string | string[];
@ -29,14 +28,14 @@ type ComponentProps<T> =
T extends (props: infer P, ...args: any) => any ? P : T extends (props: infer P, ...args: any) => any ? P :
{}; {};
export interface MountOptions<HooksConfig extends JsonObject, Component> { export interface MountOptions<HooksConfig, Component> {
props?: ComponentProps<Component>; props?: ComponentProps<Component>;
slots?: ComponentSlots; slots?: ComponentSlots;
on?: ComponentEvents; on?: ComponentEvents;
hooksConfig?: HooksConfig; hooksConfig?: HooksConfig;
} }
export interface MountOptionsJsx<HooksConfig extends JsonObject> { export interface MountOptionsJsx<HooksConfig> {
hooksConfig?: HooksConfig; hooksConfig?: HooksConfig;
} }
@ -55,11 +54,11 @@ export interface MountResultJsx extends Locator {
} }
export const test: TestType<{ export const test: TestType<{
mount<HooksConfig extends JsonObject>( mount<HooksConfig>(
component: JSX.Element, component: JSX.Element,
options: MountOptionsJsx<HooksConfig> options: MountOptionsJsx<HooksConfig>
): Promise<MountResultJsx>; ): Promise<MountResultJsx>;
mount<HooksConfig extends JsonObject, Component = unknown>( mount<HooksConfig, Component = unknown>(
component: Component, component: Component,
options?: MountOptions<HooksConfig, Component> options?: MountOptions<HooksConfig, Component>
): Promise<MountResult<Component>>; ): Promise<MountResult<Component>>;

View file

@ -14,17 +14,16 @@
* limitations under the License. * limitations under the License.
*/ */
import { ComponentOptions } from 'vue'; import type { ComponentOptions } from 'vue';
import { CombinedVueInstance, Vue, VueConstructor } from 'vue/types/vue'; import type { CombinedVueInstance, Vue, VueConstructor } from 'vue/types/vue';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
export declare function beforeMount<HooksConfig extends JsonObject>( export declare function beforeMount<HooksConfig>(
callback: (params: { callback: (params: {
hooksConfig?: HooksConfig, hooksConfig?: HooksConfig,
Vue: VueConstructor<Vue>, Vue: VueConstructor<Vue>,
}) => Promise<void | ComponentOptions<Vue> & Record<string, unknown>> }) => Promise<void | ComponentOptions<Vue> & Record<string, unknown>>
): void; ): void;
export declare function afterMount<HooksConfig extends JsonObject>( export declare function afterMount<HooksConfig>(
callback: (params: { callback: (params: {
hooksConfig?: HooksConfig; hooksConfig?: HooksConfig;
instance: CombinedVueInstance< instance: CombinedVueInstance<

View file

@ -15,7 +15,6 @@
*/ */
import type { Locator } from 'playwright/test'; import type { Locator } from 'playwright/test';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
import type { TestType } from '@playwright/experimental-ct-core'; import type { TestType } from '@playwright/experimental-ct-core';
type Slot = string | string[]; type Slot = string | string[];
@ -29,14 +28,14 @@ type ComponentProps<T> =
T extends (props: infer P, ...args: any) => any ? P : T extends (props: infer P, ...args: any) => any ? P :
{}; {};
export interface MountOptions<HooksConfig extends JsonObject, Component> { export interface MountOptions<HooksConfig, Component> {
props?: ComponentProps<Component>; props?: ComponentProps<Component>;
slots?: ComponentSlots; slots?: ComponentSlots;
on?: ComponentEvents; on?: ComponentEvents;
hooksConfig?: HooksConfig; hooksConfig?: HooksConfig;
} }
export interface MountOptionsJsx<HooksConfig extends JsonObject> { export interface MountOptionsJsx<HooksConfig> {
hooksConfig?: HooksConfig; hooksConfig?: HooksConfig;
} }
@ -55,11 +54,11 @@ export interface MountResultJsx extends Locator {
} }
export const test: TestType<{ export const test: TestType<{
mount<HooksConfig extends JsonObject>( mount<HooksConfig>(
component: JSX.Element, component: JSX.Element,
options?: MountOptionsJsx<HooksConfig> options?: MountOptionsJsx<HooksConfig>
): Promise<MountResultJsx>; ): Promise<MountResultJsx>;
mount<HooksConfig extends JsonObject, Component = unknown>( mount<HooksConfig, Component = unknown>(
component: Component, component: Component,
options?: MountOptions<HooksConfig, Component> options?: MountOptions<HooksConfig, Component>
): Promise<MountResult<Component>>; ): Promise<MountResult<Component>>;

View file

@ -4,14 +4,17 @@ import Button from '../src/components/Button.vue';
import '../src/assets/index.css'; import '../src/assets/index.css';
export type HooksConfig = { export type HooksConfig = {
route?: string;
routing?: boolean; routing?: boolean;
components?: Record<string, any>;
} }
beforeMount<HooksConfig>(async ({ app, hooksConfig }) => { beforeMount<HooksConfig>(async ({ app, hooksConfig }) => {
if (hooksConfig?.routing) if (hooksConfig?.routing)
app.use(router as any); // TODO: remove any and fix the various installed conflicting Vue versions app.use(router as any); // TODO: remove any and fix the various installed conflicting Vue versions
app.component('Button', Button);
for (const [name, component] of Object.entries(hooksConfig?.components || {}))
app.component(name, component);
console.log(`Before mount: ${JSON.stringify(hooksConfig)}, app: ${!!app}`); console.log(`Before mount: ${JSON.stringify(hooksConfig)}, app: ${!!app}`);
}); });

View file

@ -1,6 +1,7 @@
import { test, expect } from '@playwright/experimental-ct-vue'; import { test, expect } from '@playwright/experimental-ct-vue';
import DefaultSlot from '@/components/DefaultSlot.vue'; import DefaultSlot from '@/components/DefaultSlot.vue';
import NamedSlots from '@/components/NamedSlots.vue'; import NamedSlots from '@/components/NamedSlots.vue';
import Button from '@/components/Button.vue';
test('render a default slot', async ({ mount }) => { test('render a default slot', async ({ mount }) => {
const component = await mount(DefaultSlot, { const component = await mount(DefaultSlot, {
@ -16,6 +17,9 @@ test('render a component as slot', async ({ mount }) => {
slots: { slots: {
default: '<Button title="Submit" />', // component is registered globally in /playwright/index.ts default: '<Button title="Submit" />', // component is registered globally in /playwright/index.ts
}, },
hooksConfig: {
components: { Button }
}
}); });
await expect(component).toContainText('Submit'); await expect(component).toContainText('Submit');
}); });

View file

@ -1,6 +1,7 @@
import { test, expect } from '@playwright/experimental-ct-vue'; import { test, expect } from '@playwright/experimental-ct-vue';
import DefaultSlot from '@/components/DefaultSlot.vue'; import DefaultSlot from '@/components/DefaultSlot.vue';
import NamedSlots from '@/components/NamedSlots.vue'; import NamedSlots from '@/components/NamedSlots.vue';
import Button from '@/components/Button.vue';
test('render a default slot', async ({ mount }) => { test('render a default slot', async ({ mount }) => {
const component = await mount(DefaultSlot, { const component = await mount(DefaultSlot, {
@ -16,6 +17,9 @@ test('render a component as slot', async ({ mount }) => {
slots: { slots: {
default: '<Button title="Submit" />', // component is registered globally in /playwright/index.ts default: '<Button title="Submit" />', // component is registered globally in /playwright/index.ts
}, },
hooksConfig: {
components: { Button }
}
}); });
await expect(component).toContainText('Submit'); await expect(component).toContainText('Submit');
}); });