fix(ct): multi root vue components (#16211)

This commit is contained in:
sand4rt 2022-08-04 19:43:43 +02:00 committed by GitHub
parent 2fa48483af
commit bd5eddd62e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 64 additions and 19 deletions

View File

@ -78,7 +78,7 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
await hook({ hooksConfig }); await hook({ hooksConfig });
}; };
window.playwrightUnmount = async (element, rootElement) => { window.playwrightUnmount = async rootElement => {
if (!ReactDOM.unmountComponentAtNode(rootElement)) if (!ReactDOM.unmountComponentAtNode(rootElement))
throw new Error('Component was not mounted'); throw new Error('Component was not mounted');
}; };

View File

@ -68,7 +68,7 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
svelteComponent.$on(key, event => listener(event.detail)); svelteComponent.$on(key, event => listener(event.detail));
}; };
window.playwrightUnmount = async (element, rootElement) => { window.playwrightUnmount = async rootElement => {
const svelteComponent = /** @type {SvelteComponent} */ (rootElement[svelteComponentKey]); const svelteComponent = /** @type {SvelteComponent} */ (rootElement[svelteComponentKey]);
if (!svelteComponent) if (!svelteComponent)
throw new Error('Component was not mounted'); throw new Error('Component was not mounted');

View File

@ -168,21 +168,22 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
for (const hook of /** @type {any} */(window).__pw_hooks_before_mount || []) for (const hook of /** @type {any} */(window).__pw_hooks_before_mount || [])
await hook({ app, hooksConfig }); await hook({ app, hooksConfig });
const instance = app.mount(rootElement); const instance = app.mount(rootElement);
instance.$el[appKey] = app; rootElement[appKey] = app;
instance.$el[componentKey] = wrapper; rootElement[componentKey] = wrapper;
for (const hook of /** @type {any} */(window).__pw_hooks_after_mount || []) for (const hook of /** @type {any} */(window).__pw_hooks_after_mount || [])
await hook({ app, hooksConfig, instance }); await hook({ app, hooksConfig, instance });
}; };
window.playwrightUnmount = async element => { window.playwrightUnmount = async rootElement => {
const app = /** @type {import('vue').App} */ (element[appKey]); const app = /** @type {import('vue').App} */ (rootElement[appKey]);
if (!app) if (!app)
throw new Error('Component was not mounted'); throw new Error('Component was not mounted');
app.unmount(); app.unmount();
}; };
window.playwrightRerender = async (element, options) => { window.playwrightRerender = async (rootElement, options) => {
const component = element[componentKey].component; const component = rootElement[componentKey].component;
if (!component) if (!component)
throw new Error('Component was not mounted'); throw new Error('Component was not mounted');

View File

@ -134,6 +134,8 @@ function render(component, h) {
return wrapper; return wrapper;
} }
const instanceKey = Symbol('instanceKey');
window.playwrightMount = async (component, rootElement, hooksConfig) => { window.playwrightMount = async (component, rootElement, hooksConfig) => {
for (const hook of /** @type {any} */(window).__pw_hooks_before_mount || []) for (const hook of /** @type {any} */(window).__pw_hooks_before_mount || [])
await hook({ hooksConfig }); await hook({ hooksConfig });
@ -142,18 +144,16 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
render: h => render(component, h), render: h => render(component, h),
}).$mount(); }).$mount();
rootElement.appendChild(instance.$el); rootElement.appendChild(instance.$el);
/** @type {any} */ (instance.$el)[instanceKey] = instance; /** @type {any} */ (rootElement)[instanceKey] = instance;
for (const hook of /** @type {any} */(window).__pw_hooks_after_mount || []) for (const hook of /** @type {any} */(window).__pw_hooks_after_mount || [])
await hook({ hooksConfig, instance }); await hook({ hooksConfig, instance });
}; };
window.playwrightUnmount = async element => { window.playwrightUnmount = async rootElement => {
const component = /** @type {any} */(element)[instanceKey]; const component = /** @type {any} */(rootElement)[instanceKey];
if (!component) if (!component)
throw new Error('Component was not mounted'); throw new Error('Component was not mounted');
component.$destroy(); component.$destroy();
element.remove(); component.$el.remove();
}; };
const instanceKey = Symbol('instanceKey');

View File

@ -55,14 +55,15 @@ export const fixtures: Fixtures<
const locator = page.locator(selector); const locator = page.locator(selector);
return Object.assign(locator, { return Object.assign(locator, {
unmount: async () => { unmount: async () => {
await locator.evaluate(async element => { await locator.evaluate(async () => {
const rootElement = document.getElementById('root')!; const rootElement = document.getElementById('root')!;
await window.playwrightUnmount(element, rootElement); await window.playwrightUnmount(rootElement);
}); });
}, },
rerender: async (options: Omit<MountOptions, 'hooksConfig'>) => { rerender: async (options: Omit<MountOptions, 'hooksConfig'>) => {
await locator.evaluate(async (element, options) => { await locator.evaluate(async (element, options) => {
return await window.playwrightRerender(element, options); const rootElement = document.getElementById('root')!;
return await window.playwrightRerender(rootElement, options);
}, options); }, options);
} }
}); });

View File

@ -39,7 +39,7 @@ export type Component = JsxComponent | ObjectComponent;
declare global { declare global {
interface Window { interface Window {
playwrightMount(component: Component, rootElement: Element, hooksConfig: any): Promise<void>; playwrightMount(component: Component, rootElement: Element, hooksConfig: any): Promise<void>;
playwrightUnmount(element: Element, rootElement: Element): Promise<void>; playwrightUnmount(rootElement: Element): Promise<void>;
playwrightRerender(element: Element, props: Omit<MountOptions, 'hooksConfig'>): Promise<void>; playwrightRerender(rootElement: Element, options: Omit<MountOptions, 'hooksConfig'>): Promise<void>;
} }
} }

View File

@ -0,0 +1,4 @@
<template>
<div>root 1</div>
<div>root 2</div>
</template>

View File

@ -3,6 +3,7 @@ import Button from './components/Button.vue'
import Counter from './components/Counter.vue' import Counter from './components/Counter.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 MultiRoot from './components/MultiRoot.vue'
test.use({ viewport: { width: 500, height: 500 } }) test.use({ viewport: { width: 500, height: 500 } })
@ -83,3 +84,15 @@ test('should run hooks', async ({ page, mount }) => {
}) })
expect(messages).toEqual(['Before mount: {\"route\":\"A\"}, app: true', 'After mount el: HTMLButtonElement']) expect(messages).toEqual(['Before mount: {\"route\":\"A\"}, app: true', 'After mount el: HTMLButtonElement'])
}) })
test('unmount a multi root component should work', async ({ mount, page }) => {
const component = await mount(<MultiRoot />)
await expect(page.locator('#root')).toContainText('root 1')
await expect(page.locator('#root')).toContainText('root 2')
await component.unmount()
await expect(page.locator('#root')).not.toContainText('root 1')
await expect(page.locator('#root')).not.toContainText('root 2')
})

View File

@ -2,6 +2,7 @@ import { test, expect } from '@playwright/experimental-ct-vue'
import Button from './components/Button.vue' import Button from './components/Button.vue'
import Counter from './components/Counter.vue' import Counter from './components/Counter.vue'
import DefaultSlot from './components/DefaultSlot.vue' import DefaultSlot from './components/DefaultSlot.vue'
import MultiRoot from './components/MultiRoot.vue'
import NamedSlots from './components/NamedSlots.vue' import NamedSlots from './components/NamedSlots.vue'
import Component from './components/Component.vue' import Component from './components/Component.vue'
@ -96,3 +97,15 @@ test('should run hooks', async ({ page, mount }) => {
}) })
expect(messages).toEqual(['Before mount: {\"route\":\"A\"}, app: true', 'After mount el: HTMLButtonElement']) expect(messages).toEqual(['Before mount: {\"route\":\"A\"}, app: true', 'After mount el: HTMLButtonElement'])
}) })
test('unmount a multi root component should work', async ({ mount, page }) => {
const component = await mount(MultiRoot)
await expect(page.locator('#root')).toContainText('root 1')
await expect(page.locator('#root')).toContainText('root 2')
await component.unmount()
await expect(page.locator('#root')).not.toContainText('root 1')
await expect(page.locator('#root')).not.toContainText('root 2')
})

View File

@ -4,6 +4,7 @@ import Button from './components/Button.vue'
import Counter from './components/Counter.vue' import Counter from './components/Counter.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 MultiRoot from './components/MultiRoot.vue'
import Component from './components/Component.vue' import Component from './components/Component.vue'
test.use({ viewport: { width: 500, height: 500 } }) test.use({ viewport: { width: 500, height: 500 } })
@ -107,3 +108,15 @@ test('should unmount', async ({ page, mount }) => {
await component.unmount(); await component.unmount();
await expect(page.locator('#root')).not.toContainText('Submit'); await expect(page.locator('#root')).not.toContainText('Submit');
}); });
test('unmount a multi root component should work', async ({ mount, page }) => {
const component = await mount(MultiRoot)
await expect(page.locator('#root')).toContainText('root 1')
await expect(page.locator('#root')).toContainText('root 2')
await component.unmount()
await expect(page.locator('#root')).not.toContainText('root 1')
await expect(page.locator('#root')).not.toContainText('root 2')
})