refactor: make all components svelte5 compliant and update tests

This commit is contained in:
Marcin Wiśniowski 2025-02-15 14:38:24 +01:00
parent 3e0a19230d
commit 53d6d57854
7 changed files with 63 additions and 31 deletions

View file

@ -14,6 +14,7 @@
* limitations under the License.
*/
import type { Snippet } from "svelte"
import type {
SvelteComponent as V4Component,
Component as V5Component,
@ -21,7 +22,7 @@ import type {
} from 'svelte/types/runtime';
import type { TestType, Locator } from '@playwright/experimental-ct-core';
type ComponentSlot = string | string[];
type ComponentSlot = Snippet | string;
type ComponentSlots = Record<string, ComponentSlot> & { default?: ComponentSlot };
type ComponentEvents = Record<string, Function>;

View file

@ -19,10 +19,10 @@
// This file is injected into the registry as text, no dependencies are allowed.
import { asClassComponent } from 'svelte/legacy';
import { createRawSnippet } from "svelte";
/** @typedef {import('../playwright-ct-core/types/component').Component} Component */
/** @typedef {import('../playwright-ct-core/types/component').ObjectComponent} ObjectComponent */
/** @typedef {any} FrameworkComponent */
/** @typedef {import('svelte').SvelteComponent} SvelteComponent */
/** @typedef {import('svelte').ComponentType} ComponentType */
@ -38,6 +38,30 @@ function isObjectComponent(component) {
);
}
/** @type {( component: ObjectComponent ) => Record<string, any>} */
function extractParams(component) {
let {props, slots, on} = component;
slots = Object.fromEntries(
Object.entries(slots ?? {}).map(([key, snippet]) => {
if(typeof snippet === "string") {
console.log("ugraded", key);
return [key, createRawSnippet(() => ({render: () => snippet}))];
}
return [key, snippet]
})
);
on = Object.fromEntries(
Object.entries(on ?? {}).map(([key, fn]) => {
return [`on${key}`, fn]
})
);
return {props, slots, on};
}
const __pwSvelteComponentKey = Symbol('svelteComponent');
window.playwrightMount = async (component, rootElement, hooksConfig) => {
@ -45,17 +69,20 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
throw new Error('JSX mount notation is not supported');
const objectComponent = component;
component.type = component.type;
/** @type {ComponentType} */
const componentCtor = asClassComponent(component.type);
class App extends componentCtor {
constructor(options = {}) {
if (!isObjectComponent(component))
throw new Error('JSX mount notation is not supported');
let {props, slots, on} = extractParams(component);
super({
target: rootElement,
props: {
...objectComponent.props,
$$scope: {}
...props,
...slots,
...on,
},
...options
});
@ -63,9 +90,7 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
}
/** @type {SvelteComponent | undefined} */
let svelteComponent;
for (const hook of window.__pw_hooks_before_mount || []) {
svelteComponent = await hook({ hooksConfig, App });
}
@ -76,9 +101,6 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
rootElement[__pwSvelteComponentKey] = svelteComponent;
for (const [key, listener] of Object.entries(objectComponent.on || {}))
svelteComponent.$on(key, event => listener(event.detail));
for (const hook of window.__pw_hooks_after_mount || [])
await hook({ hooksConfig, svelteComponent });
};
@ -101,8 +123,11 @@ window.playwrightUpdate = async (rootElement, component) => {
);
if (!svelteComponent) throw new Error('Component was not mounted');
for (const [key, listener] of Object.entries(component.on || {}))
svelteComponent.$on(key, event => listener(event.detail));
let {props, slots, on} = extractParams(component);
if (component.props) svelteComponent.$set(component.props);
svelteComponent.$set({
...props,
...slots,
...on,
});
};

View file

@ -1,7 +1,5 @@
<script>
import { createEventDispatcher } from "svelte";
export let title;
const dispatch = createEventDispatcher();
const { title, onsubmit } = $props();
</script>
<button on:click={() => dispatch('submit', 'hello')}>{title}</button>
<button onclick={() => onsubmit('hello')}>{title}</button>

View file

@ -1,14 +1,12 @@
<script>
import { update, remountCount } from '../store'
import { createEventDispatcher } from "svelte";
export let count;
const dispatch = createEventDispatcher();
const { count, onsubmit, main, children } = $props()
update();
</script>
<button on:click={() => dispatch('submit', 'hello')}>
<button onclick={() => onsubmit?.('hello')}>
<span data-testid="props">{count}</span>
<span data-testid="remount-count">{remountCount}</span>
<slot name="main" />
<slot />
{@render main?.()}
{@render children?.()}
</button>

View file

@ -1,7 +1,11 @@
<script>
const {children} = $props()
</script>
<div>
<h1>Welcome!</h1>
<main>
<slot />
{@render children?.()}
</main>
<footer>
Thanks for visiting.

View file

@ -1,11 +1,17 @@
<script>
const {header, main, footer} = $props()
console.log({header, main, footer});
</script>
<div>
<header>
<slot name="header" />
{@render header?.()}
</header>
<main>
<slot name="main" />
{@render main?.()}
</main>
<footer>
<slot name="footer" />
{@render footer?.()}
</footer>
</div>

View file

@ -5,7 +5,7 @@ import NamedSlots from '@/components/NamedSlots.svelte';
test('render a default slot', async ({ mount }) => {
const component = await mount(DefaultSlot, {
slots: {
default: 'Main Content',
children: 'Main Content',
},
});
await expect(component).toContainText('Main Content');