diff --git a/packages/playwright-ct-vue/registerSource.mjs b/packages/playwright-ct-vue/registerSource.mjs
index 29194e9a56..2dfb41defd 100644
--- a/packages/playwright-ct-vue/registerSource.mjs
+++ b/packages/playwright-ct-vue/registerSource.mjs
@@ -18,6 +18,8 @@
// This file is injected into the registry as text, no dependencies are allowed.
import { createApp, setDevtoolsHook, h } from 'vue';
+import { compile } from '@vue/compiler-dom';
+import * as Vue from 'vue';
/** @typedef {import('@playwright/test/types/component').Component} Component */
/** @typedef {import('vue').Component} FrameworkComponent */
@@ -43,6 +45,49 @@ function createChild(child) {
return typeof child === 'string' ? child : createWrapper(child);
}
+/**
+ * Copied from: https://github.com/vuejs/test-utils/blob/main/src/utils/compileSlots.ts
+ * Vue does not provide an easy way to compile template in "slot" mode
+ * Since we do not want to rely on compiler internals and specify
+ * transforms manually we create fake component invocation with the slot we
+ * need and pick slots param from render function later. Fake component will
+ * never be instantiated but it requires to be a component so compile
+ * properly generate invocation. Since we do not want to monkey-patch
+ * `resolveComponent` function we are just using one of built-in components.
+ *
+ * @param {string} html
+ */
+function createSlot(html) {
+ let template = html.trim();
+ const hasWrappingTemplate = template && template.startsWith('${template}`;
+
+ const { code } = compile(`${template}`, {
+ mode: 'function',
+ prefixIdentifiers: false
+ });
+ const createRenderFunction = new Function('Vue', code);
+ const renderFn = createRenderFunction(Vue);
+ return (ctx = {}) => {
+ const result = renderFn(ctx);
+ const slotName = Object.keys(result.children)[0];
+ return result.children[slotName](ctx);
+ };
+}
+
+function slotToFunction(slot) {
+ if (typeof slot === 'string')
+ return createSlot(slot)();
+
+ if (Array.isArray(slot))
+ return slot.map(slot => createSlot(slot)());
+
+ throw Error(`Invalid slot received.`);
+}
+
/**
* @param {Component} component
*/
@@ -109,9 +154,9 @@ function createComponent(component) {
// Vue test util syntax.
for (const [key, value] of Object.entries(component.options?.slots || {})) {
if (key === 'default')
- children.push(value);
+ children.push(slotToFunction(value));
else
- slots[key] = value;
+ slots[key] = slotToFunction(value);
}
props = component.options?.props || {};
for (const [key, value] of Object.entries(component.options?.on || {}))
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 3786fa0b62..63569e5849 100644
--- a/tests/components/ct-vue-vite/src/notation-jsx.spec.tsx
+++ b/tests/components/ct-vue-vite/src/notation-jsx.spec.tsx
@@ -71,18 +71,18 @@ test('emit an submit event when the button is clicked', async ({ mount }) => {
test('render a default slot', async ({ mount }) => {
const component = await mount(
- Main Content
+ Main Content
)
- await expect(component).toContainText('Main Content')
+ await expect(component.getByRole('strong')).toContainText('Main Content')
})
test('render a component with multiple slots', async ({ mount }) => {
const component = await mount(
- One
- Two
+ One
+ Two
)
- await expect(component.locator('#one')).toContainText('One')
- await expect(component.locator('#two')).toContainText('Two')
+ await expect(component.getByTestId('one')).toContainText('One')
+ await expect(component.getByTestId('two')).toContainText('Two')
})
test('render a component with a named slot', async ({ mount }) => {
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 c39df92e1a..316635c478 100644
--- a/tests/components/ct-vue-vite/src/notation-vue.spec.js
+++ b/tests/components/ct-vue-vite/src/notation-vue.spec.js
@@ -81,20 +81,23 @@ test('emit an submit event when the button is clicked', async ({ mount }) => {
test('render a default slot', async ({ mount }) => {
const component = await mount(DefaultSlot, {
slots: {
- default: 'Main Content'
+ default: 'Main Content'
}
})
- await expect(component).toContainText('Main Content')
+ await expect(component.getByRole('strong')).toContainText('Main Content')
})
test('render a component with multiple slots', async ({ mount }) => {
const component = await mount(DefaultSlot, {
slots: {
- default: ['one', 'two']
+ default: [
+ '
One
',
+ 'Two
'
+ ]
}
})
- await expect(component).toContainText('one')
- await expect(component).toContainText('two')
+ await expect(component.getByTestId('one')).toContainText('One')
+ await expect(component.getByTestId('two')).toContainText('Two')
})
test('render a component with a named slot', async ({ mount }) => {
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 dbc7a0d4e9..abf3f5e0e4 100644
--- a/tests/components/ct-vue-vite/src/notation-vue.spec.ts
+++ b/tests/components/ct-vue-vite/src/notation-vue.spec.ts
@@ -82,20 +82,23 @@ test('emit an submit event when the button is clicked', async ({ mount }) => {
test('render a default slot', async ({ mount }) => {
const component = await mount(DefaultSlot, {
slots: {
- default: 'Main Content'
+ default: 'Main Content'
}
})
- await expect(component).toContainText('Main Content')
+ await expect(component.getByRole('strong')).toContainText('Main Content')
})
test('render a component with multiple slots', async ({ mount }) => {
const component = await mount(DefaultSlot, {
slots: {
- default: ['one', 'two']
+ default: [
+ 'One
',
+ 'Two
'
+ ]
}
})
- await expect(component).toContainText('one')
- await expect(component).toContainText('two')
+ await expect(component.getByTestId('one')).toContainText('One')
+ await expect(component.getByTestId('two')).toContainText('Two')
})
test('render a component with a named slot', async ({ mount }) => {