Skip to content

Commit

Permalink
test: add e2e cases
Browse files Browse the repository at this point in the history
  • Loading branch information
chenjiahan committed Sep 8, 2024
1 parent a388728 commit 6ae09fe
Show file tree
Hide file tree
Showing 27 changed files with 1,212 additions and 2 deletions.
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": ["dist"],
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
Expand All @@ -40,12 +42,14 @@
"@biomejs/biome": "^1.8.3",
"@playwright/test": "^1.46.1",
"@rsbuild/core": "^1.0.1-rc.0",
"@rsbuild/plugin-vue": "1.0.1-rc.5",
"@types/node": "^20.16.2",
"nano-staged": "^0.8.0",
"playwright": "^1.46.1",
"simple-git-hooks": "^2.11.1",
"tsup": "^8.2.4",
"typescript": "^5.5.4"
"typescript": "^5.5.4",
"vue": "^3.5.0"
},
"peerDependencies": {
"@rsbuild/core": "1.x || ^1.0.1-beta.0"
Expand Down
599 changes: 599 additions & 0 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions test/jsx-basic/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { expect, test } from '@playwright/test';
import { createRsbuild, loadConfig } from '@rsbuild/core';

const __dirname = dirname(fileURLToPath(import.meta.url));

test('should build basic Vue jsx correctly', async ({ page }) => {
const rsbuild = await createRsbuild({
cwd: __dirname,
rsbuildConfig: (await loadConfig({ cwd: __dirname })).content,
});

await rsbuild.build();
const { server, urls } = await rsbuild.preview();

await page.goto(urls[0]);

const button1 = page.locator('#button1');
await expect(button1).toHaveText('A: 0');

await server.close();
});
18 changes: 18 additions & 0 deletions test/jsx-basic/rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineConfig } from '@rsbuild/core';
import { pluginBabel } from '@rsbuild/plugin-babel';
import { pluginVue } from '@rsbuild/plugin-vue';
import { pluginVueJsx } from '@rsbuild/plugin-vue-jsx';
import { getRandomPort } from '../helper';

export default defineConfig({
plugins: [
pluginVue(),
pluginVueJsx(),
pluginBabel({
include: /\.(?:jsx|tsx)$/,
}),
],
server: {
port: getRandomPort(),
},
});
14 changes: 14 additions & 0 deletions test/jsx-basic/src/A.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineComponent, ref } from 'vue';

export default defineComponent({
name: 'Test',

setup() {
const count = ref(0);
return () => (
<button id="button1" type="button" onClick={() => count.value++}>
A: {count.value}
</button>
);
},
});
6 changes: 6 additions & 0 deletions test/jsx-basic/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createApp } from 'vue';
import A from './A';

console.log(A);

createApp(A).mount('#root');
263 changes: 263 additions & 0 deletions test/jsx-hmr/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { type Page, expect, test } from '@playwright/test';
import { createRsbuild, loadConfig } from '@rsbuild/core';
import { pluginVueJsx } from '../../src';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

test('should render', async ({ page }) => {
const rsbuild = await createRsbuild({
cwd: __dirname,
rsbuildConfig: (await loadConfig({ cwd: __dirname })).content,
});

const { server, urls } = await rsbuild.startDevServer();

await page.goto(urls[0]);

await expect(page.locator('.named')).toHaveText('named 0');
await expect(page.locator('.named-specifier')).toHaveText(
'named specifier 1',
);
await expect(page.locator('.default')).toHaveText('default 2');
await expect(page.locator('.default-tsx')).toHaveText('default tsx 3');
await expect(page.locator('.script')).toHaveText('script 4');
await expect(page.locator('.ts-import')).toHaveText('success');

await server.close();
});

test('should update', async ({ page }) => {
const rsbuild = await createRsbuild({
cwd: __dirname,
rsbuildConfig: (await loadConfig({ cwd: __dirname })).content,
});

const { server, urls } = await rsbuild.startDevServer();

await page.goto(urls[0]);

await page.locator('.named').click();
await expect(page.locator('.named')).toHaveText('named 1');

await page.locator('.named-specifier').click();
await expect(page.locator('.named-specifier')).toHaveText(
'named specifier 2',
);

await page.locator('.default').click();
await expect(page.locator('.default')).toHaveText('default 3');

await page.locator('.default-tsx').click();
await expect(page.locator('.default-tsx')).toHaveText('default tsx 4');

await page.locator('.script').click();
await expect(page.locator('.script')).toHaveText('script 5');

await server.close();
});

test.describe('vue jsx hmr', () => {
// HMR cases will fail in Windows
if (process.platform === 'win32') {
test.skip();
}

let server: {
close: () => Promise<void>;
};
let page: Page;

test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
const rsbuild = await createRsbuild({
cwd: __dirname,
rsbuildConfig: (await loadConfig({ cwd: __dirname })).content,
});

const result = await rsbuild.startDevServer();
server = result.server;

await page.goto(result.urls[0]);
});

test.afterAll(async () => {
// reset files
editFile('Comps.jsx', (code) =>
code.replace('named updated {count', 'named {count'),
);
editFile('Comps.jsx', (code) =>
code.replace('named specifier updated {count', 'named specifier {count'),
);
editFile('Comps.jsx', (code) =>
code.replace('default updated {count', 'default {count'),
);
editFile('Comp.tsx', (code) =>
code.replace('default tsx updated {count', 'default tsx {count'),
);
editFile('setup-syntax-jsx.vue', (code) =>
code.replace('let count = ref(1000)', 'let count = ref(100)'),
);

await server.close();
});

test('hmr: named export', async () => {
await page.locator('.named').click();
await expect(page.locator('.named')).toHaveText('named 1');
await page.locator('.named-specifier').click();
await expect(page.locator('.named-specifier')).toHaveText(
'named specifier 2',
);
await page.locator('.default').click();
await expect(page.locator('.default')).toHaveText('default 3');
await page.locator('.default-tsx').click();
await expect(page.locator('.default-tsx')).toHaveText('default tsx 4');

editFile('Comps.jsx', (code) =>
code.replace('named {count', 'named updated {count'),
);
await untilUpdated(() => page.textContent('.named'), 'named updated 0');

// affect all components in same file
await expect(page.locator('.named-specifier')).toHaveText(
'named specifier 1',
);
await expect(page.locator('.default')).toHaveText('default 2');
// should not affect other components from different file
await expect(page.locator('.default-tsx')).toHaveText('default tsx 4');
});

test('hmr: named export via specifier', async () => {
editFile('Comps.jsx', (code) =>
code.replace('named specifier {count', 'named specifier updated {count'),
);
await untilUpdated(
() => page.textContent('.named-specifier'),
'named specifier updated 1',
);

// affect all components in same file
await expect(page.locator('.default')).toHaveText('default 2');
// should not affect other components on the page
await expect(page.locator('.default-tsx')).toHaveText('default tsx 4');
});

test('hmr: default export', async () => {
editFile('Comps.jsx', (code) =>
code.replace('default {count', 'default updated {count'),
);
await untilUpdated(() => page.textContent('.default'), 'default updated 2');

// should not affect other components on the page
await expect(page.locator('.default-tsx')).toHaveText('default tsx 4');
});

test('hmr: default Default export', async () => {
await page.locator('.named').click();
await expect(page.locator('.named')).toHaveText('named updated 1');

editFile('Comp.tsx', (code) =>
code.replace('default tsx {count', 'default tsx updated {count'),
);
await untilUpdated(
() => page.textContent('.default-tsx'),
'default tsx updated 3',
);

// should not affect other components on the page
await expect(page.locator('.named')).toHaveText('named updated 1');
});

// not pass
test.skip('hmr: vue script lang=jsx', async () => {
await page.locator('.script').click();
await expect(page.locator('.script')).toHaveText('script 5');

editFile('Script.vue', (code) =>
code.replace('script {count', 'script updated {count'),
);

await untilUpdated(() => page.textContent('.script'), 'script updated 4');

// reset code
editFile('Script.vue', (code) =>
code.replace('script updated {count', 'script {count'),
);
});

// not pass
test.skip('hmr: script in .vue', async () => {
await page.locator('.src-import').click();
await expect(page.locator('.src-import')).toHaveText('src import 6');

editFile('Script.vue', (code) =>
code.replace('script {count', 'script updated {count'),
);
await untilUpdated(() => page.textContent('.script'), 'script updated 4');

await expect(page.locator('.src-import')).toHaveText('src import 6');

// reset code
editFile('Script.vue', (code) =>
code.replace('script updated {count', 'script {count'),
);
});

// not pass
test.skip('hmr: src import in .vue', async () => {
await page.locator('.script').click();
await expect(page.locator('.script')).toHaveText('script 5');

editFile('SrcImport.jsx', (code) =>
code.replace('src import {count', 'src import updated {count'),
);

await untilUpdated(
() => page.textContent('.src-import'),
'src import updated 5',
);

await expect(page.locator('.script')).toHaveText('script 5');

// reset code
editFile('SrcImport.jsx', (code) =>
code.replace('src import updated {count', 'src import {count'),
);
});

test('hmr: setup jsx in .vue', async () => {
editFile('setup-syntax-jsx.vue', (code) =>
code.replace('let count = ref(100)', 'let count = ref(1000)'),
);

await untilUpdated(() => page.textContent('.setup-jsx'), '1000');
});
});

function editFile(filename: string, replacer: (str: string) => string): void {
const fileName = path.join(__dirname, 'src', filename);
const content = fs.readFileSync(fileName, 'utf-8');
const modified = replacer(content);
fs.writeFileSync(fileName, modified);
}

const timeout = (n: number) => new Promise((r) => setTimeout(r, n));

async function untilUpdated(
poll: () => Promise<string | null>,
expected: string,
): Promise<void> {
const maxTries = 50;
for (let tries = 0; tries < maxTries; tries++) {
const actual = (await poll()) ?? '';
if (actual.indexOf(expected) > -1 || tries === maxTries - 1) {
expect(actual).toMatch(expected);
break;
}

await timeout(50);
}
}
21 changes: 21 additions & 0 deletions test/jsx-hmr/rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineConfig } from '@rsbuild/core';
import { pluginBabel } from '@rsbuild/plugin-babel';
import { pluginVue } from '@rsbuild/plugin-vue';
import { pluginVueJsx } from '@rsbuild/plugin-vue-jsx';
import { getRandomPort } from '../helper';

export default defineConfig({
plugins: [
pluginVue(),
pluginBabel({
include: /\.(?:jsx|tsx)(\.js)?$/,
}),
pluginVueJsx(),
],
server: {
port: getRandomPort(),
},
performance: {
buildCache: false,
},
});
14 changes: 14 additions & 0 deletions test/jsx-hmr/src/Comp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineComponent, ref } from 'vue';

const Default = defineComponent(() => {
const count = ref(3);
const inc = () => count.value++;

return () => (
<button class="default-tsx" onClick={inc} type="button">
default tsx {count.value}
</button>
);
});

export default Default;
Loading

0 comments on commit 6ae09fe

Please sign in to comment.