Skip to content

Commit

Permalink
feat(ct): allow unmounting components
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman committed Jul 27, 2022
1 parent c11f594 commit 8df7ab0
Show file tree
Hide file tree
Showing 18 changed files with 113 additions and 14 deletions.
8 changes: 5 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/playwright-ct-react/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export type PlaywrightTestConfig = Omit<BasePlaywrightTestConfig, 'use'> & {

export interface ComponentFixtures {
mount(component: JSX.Element, options?: { hooksConfig?: any }): Promise<Locator>;
unmount(component: Locator): Promise<void>;
}

export const test: TestType<
Expand Down
5 changes: 5 additions & 0 deletions packages/playwright-ct-react/registerSource.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,8 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
for (const hook of /** @type {any} */(window).__pw_hooks_after_mount || [])
await hook({ hooksConfig });
};

window.playwrightUnmount = async (element, rootElement) => {
if (!ReactDOM.unmountComponentAtNode(rootElement))
throw new Error('Component was not mounted');
};
4 changes: 3 additions & 1 deletion packages/playwright-ct-svelte/hooks.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@
* limitations under the License.
*/

import type { SvelteComponent } from "svelte";

export declare function beforeMount(callback: (params: { hooksConfig: any }) => Promise<void>): void;
export declare function afterMount(callback: (params: { hooksConfig: any }) => Promise<void>): void;
export declare function afterMount(callback: (params: { hooksConfig: any, svelteComponent: SvelteComponent }) => Promise<void>): void;
1 change: 1 addition & 0 deletions packages/playwright-ct-svelte/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ interface ComponentFixtures {
on?: { [key: string]: Function },
hooksConfig?: any,
}): Promise<Locator>;
unmount(component: Locator): Promise<void>;
}

export const test: TestType<
Expand Down
5 changes: 4 additions & 1 deletion packages/playwright-ct-svelte/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@
}
},
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"@playwright/test": "1.25.0-next",
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"vite": "^3.0.0"
},
"devDependencies": {
"svelte": "^3.49.0"
}
}
19 changes: 15 additions & 4 deletions packages/playwright-ct-svelte/registerSource.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

/** @typedef {import('../playwright-test/types/component').Component} Component */
/** @typedef {any} FrameworkComponent */
/** @typedef {import('svelte').SvelteComponent} SvelteComponent */

/** @type {Map<string, FrameworkComponent>} */
const registry = new Map();
Expand Down Expand Up @@ -54,14 +55,24 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
for (const hook of /** @type {any} */(window).__pw_hooks_before_mount || [])
await hook({ hooksConfig });

const wrapper = new componentCtor({
const svelteComponent = /** @type {SvelteComponent} */ (new componentCtor({
target: rootElement,
props: component.options?.props,
});
}));
rootElement[svelteComponentKey] = svelteComponent;

for (const hook of /** @type {any} */(window).__pw_hooks_after_mount || [])
await hook({ hooksConfig });
await hook({ hooksConfig, svelteComponent });

for (const [key, listener] of Object.entries(component.options?.on || {}))
wrapper.$on(key, event => listener(event.detail));
svelteComponent.$on(key, event => listener(event.detail));
};

window.playwrightUnmount = async (element, rootElement) => {
const svelteComponent = /** @type {SvelteComponent} */ (rootElement[svelteComponentKey]);
if (!svelteComponent)
throw new Error('Component was not mounted');
svelteComponent.$destroy();
};

const svelteComponentKey = Symbol('svelteComponent');
1 change: 1 addition & 0 deletions packages/playwright-ct-vue/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface ComponentFixtures {
on?: { [key: string]: Function },
hooksConfig?: any,
}): Promise<Locator>;
unmount(locator: Locator): Promise<void>;
}

export const test: TestType<
Expand Down
10 changes: 10 additions & 0 deletions packages/playwright-ct-vue/registerSource.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,16 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
for (const hook of /** @type {any} */(window).__pw_hooks_before_mount || [])
await hook({ app, hooksConfig });
const instance = app.mount(rootElement);
instance.$el[appKey] = app;
for (const hook of /** @type {any} */(window).__pw_hooks_after_mount || [])
await hook({ app, hooksConfig, instance });
};

window.playwrightUnmount = async element => {
const app = /** @type {import('vue').App} */ (element[appKey]);
if (!app)
throw new Error('Component was not mounted');
app.unmount();
};

const appKey = Symbol('appKey');
1 change: 1 addition & 0 deletions packages/playwright-ct-vue2/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface ComponentFixtures {
on?: { [key: string]: Function },
hooksConfig?: any,
}): Promise<Locator>;
unmount(locator: Locator): Promise<void>;
}

export const test: TestType<
Expand Down
13 changes: 11 additions & 2 deletions packages/playwright-ct-vue2/registerSource.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,25 @@ function render(component, h) {
}

window.playwrightMount = async (component, rootElement, hooksConfig) => {
const config = hooksConfig || /** @type {any} */(component).options?.hooksConfig;

for (const hook of /** @type {any} */(window).__pw_hooks_before_mount || [])
await hook({ hooksConfig });

const instance = new Vue({
render: h => render(component, h),
}).$mount();
rootElement.appendChild(instance.$el);
/** @type {any} */ (instance.$el)[instanceKey] = instance;

for (const hook of /** @type {any} */(window).__pw_hooks_after_mount || [])
await hook({ hooksConfig, instance });
};

window.playwrightUnmount = async element => {
const component = /** @type {any} */(element)[instanceKey];
if (!component)
throw new Error('Component was not mounted');
component.$destroy();
element.remove();
};

const instanceKey = Symbol('instanceKey');
14 changes: 13 additions & 1 deletion packages/playwright-test/src/mount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import type { Component, JsxComponent, ObjectComponentOptions } from '../types/c
let boundCallbacksForMount: Function[] = [];

export const fixtures: Fixtures<
PlaywrightTestArgs & PlaywrightTestOptions & { mount: (component: any, options: any) => Promise<Locator> },
PlaywrightTestArgs & PlaywrightTestOptions & {
mount: (component: any, options: any) => Promise<Locator>;
unmount: (locator: Locator) => Promise<void>;
},
PlaywrightWorkerArgs & PlaywrightWorkerOptions & { _ctWorker: { context: BrowserContext | undefined, hash: string } },
{ _contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>, _contextReuseEnabled: boolean }> = {

Expand Down Expand Up @@ -49,6 +52,15 @@ export const fixtures: Fixtures<
});
boundCallbacksForMount = [];
},

unmount: async ({}, use) => {
await use(async (locator: Locator) => {
await locator.evaluate(async element => {
const rootElement = document.getElementById('root')!;
await window.playwrightUnmount(element, rootElement);
});
});
},
};

async function innerMount(page: Page, jsxOrType: JsxComponent | string, options: ObjectComponentOptions = {}): Promise<string> {
Expand Down
1 change: 1 addition & 0 deletions packages/playwright-test/types/component.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@ export type Component = JsxComponent | ObjectComponent;
declare global {
interface Window {
playwrightMount(component: Component, rootElement: Element, hooksConfig: any): Promise<void>;
playwrightUnmount(element: Element, rootElement: Element): Promise<void>;
}
}
7 changes: 7 additions & 0 deletions tests/components/ct-react-vite/src/App.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,10 @@ test('should configure app', async ({ page, mount }) => {
});
expect(messages).toEqual(['Before mount: {\"route\":\"A\"}', 'After mount']);
});

test('should unmount', async ({ page, mount, unmount }) => {
const component = await mount(<App></App>);
await expect(page.locator('#root')).toContainText('Hello Vite + React!');
await unmount(component);
await expect(page.locator('#root')).not.toContainText('Hello Vite + React!');
});
4 changes: 2 additions & 2 deletions tests/components/ct-svelte-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"@tsconfig/svelte": "^2.0.1",
"svelte": "^3.44.0",
"svelte": "^3.49.0",
"svelte-check": "^2.2.7",
"svelte-preprocess": "^4.9.8",
"tslib": "^2.3.1",
Expand All @@ -23,4 +23,4 @@
"@playwright/experimental-ct-svelte": "^1.22.2",
"@playwright/test": "^1.22.2"
}
}
}
11 changes: 11 additions & 0 deletions tests/components/ct-svelte-vite/src/lib/Counter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,14 @@ test('should configure app', async ({ page, mount }) => {
});
expect(messages).toEqual(['Before mount: {\"route\":\"A\"}', 'After mount']);
});

test('should unmount', async ({ page, mount, unmount }) => {
const component = await mount(Counter, {
props: {
suffix: 'my suffix',
},
});
await expect(page.locator('#root')).toContainText('my suffix')
await unmount(component);
await expect(page.locator('#root')).not.toContainText('my suffix');
});
11 changes: 11 additions & 0 deletions tests/components/ct-vue-vite/src/notation-vue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,14 @@ test('should run hooks', async ({ page, mount }) => {
})
expect(messages).toEqual(['Before mount: {\"route\":\"A\"}, app: true', 'After mount el: HTMLButtonElement'])
})

test('should unmount', async ({ page, mount, unmount }) => {
const component = await mount(Button, {
props: {
title: 'Submit'
}
})
await expect(page.locator('#root')).toContainText('Submit')
await unmount(component);
await expect(page.locator('#root')).not.toContainText('Submit');
});
11 changes: 11 additions & 0 deletions tests/components/ct-vue2-cli/src/notation-vue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,14 @@ test('should run hooks', async ({ page, mount }) => {
})
expect(messages).toEqual(['Before mount: {\"route\":\"A\"}', 'After mount el: HTMLButtonElement'])
})

test('should unmount', async ({ page, mount, unmount }) => {
const component = await mount(Button, {
props: {
title: 'Submit'
}
})
await expect(page.locator('#root')).toContainText('Submit')
await unmount(component);
await expect(page.locator('#root')).not.toContainText('Submit');
});

0 comments on commit 8df7ab0

Please sign in to comment.