Skip to content

Commit 1b097af

Browse files
committed
chore: updated tests
1 parent 478bb61 commit 1b097af

File tree

6 files changed

+1021
-14
lines changed

6 files changed

+1021
-14
lines changed

src/generators/web/index.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,16 @@ export default {
5757
if (output) {
5858
// Write HTML files
5959
for (const { html, api } of results) {
60-
safeWrite(join(output, `${api}.html`), html, 'utf-8');
60+
await safeWrite(join(output, `${api}.html`), html, 'utf-8');
6161
}
6262

6363
// Write code-split JavaScript chunks
6464
for (const chunk of jsChunks) {
65-
safeWrite(join(output, chunk.fileName), chunk.code, 'utf-8');
65+
await safeWrite(join(output, chunk.fileName), chunk.code, 'utf-8');
6666
}
6767

6868
// Write CSS bundle
69-
safeWrite(join(output, 'styles.css'), css, 'utf-8');
69+
await safeWrite(join(output, 'styles.css'), css, 'utf-8');
7070
}
7171

7272
// Return HTML and CSS for each entry

src/generators/web/ui/components/CodeBox.jsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,14 @@ export default ({ className, children, ...props }) => {
4242
};
4343

4444
return (
45-
<>
46-
<BaseCodeBox
47-
onCopy={onCopy}
48-
language={getLanguageDisplayName(language)}
49-
className={className}
50-
buttonText="Copy to clipboard"
51-
{...props}
52-
>
53-
{children}
54-
</BaseCodeBox>
55-
</>
45+
<BaseCodeBox
46+
onCopy={onCopy}
47+
language={getLanguageDisplayName(language)}
48+
className={className}
49+
buttonText="Copy to clipboard"
50+
{...props}
51+
>
52+
{children}
53+
</BaseCodeBox>
5654
);
5755
};
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import { strict as assert } from 'node:assert';
2+
import { mkdir, rm, writeFile } from 'node:fs/promises';
3+
import { tmpdir } from 'node:os';
4+
import { join } from 'node:path';
5+
import { after, before, test } from 'node:test';
6+
7+
import cssLoader from '../css.mjs';
8+
9+
// Create a temporary directory for test files
10+
const testDir = join(tmpdir(), `css-test-${Date.now()}`);
11+
12+
before(async () => {
13+
await mkdir(testDir, { recursive: true });
14+
});
15+
16+
after(async () => {
17+
await rm(testDir, { recursive: true, force: true });
18+
});
19+
20+
test('cssLoader - plugin has required properties', () => {
21+
const plugin = cssLoader();
22+
23+
assert.equal(plugin.name, 'css-loader');
24+
assert.ok(plugin.load);
25+
assert.ok(plugin.load.handler);
26+
assert.ok(plugin.load.filter);
27+
assert.ok(plugin.buildEnd);
28+
});
29+
30+
test('cssLoader - filter matches .module.css files', () => {
31+
const plugin = cssLoader();
32+
const { filter } = plugin.load;
33+
34+
assert.ok(filter.id.include.test('/path/to/styles.module.css'));
35+
assert.ok(filter.id.include.test('component.module.css'));
36+
assert.ok(!filter.id.include.test('styles.css'));
37+
assert.ok(!filter.id.include.test('styles.module.scss'));
38+
});
39+
40+
test('cssLoader - processes CSS module file', async () => {
41+
const plugin = cssLoader();
42+
const testFile = join(testDir, 'test.module.css');
43+
44+
// Write a simple CSS module file
45+
await writeFile(
46+
testFile,
47+
`.button {
48+
color: blue;
49+
background: white;
50+
}
51+
.container {
52+
padding: 10px;
53+
}`
54+
);
55+
56+
const result = await plugin.load.handler(testFile);
57+
58+
// Should return JS module with exports
59+
assert.ok(result.code);
60+
assert.equal(result.moduleType, 'js');
61+
62+
// Parse the exported default object
63+
const exportMatch = result.code.match(/export default (.+);/);
64+
assert.ok(exportMatch, 'Should export an object');
65+
66+
const exports = JSON.parse(exportMatch[1]);
67+
assert.ok(exports.button, 'Should have button class');
68+
assert.ok(exports.container, 'Should have container class');
69+
70+
// Scoped names should not be the original names
71+
assert.notEqual(exports.button, 'button');
72+
assert.notEqual(exports.container, 'container');
73+
});
74+
75+
test('cssLoader - caches processed files', async () => {
76+
const plugin = cssLoader();
77+
const testFile = join(testDir, 'cached.module.css');
78+
79+
await writeFile(testFile, '.test { color: red; }');
80+
81+
// First call
82+
const result1 = await plugin.load.handler(testFile);
83+
84+
// Second call (should hit cache)
85+
const result2 = await plugin.load.handler(testFile);
86+
87+
// Both results should be identical
88+
assert.equal(result1.code, result2.code);
89+
assert.equal(result1.moduleType, result2.moduleType);
90+
});
91+
92+
test('cssLoader - collects CSS chunks', async () => {
93+
const plugin = cssLoader();
94+
const testFile1 = join(testDir, 'chunk1.module.css');
95+
const testFile2 = join(testDir, 'chunk2.module.css');
96+
97+
await writeFile(testFile1, '.class1 { color: red; }');
98+
await writeFile(testFile2, '.class2 { color: blue; }');
99+
100+
// Process both files
101+
await plugin.load.handler(testFile1);
102+
await plugin.load.handler(testFile2);
103+
104+
// Mock emitFile to capture output
105+
const emittedFiles = [];
106+
const mockContext = {
107+
emitFile(file) {
108+
emittedFiles.push(file);
109+
},
110+
};
111+
112+
// Call buildEnd with mock context
113+
plugin.buildEnd.call(mockContext);
114+
115+
// Should emit one CSS file
116+
assert.equal(emittedFiles.length, 1);
117+
assert.equal(emittedFiles[0].type, 'asset');
118+
assert.equal(emittedFiles[0].name, 'styles.css');
119+
120+
// CSS should contain both classes (with scoped names)
121+
const cssContent = emittedFiles[0].source;
122+
assert.ok(typeof cssContent === 'string');
123+
assert.ok(cssContent.length > 0);
124+
});
125+
126+
test('cssLoader - handles multiple CSS modules with different class names', async () => {
127+
const plugin = cssLoader();
128+
const testFile1 = join(testDir, 'buttons.module.css');
129+
const testFile2 = join(testDir, 'layout.module.css');
130+
131+
await writeFile(
132+
testFile1,
133+
`.primary { background: blue; }
134+
.secondary { background: gray; }`
135+
);
136+
137+
await writeFile(
138+
testFile2,
139+
`.header { height: 60px; }
140+
.footer { height: 40px; }`
141+
);
142+
143+
const result1 = await plugin.load.handler(testFile1);
144+
const result2 = await plugin.load.handler(testFile2);
145+
146+
// Parse exports from both modules
147+
const exports1 = JSON.parse(result1.code.match(/export default (.+);/)[1]);
148+
const exports2 = JSON.parse(result2.code.match(/export default (.+);/)[1]);
149+
150+
// Each should have their own classes
151+
assert.ok(exports1.primary);
152+
assert.ok(exports1.secondary);
153+
assert.ok(exports2.header);
154+
assert.ok(exports2.footer);
155+
156+
// Classes from different files should not overlap
157+
assert.ok(!exports1.header);
158+
assert.ok(!exports2.primary);
159+
});
160+
161+
test('cssLoader - buildEnd skips emitting when no CSS processed', () => {
162+
const plugin = cssLoader();
163+
164+
const emittedFiles = [];
165+
const mockContext = {
166+
emitFile(file) {
167+
emittedFiles.push(file);
168+
},
169+
};
170+
171+
// Call buildEnd without processing any CSS files
172+
plugin.buildEnd.call(mockContext);
173+
174+
// Should not emit anything
175+
assert.equal(emittedFiles.length, 0);
176+
});
177+
178+
test('cssLoader - handles CSS with pseudo-selectors', async () => {
179+
const plugin = cssLoader();
180+
const testFile = join(testDir, 'pseudo.module.css');
181+
182+
await writeFile(
183+
testFile,
184+
`.button:hover {
185+
color: red;
186+
}
187+
.button:active {
188+
color: darkred;
189+
}`
190+
);
191+
192+
const result = await plugin.load.handler(testFile);
193+
const exports = JSON.parse(result.code.match(/export default (.+);/)[1]);
194+
195+
assert.ok(exports.button);
196+
});
197+
198+
test('cssLoader - processes CSS with multiple classes on same element', async () => {
199+
const plugin = cssLoader();
200+
const testFile = join(testDir, 'multi.module.css');
201+
202+
await writeFile(
203+
testFile,
204+
`.btn { padding: 10px; }
205+
.btnPrimary { background: blue; }
206+
.btnSecondary { background: gray; }`
207+
);
208+
209+
const result = await plugin.load.handler(testFile);
210+
const exports = JSON.parse(result.code.match(/export default (.+);/)[1]);
211+
212+
assert.ok(exports.btn);
213+
assert.ok(exports.btnPrimary);
214+
assert.ok(exports.btnSecondary);
215+
});
216+
217+
test('cssLoader - separate plugin instances have separate caches', async () => {
218+
const plugin1 = cssLoader();
219+
const plugin2 = cssLoader();
220+
221+
const testFile = join(testDir, 'separate.module.css');
222+
await writeFile(testFile, '.test { color: green; }');
223+
224+
await plugin1.load.handler(testFile);
225+
226+
// Mock emitFile for both plugins
227+
const emitted1 = [];
228+
const emitted2 = [];
229+
230+
plugin1.buildEnd.call({
231+
emitFile(file) {
232+
emitted1.push(file);
233+
},
234+
});
235+
236+
plugin2.buildEnd.call({
237+
emitFile(file) {
238+
emitted2.push(file);
239+
},
240+
});
241+
242+
// Plugin 1 should emit CSS, plugin 2 should not (separate chunk tracking)
243+
assert.equal(emitted1.length, 1);
244+
assert.equal(emitted2.length, 0);
245+
});

0 commit comments

Comments
 (0)