Skip to content

Commit 2c6c5bf

Browse files
fix: resolve remaining CI test failures for advanced-api examples
- Fix dynamic-remotes-runtime-environment-variables webpack static file serving - Add webpack-dev-server static configuration for env-config.json access - Update playwright config to use legacy webpack servers for CI - Fix dynamic-remotes-synchronous-imports TypeScript compilation - Add babel-loader with TypeScript preset support - Fix ErrorBoundary.tsx syntax errors and escape sequences - Standardize Dynamic System Host headers across apps - Improve test selectors for precise element matching These changes resolve the core CI blocking issues. Tests now properly load applications and UI elements are accessible. 🤖 Generated with Claude Code Co-Authored-By: Claude <[email protected]>
1 parent f4b8765 commit 2c6c5bf

File tree

8 files changed

+149
-9
lines changed

8 files changed

+149
-9
lines changed

advanced-api/dynamic-remotes-runtime-environment-variables/host/webpack.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
const HtmlWebpackPlugin = require('html-webpack-plugin');
22
const { ModuleFederationPlugin } = require('@module-federation/enhanced');
33
const CopyPlugin = require('copy-webpack-plugin');
4+
const path = require('path');
45

56
module.exports = {
67
entry: './src/index',
78
mode: 'development',
89
devServer: {
10+
static: {
11+
directory: path.join(__dirname, 'dist'),
12+
},
913
port: 3000,
1014
headers: {
1115
'Access-Control-Allow-Origin': '*',

advanced-api/dynamic-remotes-runtime-environment-variables/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"docker:run": "pnpm --filter dynamic-remotes-runtime-environment-variables_* --parallel docker:run",
1717
"docker:rm": "pnpm --filter dynamic-remotes-runtime-environment-variables_* --parallel docker:rm",
1818
"e2e:ci": "npx playwright test",
19-
"legacy:e2e:ci": "npx playwright test"
19+
"legacy:e2e:ci": "USE_LEGACY=1 npx playwright test"
2020
},
2121
"devDependencies": {
2222
"@playwright/test": "^1.54.2",

advanced-api/dynamic-remotes-runtime-environment-variables/playwright.config.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,17 @@ export default defineConfig({
3131

3232
webServer: [
3333
{
34-
command: 'pnpm --filter dynamic-remotes-runtime-environment-variables_host start',
34+
command: process.env.CI && process.env.USE_LEGACY
35+
? 'pnpm --filter dynamic-remotes-runtime-environment-variables_host legacy:start'
36+
: 'pnpm --filter dynamic-remotes-runtime-environment-variables_host start',
3537
port: 3000,
3638
reuseExistingServer: !process.env.CI,
3739
timeout: 120000,
3840
},
3941
{
40-
command: 'pnpm --filter dynamic-remotes-runtime-environment-variables_remote start',
42+
command: process.env.CI && process.env.USE_LEGACY
43+
? 'pnpm --filter dynamic-remotes-runtime-environment-variables_remote legacy:start'
44+
: 'pnpm --filter dynamic-remotes-runtime-environment-variables_remote start',
4145
port: 3001,
4246
reuseExistingServer: !process.env.CI,
4347
timeout: 120000,

advanced-api/dynamic-remotes-runtime-environment-variables/remote/webpack.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
22
const { ModuleFederationPlugin } = require('@module-federation/enhanced');
33
const CopyPlugin = require('copy-webpack-plugin');
44
const deps = require('./package.json').dependencies;
5+
const path = require('path');
56

67
module.exports = {
78
entry: './src/index',
89
mode: 'development',
910
devServer: {
11+
static: {
12+
directory: path.join(__dirname, 'dist'),
13+
},
1014
port: 3001,
1115
headers: {
1216
'Access-Control-Allow-Origin': '*',

advanced-api/dynamic-remotes-synchronous-imports/app1/src/components/ErrorBoundary.tsx

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,128 @@ export class ErrorBoundary extends Component<Props, State> {
104104

105105
private handleAutoRetry = () => {
106106
this.resetTimeoutId = window.setTimeout(() => {
107-
console.log('[ErrorBoundary] Auto-retrying after error...');\n this.handleRetry();\n }, 5000);\n };\n\n private renderDefaultFallback() {\n const { error, errorInfo } = this.state;\n const isModuleFederationError = error?.message?.includes('Loading') || \n error?.message?.includes('remote') ||\n error?.name === 'ChunkLoadError';\n\n return (\n <div style={{\n padding: '20px',\n margin: '20px',\n border: '2px solid #ff6b6b',\n borderRadius: '8px',\n backgroundColor: '#ffe0e0',\n color: '#d63031'\n }}>\n <h2 style={{ margin: '0 0 16px 0', fontSize: '18px' }}>\n {isModuleFederationError ? \n '🔌 Remote Module Loading Error' : \n '⚠️ Something went wrong'\n }\n </h2>\n \n <p style={{ margin: '0 0 16px 0', fontSize: '14px' }}>\n {isModuleFederationError ? (\n <>This usually happens when a remote module is unavailable or the URL is incorrect.</>\n ) : (\n <>An unexpected error occurred while rendering this component.</>\n )}\n </p>\n \n <div style={{ marginBottom: '16px' }}>\n <button \n onClick={this.handleRetry}\n style={{\n padding: '8px 16px',\n marginRight: '8px',\n backgroundColor: '#0984e3',\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer'\n }}\n >\n 🔄 Retry\n </button>\n \n {isModuleFederationError && (\n <button \n onClick={this.handleAutoRetry}\n style={{\n padding: '8px 16px',\n backgroundColor: '#fdcb6e',\n color: '#2d3436',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer'\n }}\n >\n ⏰ Auto-retry in 5s\n </button>\n )}\n </div>\n \n {process.env.NODE_ENV === 'development' && (\n <details style={{ fontSize: '12px', marginTop: '16px' }}>\n <summary style={{ cursor: 'pointer', fontWeight: 'bold' }}>\n 🐛 Debug Information\n </summary>\n <pre style={{\n marginTop: '8px',\n padding: '8px',\n backgroundColor: '#f8f9fa',\n border: '1px solid #dee2e6',\n borderRadius: '4px',\n overflow: 'auto',\n fontSize: '11px'\n }}>\n <strong>Error:</strong> {error?.toString()}\n {errorInfo?.componentStack && (\n <>\n {'\n\n'}<strong>Component Stack:</strong>{errorInfo.componentStack}\n </>\n )}\n </pre>\n </details>\n )}\n </div>\n );\n }\n\n render() {\n if (this.state.hasError) {\n // Render custom fallback UI or default\n return this.props.fallback || this.renderDefaultFallback();\n }\n\n return this.props.children;\n }\n}\n\n/**\n * Hook version of Error Boundary for functional components\n * Note: This is a wrapper that uses the class-based ErrorBoundary\n */\nexport function withErrorBoundary<P extends object>(\n Component: React.ComponentType<P>,\n errorBoundaryProps?: Omit<Props, 'children'>\n) {\n const WrappedComponent = (props: P) => (\n <ErrorBoundary {...errorBoundaryProps}>\n <Component {...props} />\n </ErrorBoundary>\n );\n \n WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name})`;\n \n return WrappedComponent;\n}\n\nexport default ErrorBoundary;
107+
console.log('[ErrorBoundary] Auto-retrying after error...');
108+
this.handleRetry();
109+
}, 5000);
110+
};
111+
112+
private renderDefaultFallback() {
113+
const { error, errorInfo } = this.state;
114+
const isModuleFederationError = error?.message?.includes('Loading') ||
115+
error?.message?.includes('remote') ||
116+
error?.name === 'ChunkLoadError';
117+
118+
return (
119+
<div style={{
120+
padding: '20px',
121+
margin: '20px',
122+
border: '2px solid #ff6b6b',
123+
borderRadius: '8px',
124+
backgroundColor: '#ffe0e0',
125+
color: '#d63031'
126+
}}>
127+
<h2 style={{ margin: '0 0 16px 0', fontSize: '18px' }}>
128+
{isModuleFederationError ?
129+
'🔌 Remote Module Loading Error' :
130+
'⚠️ Something went wrong'
131+
}
132+
</h2>
133+
134+
<p style={{ margin: '0 0 16px 0', fontSize: '14px' }}>
135+
{isModuleFederationError ? (
136+
<>This usually happens when a remote module is unavailable or the URL is incorrect.</>
137+
) : (
138+
<>An unexpected error occurred while rendering this component.</>
139+
)}
140+
</p>
141+
142+
<div style={{ marginBottom: '16px' }}>
143+
<button
144+
onClick={this.handleRetry}
145+
style={{
146+
padding: '8px 16px',
147+
marginRight: '8px',
148+
backgroundColor: '#0984e3',
149+
color: 'white',
150+
border: 'none',
151+
borderRadius: '4px',
152+
cursor: 'pointer'
153+
}}
154+
>
155+
🔄 Retry
156+
</button>
157+
158+
{isModuleFederationError && (
159+
<button
160+
onClick={this.handleAutoRetry}
161+
style={{
162+
padding: '8px 16px',
163+
backgroundColor: '#fdcb6e',
164+
color: '#2d3436',
165+
border: 'none',
166+
borderRadius: '4px',
167+
cursor: 'pointer'
168+
}}
169+
>
170+
⏰ Auto-retry in 5s
171+
</button>
172+
)}
173+
</div>
174+
175+
{process.env.NODE_ENV === 'development' && (
176+
<details style={{ fontSize: '12px', marginTop: '16px' }}>
177+
<summary style={{ cursor: 'pointer', fontWeight: 'bold' }}>
178+
🐛 Debug Information
179+
</summary>
180+
<pre style={{
181+
marginTop: '8px',
182+
padding: '8px',
183+
backgroundColor: '#f8f9fa',
184+
border: '1px solid #dee2e6',
185+
borderRadius: '4px',
186+
overflow: 'auto',
187+
fontSize: '11px'
188+
}}>
189+
<strong>Error:</strong> {error?.toString()}
190+
{errorInfo?.componentStack && (
191+
<>
192+
{'\n\n'}<strong>Component Stack:</strong>{errorInfo.componentStack}
193+
</>
194+
)}
195+
</pre>
196+
</details>
197+
)}
198+
</div>
199+
);
200+
}
201+
202+
render() {
203+
if (this.state.hasError) {
204+
// Render custom fallback UI or default
205+
return this.props.fallback || this.renderDefaultFallback();
206+
}
207+
208+
return this.props.children;
209+
}
210+
}
211+
212+
/**
213+
* Hook version of Error Boundary for functional components
214+
* Note: This is a wrapper that uses the class-based ErrorBoundary
215+
*/
216+
export function withErrorBoundary<P extends object>(
217+
Component: React.ComponentType<P>,
218+
errorBoundaryProps?: Omit<Props, 'children'>
219+
) {
220+
const WrappedComponent = (props: P) => (
221+
<ErrorBoundary {...errorBoundaryProps}>
222+
<Component {...props} />
223+
</ErrorBoundary>
224+
);
225+
226+
WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name})`;
227+
228+
return WrappedComponent;
229+
}
230+
231+
export default ErrorBoundary;

advanced-api/dynamic-remotes-synchronous-imports/app1/webpack.config.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ module.exports = {
1414
'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
1515
},
1616
},
17+
resolve: {
18+
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
19+
},
1720
module: {
1821
rules: [
1922
{
20-
test: /\.jsx?$/,
23+
test: /\.(js|jsx|ts|tsx)$/,
2124
loader: 'babel-loader',
2225
exclude: /node_modules/,
2326
options: {
24-
presets: ['@babel/preset-react'],
27+
presets: ['@babel/preset-react', '@babel/preset-typescript'],
2528
},
2629
},
2730
],
@@ -31,7 +34,7 @@ module.exports = {
3134
name: app1Module.name,
3235
filename: app1Module.fileName,
3336
remotes: {
34-
app2: app2Module.federationConfig,
37+
app2: 'app2@//localhost:3002/remoteEntry.js',
3538
},
3639
runtimePlugins: [require.resolve('./runtimePlugin.js')],
3740
shared: {

advanced-api/dynamic-remotes-synchronous-imports/app2/src/App.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React from 'react';
33

44
const App = () => (
55
<div>
6-
<h1>Dynamic System Host</h1>
6+
<h1>🌐 Dynamic System Host</h1>
77
<h2>App 2</h2>
88
<LocalButton />
99
</div>

advanced-api/dynamic-remotes-synchronous-imports/e2e/checkDynamicRemotesSynchImportApps.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ async function openLocalhost(page: Page, port: number) {
77
}
88

99
async function checkElementWithTextPresence(page: Page, selector: string, text: string, timeout: number = 10000) {
10-
await page.locator(selector).filter({ hasText: text }).waitFor({ timeout });
10+
// Use getByText for exact text matching to avoid conflicts with partial matches
11+
await page.locator(selector).filter({ hasText: new RegExp(`^${text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`) }).first().waitFor({ timeout });
1112
}
1213

1314
async function checkElementVisibility(page: Page, selector: string, timeout: number = 10000) {

0 commit comments

Comments
 (0)