Skip to content

Commit f21c2df

Browse files
refactor: migrate runtime env e2e test to Playwright (#4403)
1 parent 1379ded commit f21c2df

File tree

7 files changed

+103
-331
lines changed

7 files changed

+103
-331
lines changed

advanced-api/dynamic-remotes-runtime-environment-variables/cypress.env.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

advanced-api/dynamic-remotes-runtime-environment-variables/e2e/checkDynamicRemotesRuntimesApps.cy.ts

Lines changed: 0 additions & 104 deletions
This file was deleted.
Lines changed: 56 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -1,187 +1,67 @@
1-
import { test, expect, Page } from '@playwright/test';
2-
3-
// Helper functions
4-
async function openLocalhost(page: Page, port: number) {
5-
// Set up console and error logging
6-
const consoleMessages: string[] = [];
7-
const pageErrors: string[] = [];
8-
9-
page.on('console', (msg) => {
10-
consoleMessages.push(`[${msg.type()}] ${msg.text()}`);
11-
});
12-
13-
page.on('pageerror', (err) => {
14-
pageErrors.push(`Page error: ${err.message}\nStack: ${err.stack || 'No stack trace'}`);
15-
});
1+
import { test, expect } from '@playwright/test';
2+
import { BasePage } from './utils/base-test';
3+
import { selectors } from './utils/selectors';
4+
import { Constants } from './utils/constants';
165

17-
await page.goto(`http://localhost:${port}`);
18-
19-
// Wait for the page to load but don't wait for networkidle since env loading might be polling
20-
await page.waitForLoadState('load');
21-
22-
// Wait for the root element to be attached. It may not be visible immediately
23-
// (for example, the remote app shows an empty #root while it loads env config),
24-
// so we only wait for the element to exist in the DOM.
25-
await page.waitForSelector('#root', { state: 'attached', timeout: 10000 });
26-
27-
// Log any errors found
28-
if (pageErrors.length > 0) {
29-
console.log('=== PAGE ERRORS ===');
30-
pageErrors.forEach(error => console.log(error));
31-
console.log('==================');
32-
}
33-
34-
if (consoleMessages.length > 0) {
35-
console.log('=== CONSOLE MESSAGES ===');
36-
consoleMessages.forEach(msg => console.log(msg));
37-
console.log('========================');
38-
}
39-
}
40-
41-
async function waitForEnvironmentLoading(page: Page) {
42-
// Wait for either the loading screen to disappear or main content to appear
43-
// The loading screen shows "Loading environment configuration..."
44-
const loadingText = page.locator('text=Loading environment configuration...');
45-
const mainContent = page.locator('h1');
46-
47-
try {
48-
// Wait up to 15 seconds for either loading to finish or main content to appear
49-
await Promise.race([
50-
loadingText.waitFor({ state: 'hidden', timeout: 15000 }),
51-
mainContent.waitFor({ state: 'visible', timeout: 15000 })
52-
]);
53-
} catch (error) {
54-
console.log('Environment loading timeout - checking current page state');
55-
const pageContent = await page.content();
56-
console.log('Current page content length:', pageContent.length);
57-
58-
// If still loading, wait a bit more and proceed
59-
if (await loadingText.isVisible()) {
60-
console.log('Still showing loading screen, waiting 10 more seconds...');
61-
await page.waitForTimeout(10000);
62-
}
63-
}
64-
}
65-
66-
async function checkElementWithTextPresence(page: Page, selector: string, text: string) {
67-
const element = page.locator(`${selector}:has-text("${text}")`);
68-
await expect(element).toBeVisible();
69-
}
70-
71-
async function clickElementWithText(page: Page, selector: string, text: string) {
72-
await page.click(`${selector}:has-text("${text}")`);
73-
}
74-
75-
async function checkDateFormat(page: Page) {
76-
const dateElement = page.locator('text=/[A-Z][a-z]+ \\d{1,2}[a-z]{2} \\d{4}, \\d{1,2}:\\d{2}/').first();
77-
await expect(dateElement).toBeVisible();
78-
}
79-
80-
const appsData = [
81-
{
82-
header: 'Dynamic Remotes with Runtime Environment Variables',
83-
subheader: 'Host',
84-
hostH3: 'Environment Configuration:',
85-
paragraph: 'This example demonstrates how Module Federation can load remote components dynamically',
86-
button: 'Load Remote Widget',
87-
buttonH2: 'Remote Widget',
88-
buttonParagraph: 'Using momentjs for format the date',
89-
host: 3000,
90-
},
91-
{
92-
header: 'Dynamic System Host',
93-
subheader: 'Remote',
94-
buttonH2: 'Remote Widget',
95-
buttonParagraph: 'Using momentjs for format the date',
96-
host: 3001,
97-
},
98-
];
99-
100-
test.describe('Dynamic Remotes Runtime Environment Variables E2E Tests', () => {
101-
102-
appsData.forEach((appData) => {
103-
const { host, subheader, header, hostH3, paragraph, button, buttonH2, buttonParagraph } = appData;
104-
105-
test.describe(`Check ${subheader} app`, () => {
106-
test(`should display ${subheader} app widget functionality and application elements`, async ({ page }) => {
107-
await openLocalhost(page, host);
108-
109-
// Wait for environment loading to complete for host app
110-
if (host === 3000) {
111-
await waitForEnvironmentLoading(page);
112-
}
113-
114-
// Check main header
115-
await checkElementWithTextPresence(page, 'h1', header);
116-
117-
if (host === 3000) {
118-
// Host app specific elements
119-
await checkElementWithTextPresence(page, 'h3', hostH3!);
120-
await checkElementWithTextPresence(page, 'p', paragraph!);
121-
122-
// Click the load remote component button
123-
await clickElementWithText(page, 'button', button!);
124-
125-
// Wait for loading to complete
126-
await page.waitForTimeout(3000);
127-
}
128-
129-
// Check that the remote component loaded successfully
130-
await checkElementWithTextPresence(page, 'h2', buttonH2);
131-
await checkElementWithTextPresence(page, 'p', buttonParagraph);
132-
133-
// Check moment.js date formatting
134-
await checkDateFormat(page);
135-
});
136-
});
137-
});
6+
const {
7+
host,
8+
remoteApp,
9+
widget,
10+
} = Constants.elementsText.dynamicSystemRemotesRuntimeApp;
13811

139-
test.describe('Runtime Environment Variable Tests', () => {
140-
test('should load environment configuration successfully', async ({ page }) => {
141-
const networkRequests: string[] = [];
142-
143-
page.on('request', (request) => {
144-
networkRequests.push(request.url());
145-
});
146-
147-
await page.goto('http://localhost:3000');
148-
await page.waitForLoadState('load');
149-
await waitForEnvironmentLoading(page);
150-
151-
// Check that env-config.json was loaded
152-
const envConfigRequests = networkRequests.filter(url =>
153-
url.includes('env-config.json')
154-
);
155-
156-
expect(envConfigRequests.length).toBeGreaterThan(0);
157-
});
12+
const { envLoader, remoteConfigLoader } = Constants.commonConstantsData;
13+
14+
test.describe('Dynamic Remotes with runtime environment variables', () => {
15+
test('host application loads the remote widget on demand', async ({ page }) => {
16+
const basePage = new BasePage(page);
17+
18+
await basePage.openLocalhost(3000);
19+
20+
await basePage.waitForTextToDisappear(selectors.tags.coreElements.div, envLoader, 15000);
15821

159-
test('should demonstrate dynamic remote loading with environment config', async ({ page }) => {
160-
await openLocalhost(page, 3000);
161-
await waitForEnvironmentLoading(page);
22+
await basePage.checkElementWithTextPresence(selectors.tags.headers.h1, host.header);
23+
await basePage.checkElementWithTextPresence(selectors.tags.headers.h3, host.envSectionTitle);
24+
await basePage.checkElementWithTextPresence(selectors.tags.paragraph, host.paragraph);
16225

163-
// Click to load remote component
164-
await clickElementWithText(page, 'button', 'Load Remote Widget');
26+
await basePage.clickElementWithText(selectors.tags.coreElements.button, host.button);
16527

166-
// Wait for loading to complete
167-
await page.waitForTimeout(3000);
28+
await basePage.waitForTextToDisappear(selectors.tags.coreElements.div, host.remoteLoading, 15000);
29+
await basePage.waitForTextToDisappear(selectors.tags.coreElements.div, remoteConfigLoader, 15000);
16830

169-
// Verify remote component is now loaded
170-
await checkElementWithTextPresence(page, 'h2', 'Remote Widget');
171-
await checkElementWithTextPresence(page, 'p', 'Using momentjs for format the date');
31+
await basePage.checkElementWithTextPresence(selectors.tags.headers.h3, host.remoteSectionTitle);
32+
await basePage.checkElementWithTextPresence(selectors.tags.headers.h2, widget.title);
33+
34+
const envHeading = page.getByRole('heading', {
35+
level: 2,
36+
name: new RegExp(`^${widget.envPrefix} `),
17237
});
38+
await expect(envHeading).toHaveText(new RegExp(`^${widget.envPrefix} https?://`));
39+
40+
await basePage.checkElementWithTextPresence(selectors.tags.paragraph, widget.paragraph);
41+
await basePage.checkDateFormat();
17342
});
17443

175-
test.describe('Performance and Loading', () => {
176-
test('should load applications within reasonable time', async ({ page }) => {
177-
const startTime = Date.now();
178-
179-
await page.goto('http://localhost:3000');
180-
await page.waitForLoadState('load');
181-
await waitForEnvironmentLoading(page);
182-
183-
const loadTime = Date.now() - startTime;
184-
expect(loadTime).toBeLessThan(10000); // Should load within 10 seconds
44+
test('remote application exposes the widget with runtime configuration', async ({ page }) => {
45+
const basePage = new BasePage(page);
46+
47+
await basePage.openLocalhost(3001);
48+
49+
await basePage.checkElementWithTextPresence(selectors.tags.headers.h1, remoteApp.header);
50+
await expect(
51+
page.getByRole('heading', { level: 2, name: new RegExp(`^${remoteApp.subheader}$`) }),
52+
).toBeVisible();
53+
54+
await basePage.waitForTextToDisappear(selectors.tags.coreElements.div, remoteConfigLoader, 15000);
55+
56+
await basePage.checkElementWithTextPresence(selectors.tags.headers.h2, widget.title);
57+
58+
const remoteEnvHeading = page.getByRole('heading', {
59+
level: 2,
60+
name: new RegExp(`^${widget.envPrefix}`),
18561
});
62+
await expect(remoteEnvHeading).toHaveText(new RegExp(`^${widget.envPrefix} https?://`));
63+
64+
await basePage.checkElementWithTextPresence(selectors.tags.paragraph, widget.paragraph);
65+
await basePage.checkDateFormat();
18666
});
187-
});
67+
});

0 commit comments

Comments
 (0)