feat(ct): class and object components

This commit is contained in:
sand4rt 2024-04-05 19:01:08 +02:00
parent 7ad255301f
commit b1119c1832
5 changed files with 56 additions and 1 deletions

View file

@ -21,6 +21,7 @@ import { setTransformData } from 'playwright/lib/transform/transform';
const t: typeof T = types;
let jsxComponentNames: Set<string>;
let classComponentNames: Set<string>;
let importInfos: Map<string, ImportInfo>;
export default declare((api: BabelAPI) => {
@ -32,6 +33,7 @@ export default declare((api: BabelAPI) => {
Program: {
enter(path) {
jsxComponentNames = collectJsxComponentUsages(path.node);
classComponentNames = collectClassMountUsages(path.node);
importInfos = new Map();
},
exit(path) {
@ -93,7 +95,7 @@ export default declare((api: BabelAPI) => {
if (t.isImportNamespaceSpecifier(specifier))
continue;
const { localName, info } = importInfo(importNode, specifier, this.filename!);
if (jsxComponentNames.has(localName)) {
if (jsxComponentNames.has(localName) || classComponentNames.has(localName)) {
importInfos.set(localName, info);
++importCount;
}
@ -141,6 +143,23 @@ function collectJsxComponentUsages(node: T.Node): Set<string> {
return names;
}
function collectClassMountUsages(node: T.Node): Set<string> {
const names = new Set<string>();
traverse(node, {
enter: p => {
// Treat calls to mount and all identifiers in arguments as component usages e.g. mount(Component)
if (t.isCallExpression(p.node) && t.isIdentifier(p.node.callee) && p.node.callee.name === 'mount') {
p.traverse({
Identifier: p => {
names.add(p.node.name);
}
});
}
}
});
return names;
}
export type ImportInfo = {
id: string;
filename: string;

View file

@ -0,0 +1,10 @@
import { defineComponent, h } from 'vue';
export const Story = defineComponent(
(props) => {
return () => h('div', props.title);
},
{
props: ['title'],
}
);

View file

@ -2,6 +2,7 @@ import { test, expect } from '@playwright/experimental-ct-vue';
import Button from '@/components/Button.vue';
import EmptyTemplate from '@/components/EmptyTemplate.vue';
import Component from '@/components/Component.vue';
import { Story } from '@/components/Story';
test('render props', async ({ mount }) => {
const component = await mount(Button, {
@ -23,3 +24,12 @@ test('render a component without options', async ({ mount }) => {
const component = await mount(Component);
await expect(component).toContainText('test');
});
test('render props with defineComponent syntax', async ({ mount }) => {
const component = await mount(Story, {
props: {
title: 'story/wrapper'
}
});
await expect(component).toContainText('story/wrapper');
});

View file

@ -2,6 +2,7 @@ import { test, expect } from '@playwright/experimental-ct-vue';
import Button from '@/components/Button.vue';
import EmptyTemplate from '@/components/EmptyTemplate.vue';
import Component from '@/components/Component.vue';
import { Story } from '@/components/Story';
test('render props', async ({ mount }) => {
const component = await mount(Button, {
@ -23,3 +24,12 @@ test('render a component without options', async ({ mount }) => {
const component = await mount(Component);
await expect(component).toContainText('test');
});
test('render props with defineComponent syntax', async ({ mount }) => {
const component = await mount(Story, {
props: {
title: 'story/wrapper'
}
});
await expect(component).toContainText('story/wrapper');
});

View file

@ -1,6 +1,7 @@
import { test, expect } from '@playwright/experimental-ct-vue';
import Button from '@/components/Button.vue';
import EmptyTemplate from '@/components/EmptyTemplate.vue';
import { Story } from '@/components/Story';
test('render props', async ({ mount }) => {
const component = await mount(<Button title='Submit' />);
@ -19,3 +20,8 @@ test('render an empty component', async ({ page, mount }) => {
expect(await component.textContent()).toBe('');
await expect(component).toHaveText('');
});
test('render props with defineComponent syntax', async ({ mount }) => {
const component = await mount(<Story title="story/wrapper" />);
await expect(component).toContainText('story/wrapper');
});