Skip to content

Commit c41ef0c

Browse files
negreirosleo¨Leonardodai-shi
authored
test: add e2e tests for css modules (#693)
This PR relates to #186. It adds e2e tests for CSS modules. I used `expect .toContain('class-name')` because I was not sure if the generated name would always be the same. --------- Co-authored-by: ¨Leonardo <¨[email protected]¨> Co-authored-by: Daishi Kato <[email protected]>
1 parent c04f94a commit c41ef0c

14 files changed

+250
-0
lines changed
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# RSC CSS Modules
2+
3+
Only RSC features, no SSR. Using css modules to style the application, both on server and client components.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module '*.module.css';
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "rsc-css-modules",
3+
"version": "0.1.0",
4+
"type": "module",
5+
"private": true,
6+
"scripts": {
7+
"dev": "waku dev",
8+
"build": "waku build",
9+
"start": "waku start"
10+
},
11+
"dependencies": {
12+
"react": "19.0.0-beta-4508873393-20240430",
13+
"react-dom": "19.0.0-beta-4508873393-20240430",
14+
"react-server-dom-webpack": "19.0.0-beta-4508873393-20240430",
15+
"waku": "workspace:*"
16+
},
17+
"devDependencies": {
18+
"@types/react": "18.3.1",
19+
"@types/react-dom": "18.3.0",
20+
"typescript": "5.4.4"
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import styles from './app.module.css';
2+
import { ClientCounter } from './ClientCounter.js';
3+
4+
const App = ({ name }: { name: string }) => {
5+
return (
6+
<div data-testid="app-wrapper" className={styles.wrapper}>
7+
<title>Waku example</title>
8+
<p className={styles.text} data-testid="app-name">
9+
{name}
10+
</p>
11+
<ClientCounter />
12+
</div>
13+
);
14+
};
15+
16+
export default App;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use client';
2+
import { useState } from 'react';
3+
import styles from './clientCounter.module.css';
4+
5+
export const ClientCounter = () => {
6+
const [count, setCount] = useState(0);
7+
8+
return (
9+
<div className={styles.counterWrapper} data-testid="client-counter">
10+
<p data-testid="count">{count}</p>
11+
<button
12+
className={styles.counterButton}
13+
data-testid="increment"
14+
onClick={() => setCount((c) => c + 1)}
15+
>
16+
Increment
17+
</button>
18+
</div>
19+
);
20+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.wrapper {
2+
display: flex;
3+
flex-direction: column;
4+
}
5+
6+
.text {
7+
font-size: 18px;
8+
color: blue;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.counterWrapper {
2+
display: flex;
3+
flex-direction: row;
4+
gap: 4px;
5+
}
6+
7+
.counterButton {
8+
padding: 8px;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { lazy } from 'react';
2+
import { defineEntries } from 'waku/server';
3+
4+
const App = lazy(() => import('./components/App.js'));
5+
6+
export default defineEntries(
7+
// renderEntries
8+
async (input) => {
9+
return {
10+
App: <App name={input || 'Waku'} />,
11+
};
12+
},
13+
// getBuildConfig
14+
async () => [{ pathname: '/', entries: [{ input: '' }] }],
15+
// getSsrConfig
16+
() => {
17+
throw new Error('SSR should not be used in this test.');
18+
},
19+
);
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { StrictMode } from 'react';
2+
import { createRoot } from 'react-dom/client';
3+
import { Root, Slot } from 'waku/client';
4+
5+
const rootElement = (
6+
<StrictMode>
7+
<Root>
8+
<Slot id="App" />
9+
</Root>
10+
</StrictMode>
11+
);
12+
13+
createRoot(document.body).render(rootElement);
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"strict": true,
4+
"target": "esnext",
5+
"downlevelIteration": true,
6+
"esModuleInterop": true,
7+
"module": "nodenext",
8+
"skipLibCheck": true,
9+
"noUncheckedIndexedAccess": true,
10+
"exactOptionalPropertyTypes": true,
11+
"types": ["react/experimental"],
12+
"jsx": "react-jsx",
13+
"outDir": "./dist",
14+
"composite": true
15+
},
16+
"include": ["./src", "./waku.config.ts", "declaration.d.ts"]
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const DO_NOT_BUNDLE = '';
2+
3+
/** @type {import('waku/config').Config} */
4+
export default {
5+
middleware: (cmd: 'dev' | 'start') => [
6+
...(cmd === 'dev'
7+
? [
8+
import(
9+
/* @vite-ignore */ DO_NOT_BUNDLE + 'waku/middleware/dev-server'
10+
),
11+
]
12+
: []),
13+
import('waku/middleware/rsc'),
14+
import('waku/middleware/fallback'),
15+
],
16+
};

e2e/rsc-css-modules.spec.ts

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { expect } from '@playwright/test';
2+
import { execSync, exec, ChildProcess } from 'node:child_process';
3+
import { fileURLToPath } from 'node:url';
4+
import waitPort from 'wait-port';
5+
import { debugChildProcess, getFreePort, terminate, test } from './utils.js';
6+
import { rm } from 'node:fs/promises';
7+
8+
const waku = fileURLToPath(
9+
new URL('../packages/waku/dist/cli.js', import.meta.url),
10+
);
11+
12+
const commands = [
13+
{
14+
command: 'dev',
15+
},
16+
{
17+
build: 'build',
18+
command: 'start',
19+
},
20+
];
21+
22+
const cwd = fileURLToPath(
23+
new URL('./fixtures/rsc-css-modules', import.meta.url),
24+
);
25+
26+
for (const { build, command } of commands) {
27+
test.describe(`rsc-css-modules: ${command}`, () => {
28+
let cp: ChildProcess;
29+
let port: number;
30+
test.beforeAll('remove cache', async () => {
31+
await rm(`${cwd}/dist`, {
32+
recursive: true,
33+
force: true,
34+
});
35+
});
36+
37+
test.beforeAll(async () => {
38+
if (build) {
39+
execSync(`node ${waku} ${build}`, { cwd });
40+
}
41+
port = await getFreePort();
42+
cp = exec(`node ${waku} ${command} --port ${port}`, { cwd });
43+
debugChildProcess(cp, fileURLToPath(import.meta.url), [
44+
/ExperimentalWarning: Custom ESM Loaders is an experimental feature and might change at any time/,
45+
]);
46+
await waitPort({ port });
47+
});
48+
49+
test.afterAll(async () => {
50+
await terminate(cp.pid!);
51+
});
52+
53+
test('css-modules classes', async ({ page }) => {
54+
await page.goto(`http://localhost:${port}/`);
55+
56+
const wrapperClass = await page
57+
.getByTestId('app-wrapper')
58+
.getAttribute('class');
59+
expect(wrapperClass).toContain('wrapper');
60+
61+
const appNameClass = await page
62+
.getByTestId('app-name')
63+
.getAttribute('class');
64+
expect(appNameClass).toContain('text');
65+
66+
const clientcounterClass = await page
67+
.getByTestId('client-counter')
68+
.getAttribute('class');
69+
expect(clientcounterClass).toContain('counterWrapper');
70+
71+
const incrementClass = await page
72+
.getByTestId('increment')
73+
.getAttribute('class');
74+
expect(incrementClass).toContain('counterButton');
75+
});
76+
});
77+
}

pnpm-lock.yaml

+25
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tsconfig.e2e.json

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
{
1010
"path": "./e2e/fixtures/rsc-basic/tsconfig.json"
1111
},
12+
{
13+
"path": "./e2e/fixtures/rsc-css-modules/tsconfig.json"
14+
},
1215
{
1316
"path": "./e2e/fixtures/ssr-basic/tsconfig.json"
1417
},

0 commit comments

Comments
 (0)