Skip to content

Commit cb3a7f9

Browse files
committed
test(integration): cover host build, CSS & shared deps
1 parent 83e37a9 commit cb3a7f9

File tree

16 files changed

+375
-56
lines changed

16 files changed

+375
-56
lines changed

integration/build.test.ts

Lines changed: 12 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,39 @@
1-
import defu from 'defu';
21
import { resolve } from 'path';
3-
import { build, Rollup } from 'vite';
42
import { describe, expect, it } from 'vitest';
5-
import { federation } from '../src/index';
6-
import { findAsset, findChunk, getChunkNames } from './helpers/matchers';
3+
import { buildFixture, FIXTURES } from './helpers/build';
4+
import { findAsset, findChunk, getAllChunkCode, getChunkNames } from './helpers/matchers';
75

8-
const FIXTURES = resolve(__dirname, 'fixtures');
9-
const BASIC_REMOTE = resolve(FIXTURES, 'basic-remote');
10-
11-
async function buildFixture(optionsOverrides?: Partial<Parameters<typeof federation>[0]>) {
12-
const defaultOptions = {
13-
name: 'basicRemote',
14-
filename: 'remoteEntry.js',
15-
exposes: {
16-
'./exposed': resolve(BASIC_REMOTE, 'exposed-module.js'),
17-
},
18-
shared: {},
19-
dts: false,
20-
} satisfies Parameters<typeof federation>[0];
21-
22-
const mfOptions = defu(defaultOptions, optionsOverrides);
23-
24-
const result = await build({
25-
root: BASIC_REMOTE,
26-
logLevel: 'silent',
27-
plugins: [federation(mfOptions)],
28-
build: {
29-
write: false,
30-
target: 'chrome89',
31-
},
32-
});
33-
34-
// Vite returns RollupOutput[] only with multiple rollupOptions.output entries.
35-
// Our test configs should never produce that — fail fast if they do.
36-
expect(Array.isArray(result), 'E xpected a single RollupOutput, not an array').toBe(false);
37-
return result as Rollup.RollupOutput;
38-
}
6+
const BASIC_REMOTE_MF_OPTIONS = {
7+
exposes: {
8+
'./exposed': resolve(FIXTURES, 'basic-remote', 'exposed-module.js'),
9+
},
10+
};
3911

4012
describe('build', () => {
4113
describe('remote', () => {
4214
it('produces a remoteEntry chunk', async () => {
43-
const output = await buildFixture();
15+
const output = await buildFixture({ mfOptions: BASIC_REMOTE_MF_OPTIONS });
4416
const chunks = getChunkNames(output);
4517
expect(chunks.some((name) => name.includes('remoteEntry'))).toBe(true);
4618
});
4719

4820
it('remoteEntry contains federation runtime init with correct name', async () => {
49-
const output = await buildFixture();
21+
const output = await buildFixture({ mfOptions: BASIC_REMOTE_MF_OPTIONS });
5022
const remoteEntry = findChunk(output, 'remoteEntry');
5123
expect(remoteEntry).toBeDefined();
5224
expect(remoteEntry!.code).toContain('basicRemote');
5325
expect(remoteEntry!.code).toContain('moduleCache');
5426
});
5527

5628
it('exposed module content is included in output', async () => {
57-
const output = await buildFixture();
58-
const allCode = output.output
59-
.filter((o): o is Rollup.OutputChunk => o.type === 'chunk')
60-
.map((c) => c.code)
61-
.join('\n');
62-
29+
const output = await buildFixture({ mfOptions: BASIC_REMOTE_MF_OPTIONS });
30+
const allCode = getAllChunkCode(output);
6331
expect(allCode).toContain('Hello');
6432
});
6533

6634
it('generates mf-manifest.json when manifest is enabled', async () => {
6735
const manifestOutput = await buildFixture({
68-
manifest: true,
36+
mfOptions: { ...BASIC_REMOTE_MF_OPTIONS, manifest: true },
6937
});
7038

7139
const manifest = findAsset(manifestOutput, 'mf-manifest.json');

integration/css-manifest.test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { resolve } from 'path';
2+
import { describe, expect, it } from 'vitest';
3+
import type { ModuleFederationOptions } from '../src/utils/normalizeModuleFederationOptions';
4+
import { buildFixture, FIXTURES } from './helpers/build';
5+
import { parseManifest } from './helpers/matchers';
6+
7+
const CSS_BASE_MF_OPTIONS = {
8+
name: 'cssRemote',
9+
filename: 'remoteEntry.js',
10+
exposes: {
11+
'./widget': resolve(FIXTURES, 'css-remote', 'exposed-module.js'),
12+
},
13+
manifest: true,
14+
dts: false,
15+
} satisfies Partial<ModuleFederationOptions>;
16+
17+
interface ManifestExpose {
18+
id: string;
19+
name: string;
20+
path: string;
21+
assets: {
22+
js: { sync: string[]; async: string[] };
23+
css: { sync: string[]; async: string[] };
24+
};
25+
}
26+
27+
describe('css manifest', () => {
28+
it('tracks CSS and JS assets under the correct expose', async () => {
29+
const output = await buildFixture({
30+
fixture: 'css-remote',
31+
mfOptions: CSS_BASE_MF_OPTIONS,
32+
});
33+
const manifest = parseManifest(output) as Record<string, unknown>;
34+
expect(manifest).toBeDefined();
35+
expect(manifest).toHaveProperty('exposes');
36+
37+
const exposes = manifest.exposes as ManifestExpose[];
38+
const widget = exposes.find((e) => e.name === 'widget');
39+
expect(widget).toBeDefined();
40+
41+
const allCssFiles = [...widget!.assets.css.sync, ...widget!.assets.css.async];
42+
expect(allCssFiles.length).toBeGreaterThanOrEqual(1);
43+
for (const file of allCssFiles) {
44+
expect(file).toMatch(/\.css$/);
45+
}
46+
47+
const allJsFiles = [...widget!.assets.js.sync, ...widget!.assets.js.async];
48+
expect(allJsFiles.length).toBeGreaterThanOrEqual(1);
49+
for (const file of allJsFiles) {
50+
expect(file).toMatch(/\.js$/);
51+
}
52+
});
53+
54+
it('adds CSS to all exposes when bundleAllCSS is enabled', async () => {
55+
const output = await buildFixture({
56+
fixture: 'css-remote',
57+
mfOptions: { ...CSS_BASE_MF_OPTIONS, bundleAllCSS: true },
58+
});
59+
const manifest = parseManifest(output) as Record<string, unknown>;
60+
expect(manifest).toBeDefined();
61+
62+
const exposes = manifest.exposes as ManifestExpose[];
63+
expect(exposes.length).toBeGreaterThanOrEqual(1);
64+
65+
for (const expose of exposes) {
66+
const cssCount = expose.assets.css.sync.length + expose.assets.css.async.length;
67+
expect(
68+
cssCount,
69+
`expose "${expose.name}" should have at least one CSS asset`
70+
).toBeGreaterThanOrEqual(1);
71+
}
72+
});
73+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
const mod = import('remote1/Module');
2+
mod.then((m) => console.log(m));
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!doctype html>
2+
<html>
3+
<head></head>
4+
<body>
5+
<div id="app"></div>
6+
<script type="module" src="./entry.js"></script>
7+
</body>
8+
</html>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('css remote entry');
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import './styles.css';
2+
3+
export function Widget() {
4+
return '<div class="widget">Widget</div>';
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<!doctype html>
2+
<html>
3+
<body>
4+
<div id="app"></div>
5+
<script type="module" src="./entry.js"></script>
6+
</body>
7+
</html>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.widget {
2+
color: red;
3+
padding: 8px;
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('shared remote entry');
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import defu from 'defu';
2+
3+
export function merge(a, b) {
4+
return defu(a, b);
5+
}

0 commit comments

Comments
 (0)