Skip to content

Commit 04c7109

Browse files
committed
Improve microfrontend e2e test with real plugin setup with module fed
1 parent 1e1d0fc commit 04c7109

File tree

21 files changed

+3581
-331
lines changed

21 files changed

+3581
-331
lines changed

LICENSE-3rdparty.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ prod,react-dom,MIT,Copyright (c) Facebook, Inc. and its affiliates.
1313
dev,typedoc,Apache-2.0,TypeStrong
1414
dev,@eslint/js,MIT,Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
1515
dev,@jsdevtools/coverage-istanbul-loader,MIT,Copyright (c) 2015 James Messinger
16+
dev,@module-federation/enhanced,MIT, Copyright (c) 2020 ScriptedAlchemy LLC (Zack Jackson) Zhou Shaw (zhouxiao)
1617
dev,@playwright/test,Apache-2.0,Copyright Microsoft Corporation
1718
dev,@swc/core,Apache-2.0,Copyright (c) SWC Contributors
1819
dev,@types/chrome,MIT,Copyright Microsoft Corporation

eslint.config.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,15 @@ export default tseslint.config(
469469
},
470470
},
471471

472+
{
473+
files: ['test/apps/microfrontend/**/*.ts'],
474+
rules: {
475+
// Module Federation remote entries (app1/app1, app2/app2) are resolved at runtime
476+
// and have no static filesystem representation.
477+
'import/no-unresolved': ['error', { ignore: ['^app1/', '^app2/'] }],
478+
},
479+
},
480+
472481
{
473482
files: ['packages/core/src/tools/**/*.ts'],
474483
ignores: [SPEC_FILES],

scripts/build/build-test-apps.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ runMain(async () => {
1818
buildApp('test/apps/react-router-v6-app')
1919
buildApp('test/apps/react-heavy-spa')
2020
buildApp('test/apps/react-shopist-like')
21+
buildApp('test/apps/microfrontend')
2122
await buildReactRouterv7App()
2223
await buildExtensions()
2324

test/apps/microfrontend/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist
2+
node_modules
3+
.yarn/*

test/apps/microfrontend/app1.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
export function createButton(containerApp: HTMLDivElement, id: string, clickHandler: () => void): HTMLButtonElement {
2+
const button = document.createElement('button')
3+
const appName = containerApp.id
4+
button.id = `${appName}-${id}`
5+
button.textContent = `${appName}-${id}`
6+
button.onclick = clickHandler
7+
containerApp.appendChild(button)
8+
return button
9+
}
10+
11+
export function createAppContainer(id: string, title: string, borderColor: string): HTMLDivElement {
12+
const appContainer = document.createElement('div')
13+
appContainer.id = id
14+
appContainer.style.flex = '1'
15+
appContainer.style.border = `2px solid ${borderColor}`
16+
appContainer.style.padding = '10px'
17+
18+
const appTitle = document.createElement('h2')
19+
appTitle.textContent = title
20+
appContainer.appendChild(appTitle)
21+
22+
return appContainer
23+
}
24+
25+
const containerApp = createAppContainer('app1', 'App 1 (mf-app1-service v1.0.0)', 'blue')
26+
document.body.appendChild(containerApp)
27+
28+
createButton(containerApp, 'fetch-button', () => {
29+
fetch('/ok').then(
30+
() => {
31+
/* empty */
32+
},
33+
() => {
34+
/* empty */
35+
}
36+
)
37+
})
38+
39+
createButton(containerApp, 'xhr-button', () => {
40+
const xhr = new XMLHttpRequest()
41+
xhr.open('GET', '/ok')
42+
xhr.send()
43+
})
44+
45+
createButton(containerApp, 'error-button', () => {
46+
window.DD_RUM.addError(new Error('mf-app1-error'))
47+
})
48+
49+
createButton(containerApp, 'console-error-button', () => {
50+
console.error('mf-app1-console-error')
51+
})
52+
53+
createButton(containerApp, 'runtime-error-button', () => {
54+
throw new Error('mf-app1-runtime-error')
55+
})
56+
57+
createButton(containerApp, 'loaf-button', () => {
58+
const end = performance.now() + 55
59+
while (performance.now() < end) {
60+
// block the handler for ~55ms to trigger a long task
61+
}
62+
})
63+
64+
createButton(containerApp, 'custom-action-button', () => {
65+
window.DD_RUM.addAction('mf-app1-action')
66+
})
67+
68+
createButton(containerApp, 'vital-button', () => {
69+
const ref = window.DD_RUM.startDurationVital('mf-app1-vital')
70+
window.DD_RUM.stopDurationVital(ref)
71+
})
72+
73+
createButton(containerApp, 'view-button', () => {
74+
window.DD_RUM.startView({ name: 'mf-app1-view' })
75+
})

test/apps/microfrontend/app2.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
export function createButton(containerApp: HTMLDivElement, id: string, clickHandler: () => void): HTMLButtonElement {
2+
const button = document.createElement('button')
3+
const appName = containerApp.id
4+
button.id = `${appName}-${id}`
5+
button.textContent = `${appName}-${id}`
6+
button.onclick = () => clickHandler()
7+
containerApp.appendChild(button)
8+
return button
9+
}
10+
11+
export function createAppContainer(id: string, title: string, borderColor: string): HTMLDivElement {
12+
const appContainer = document.createElement('div')
13+
appContainer.id = id
14+
appContainer.style.flex = '1'
15+
appContainer.style.border = `2px solid ${borderColor}`
16+
appContainer.style.padding = '10px'
17+
18+
const appTitle = document.createElement('h2')
19+
appTitle.textContent = title
20+
appContainer.appendChild(appTitle)
21+
22+
return appContainer
23+
}
24+
25+
const containerApp = createAppContainer('app2', 'App 2 (mf-app2-service v0.2.0)', 'green')
26+
document.body.appendChild(containerApp)
27+
28+
createButton(containerApp, 'fetch-button', () => {
29+
fetch('/ok').then(
30+
() => {
31+
/* empty */
32+
},
33+
() => {
34+
/* empty */
35+
}
36+
)
37+
})
38+
39+
createButton(containerApp, 'xhr-button', () => {
40+
const xhr = new XMLHttpRequest()
41+
xhr.open('GET', '/ok')
42+
xhr.send()
43+
})
44+
45+
createButton(containerApp, 'error-button', () => {
46+
window.DD_RUM.addError(new Error('mf-app2-error'))
47+
})
48+
49+
createButton(containerApp, 'console-error-button', () => {
50+
console.error('mf-app2-console-error')
51+
})
52+
53+
createButton(containerApp, 'runtime-error-button', () => {
54+
throw new Error('mf-app2-runtime-error')
55+
})
56+
57+
createButton(containerApp, 'loaf-button', () => {
58+
const end = performance.now() + 55
59+
while (performance.now() < end) {
60+
// block the handler for ~55ms to trigger a long task
61+
}
62+
})
63+
64+
createButton(containerApp, 'custom-action-button', () => {
65+
window.DD_RUM.addAction('mf-app2-action')
66+
})
67+
68+
createButton(containerApp, 'vital-button', () => {
69+
const ref = window.DD_RUM.startDurationVital('mf-app2-vital')
70+
window.DD_RUM.stopDurationVital(ref)
71+
})
72+
73+
createButton(containerApp, 'view-button', () => {
74+
window.DD_RUM.startView({ name: 'mf-app2-view' })
75+
})
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// @ts-ignore The dynamic import keeps shell.ts out of the entry chunk,
2+
void import('./shell')
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "microfrontend-test-app",
3+
"private": true,
4+
"scripts": {
5+
"build": "webpack --config webpack.app1.js && webpack --config webpack.app2.js && webpack --config webpack.shell.js"
6+
},
7+
"devDependencies": {
8+
"@datadog/webpack-plugin": "^3.1.0",
9+
"@module-federation/enhanced": "^2.0.1",
10+
"ts-loader": "9.5.4",
11+
"typescript": "5.9.3",
12+
"webpack": "5.105.2",
13+
"webpack-cli": "^6.0.1"
14+
}
15+
}

test/apps/microfrontend/shell.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
async function initShell() {
2+
await Promise.all([import('app1/app1'), import('app2/app2')])
3+
}
4+
5+
void initShell()

test/apps/microfrontend/tools.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export function createButton(containerApp: HTMLDivElement, id: string, clickHandler: () => void): HTMLButtonElement {
2+
const button = document.createElement('button')
3+
const appName = containerApp.id
4+
button.id = `${appName}-${id}`
5+
button.textContent = `${appName}-${id}`
6+
button.onclick = clickHandler
7+
containerApp.appendChild(button)
8+
return button
9+
}
10+
11+
export function createAppContainer(id: string, title: string, borderColor: string): HTMLDivElement {
12+
const appContainer = document.createElement('div')
13+
appContainer.id = id
14+
appContainer.style.flex = '1'
15+
appContainer.style.border = `2px solid ${borderColor}`
16+
appContainer.style.padding = '10px'
17+
18+
const appTitle = document.createElement('h2')
19+
appTitle.textContent = title
20+
appContainer.appendChild(appTitle)
21+
22+
return appContainer
23+
}

0 commit comments

Comments
 (0)