diff --git a/packages/playwright-ct-react/registerSource.mjs b/packages/playwright-ct-react/registerSource.mjs index 644d80f1f5..a6948efe7d 100644 --- a/packages/playwright-ct-react/registerSource.mjs +++ b/packages/playwright-ct-react/registerSource.mjs @@ -78,7 +78,7 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => { await hook({ hooksConfig }); }; -window.playwrightUnmount = async (element, rootElement) => { +window.playwrightUnmount = async rootElement => { if (!ReactDOM.unmountComponentAtNode(rootElement)) throw new Error('Component was not mounted'); }; diff --git a/packages/playwright-ct-svelte/registerSource.mjs b/packages/playwright-ct-svelte/registerSource.mjs index 40ffd5aa98..788fd2886b 100644 --- a/packages/playwright-ct-svelte/registerSource.mjs +++ b/packages/playwright-ct-svelte/registerSource.mjs @@ -68,7 +68,7 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => { svelteComponent.$on(key, event => listener(event.detail)); }; -window.playwrightUnmount = async (element, rootElement) => { +window.playwrightUnmount = async rootElement => { const svelteComponent = /** @type {SvelteComponent} */ (rootElement[svelteComponentKey]); if (!svelteComponent) throw new Error('Component was not mounted'); diff --git a/packages/playwright-ct-vue/registerSource.mjs b/packages/playwright-ct-vue/registerSource.mjs index dfd6f6e1a7..5f388308b8 100644 --- a/packages/playwright-ct-vue/registerSource.mjs +++ b/packages/playwright-ct-vue/registerSource.mjs @@ -168,21 +168,22 @@ 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; - instance.$el[componentKey] = wrapper; + rootElement[appKey] = app; + rootElement[componentKey] = wrapper; + 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]); +window.playwrightUnmount = async rootElement => { + const app = /** @type {import('vue').App} */ (rootElement[appKey]); if (!app) throw new Error('Component was not mounted'); app.unmount(); }; -window.playwrightRerender = async (element, options) => { - const component = element[componentKey].component; +window.playwrightRerender = async (rootElement, options) => { + const component = rootElement[componentKey].component; if (!component) throw new Error('Component was not mounted'); diff --git a/packages/playwright-ct-vue2/registerSource.mjs b/packages/playwright-ct-vue2/registerSource.mjs index 3edb704ab5..32185c2523 100644 --- a/packages/playwright-ct-vue2/registerSource.mjs +++ b/packages/playwright-ct-vue2/registerSource.mjs @@ -134,6 +134,8 @@ function render(component, h) { return wrapper; } +const instanceKey = Symbol('instanceKey'); + window.playwrightMount = async (component, rootElement, hooksConfig) => { for (const hook of /** @type {any} */(window).__pw_hooks_before_mount || []) await hook({ hooksConfig }); @@ -142,18 +144,16 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => { render: h => render(component, h), }).$mount(); 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 || []) await hook({ hooksConfig, instance }); }; -window.playwrightUnmount = async element => { - const component = /** @type {any} */(element)[instanceKey]; +window.playwrightUnmount = async rootElement => { + const component = /** @type {any} */(rootElement)[instanceKey]; if (!component) throw new Error('Component was not mounted'); component.$destroy(); - element.remove(); + component.$el.remove(); }; - -const instanceKey = Symbol('instanceKey'); diff --git a/packages/playwright-test/src/mount.ts b/packages/playwright-test/src/mount.ts index 9fee000f89..feeb6c9bf5 100644 --- a/packages/playwright-test/src/mount.ts +++ b/packages/playwright-test/src/mount.ts @@ -55,14 +55,15 @@ export const fixtures: Fixtures< const locator = page.locator(selector); return Object.assign(locator, { unmount: async () => { - await locator.evaluate(async element => { + await locator.evaluate(async () => { const rootElement = document.getElementById('root')!; - await window.playwrightUnmount(element, rootElement); + await window.playwrightUnmount(rootElement); }); }, rerender: async (options: Omit) => { await locator.evaluate(async (element, options) => { - return await window.playwrightRerender(element, options); + const rootElement = document.getElementById('root')!; + return await window.playwrightRerender(rootElement, options); }, options); } }); diff --git a/packages/playwright-test/types/component.d.ts b/packages/playwright-test/types/component.d.ts index e1a96c9bd0..561d400832 100644 --- a/packages/playwright-test/types/component.d.ts +++ b/packages/playwright-test/types/component.d.ts @@ -39,7 +39,7 @@ export type Component = JsxComponent | ObjectComponent; declare global { interface Window { playwrightMount(component: Component, rootElement: Element, hooksConfig: any): Promise; - playwrightUnmount(element: Element, rootElement: Element): Promise; - playwrightRerender(element: Element, props: Omit): Promise; + playwrightUnmount(rootElement: Element): Promise; + playwrightRerender(rootElement: Element, options: Omit): Promise; } } diff --git a/tests/components/ct-vue-vite/src/components/MultiRoot.vue b/tests/components/ct-vue-vite/src/components/MultiRoot.vue new file mode 100644 index 0000000000..d1c348d750 --- /dev/null +++ b/tests/components/ct-vue-vite/src/components/MultiRoot.vue @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/tests/components/ct-vue-vite/src/notation-jsx.spec.tsx b/tests/components/ct-vue-vite/src/notation-jsx.spec.tsx index df4b20f286..755c9a4882 100644 --- a/tests/components/ct-vue-vite/src/notation-jsx.spec.tsx +++ b/tests/components/ct-vue-vite/src/notation-jsx.spec.tsx @@ -3,6 +3,7 @@ import Button from './components/Button.vue' import Counter from './components/Counter.vue' import DefaultSlot from './components/DefaultSlot.vue' import NamedSlots from './components/NamedSlots.vue' +import MultiRoot from './components/MultiRoot.vue' 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']) }) + +test('unmount a multi root component should work', async ({ mount, page }) => { + const component = await mount() + + 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') +}) diff --git a/tests/components/ct-vue-vite/src/notation-vue.spec.js b/tests/components/ct-vue-vite/src/notation-vue.spec.js index 87f262f9c5..cbcc3b3390 100644 --- a/tests/components/ct-vue-vite/src/notation-vue.spec.js +++ b/tests/components/ct-vue-vite/src/notation-vue.spec.js @@ -2,6 +2,7 @@ import { test, expect } from '@playwright/experimental-ct-vue' import Button from './components/Button.vue' import Counter from './components/Counter.vue' import DefaultSlot from './components/DefaultSlot.vue' +import MultiRoot from './components/MultiRoot.vue' import NamedSlots from './components/NamedSlots.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']) }) + +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') +}) diff --git a/tests/components/ct-vue-vite/src/notation-vue.spec.ts b/tests/components/ct-vue-vite/src/notation-vue.spec.ts index 9587564652..c37774f8f3 100644 --- a/tests/components/ct-vue-vite/src/notation-vue.spec.ts +++ b/tests/components/ct-vue-vite/src/notation-vue.spec.ts @@ -4,6 +4,7 @@ import Button from './components/Button.vue' import Counter from './components/Counter.vue' import DefaultSlot from './components/DefaultSlot.vue' import NamedSlots from './components/NamedSlots.vue' +import MultiRoot from './components/MultiRoot.vue' import Component from './components/Component.vue' test.use({ viewport: { width: 500, height: 500 } }) @@ -107,3 +108,15 @@ test('should unmount', async ({ page, mount }) => { await component.unmount(); 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') +})