mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(ct): vue3 html as slot (#18007)
This commit is contained in:
parent
49901c8ed7
commit
e76adafc5b
@ -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');
|
||||
|
||||
// allow content without `template` tag, for easier testing
|
||||
if (!hasWrappingTemplate)
|
||||
template = `<template #default="params">${template}</template>`;
|
||||
|
||||
const { code } = compile(`<transition>${template}</transition>`, {
|
||||
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 || {}))
|
||||
|
||||
@ -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(<DefaultSlot>
|
||||
Main Content
|
||||
<strong>Main Content</strong>
|
||||
</DefaultSlot>)
|
||||
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>
|
||||
<div id="one">One</div>
|
||||
<div id="two">Two</div>
|
||||
<div data-testid="one">One</div>
|
||||
<div data-testid="two">Two</div>
|
||||
</DefaultSlot>)
|
||||
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 }) => {
|
||||
|
||||
@ -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: '<strong>Main Content</strong>'
|
||||
}
|
||||
})
|
||||
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: [
|
||||
'<div data-testid="one">One</div>',
|
||||
'<div data-testid="two">Two</div>'
|
||||
]
|
||||
}
|
||||
})
|
||||
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 }) => {
|
||||
|
||||
@ -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: '<strong>Main Content</strong>'
|
||||
}
|
||||
})
|
||||
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: [
|
||||
'<div data-testid="one">One</div>',
|
||||
'<div data-testid="two">Two</div>'
|
||||
]
|
||||
}
|
||||
})
|
||||
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 }) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user