Skip to content

Commit 2e5ac10

Browse files
feat: port comprehensive demo react16 tests to playwright (#4378)
* feat: port comprehensive demo react16 tests to playwright * test: fix comprehensive demo tests * chore: regenerate lockfile after playwright migration * test: expand comprehensive demo playwright coverage * chore: regenerate lockfile and fix regex escape
1 parent 0c352c2 commit 2e5ac10

15 files changed

+664
-989
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import { test, expect } from '@playwright/test';
2+
import type { Page } from '@playwright/test';
3+
4+
const base = 'http://localhost:3001';
5+
6+
const demoPages = [
7+
{ name: 'Main', hash: '#/' },
8+
{ name: 'UI Library', hash: '#/ui-library' },
9+
{ name: 'Dialog', hash: '#/dialog' },
10+
{ name: 'Svelte Page', hash: '#/svelte' },
11+
{ name: 'Routing', hash: '#/routing/foo' },
12+
];
13+
14+
const appLinks = [
15+
{ name: 'App #1', href: 'http://localhost:3001' },
16+
{ name: 'App #2', href: 'http://localhost:3002' },
17+
{ name: 'App #3', href: 'http://localhost:3003' },
18+
{ name: 'App #4', href: 'http://localhost:3004' },
19+
{ name: 'App #5', href: 'http://localhost:3005' },
20+
];
21+
22+
const mainPageParagraphs = [
23+
'Welcome to the Module Federation Demo!',
24+
'Click any of the items on the left to get started.',
25+
'Feel free to leave me feedback',
26+
];
27+
28+
const uiLibraryParagraphs = [
29+
'Simple example showing host app and external component using separate CSS solutions.',
30+
'This Button component can be found in App #3.',
31+
'This button is also used in the routing demo.',
32+
];
33+
34+
const routingParagraphs = [
35+
'The following tab components are being imported remotely from "bravo-app".',
36+
"Notice that your browser's route is /routing/<foo|bar> depending on which tab is active.",
37+
'If you open http://localhost:3002 you will see the same tab components at the root level',
38+
'The "Bar" tab also lazily renders the styled-component Button from the UI Library demo only when rendered.',
39+
];
40+
41+
const escapeRegExp = (value: string) =>
42+
value.replace(/[-/\^$*+?.()|[\]{}]/g, match => `\\${match}`);
43+
44+
const expectAppBar = async (page: Page, title: string) => {
45+
const appBar = page.locator('header').first();
46+
await expect(appBar).toBeVisible();
47+
await expect(appBar).toHaveCSS('background-color', 'rgb(63, 81, 181)');
48+
await expect(page.getByRole('heading', { name: title })).toBeVisible();
49+
};
50+
51+
test.describe('Comprehensive Demo App1', () => {
52+
test('main page displays sidebar links and elements', async ({ page }) => {
53+
await page.goto(base);
54+
55+
await expect(page.getByRole('heading', { name: 'SideNav' })).toBeVisible();
56+
await expect(page.getByText('Demo Pages')).toBeVisible();
57+
await expect(page.getByText('Apps')).toBeVisible();
58+
59+
for (const { name, hash } of demoPages) {
60+
const link = page.locator('a', { hasText: name }).first();
61+
await expect(link).toBeVisible();
62+
await expect(link).toHaveAttribute('href', hash);
63+
}
64+
65+
for (const { name, href } of appLinks) {
66+
const link = page.locator(`a[href="${href}"]`).first();
67+
await expect(link).toBeVisible();
68+
await expect(link).toHaveAttribute('href', href);
69+
await expect(link).toContainText(name);
70+
await expect(link).toContainText(href);
71+
}
72+
73+
await expectAppBar(page, 'Module Federation Demo');
74+
75+
const alert = page.locator('.alert');
76+
await expect(alert).toBeVisible();
77+
await expect(alert).toHaveText(/Alert from LitElement/);
78+
await expect(page.locator('.closebtn')).toBeVisible();
79+
80+
for (const paragraph of mainPageParagraphs) {
81+
await expect(page.locator('p', { hasText: paragraph })).toBeVisible();
82+
}
83+
84+
await expect(
85+
page.getByRole('link', { name: 'https://github.com/module-federation/mfe-webpack-demo' }),
86+
).toHaveAttribute('href', 'https://github.com/module-federation/mfe-webpack-demo');
87+
88+
const actionButton = page.locator('action-button button');
89+
await expect(actionButton).toHaveText('Lit Element Action');
90+
await expect(actionButton).toHaveCSS('background-color', 'rgb(219, 112, 147)');
91+
});
92+
93+
test('main tab functionality', async ({ page }) => {
94+
await page.goto(base);
95+
96+
page.once('dialog', async dialog => {
97+
expect(dialog.message()).toBe('You have pressed a button.');
98+
await dialog.accept();
99+
});
100+
101+
await page.locator('action-button button').click();
102+
await page.locator('.closebtn').click();
103+
await expect(page.locator('.alert')).toBeHidden();
104+
105+
for (const { name, hash } of demoPages) {
106+
await page.locator('a', { hasText: name }).first().click();
107+
await expect(page).toHaveURL(`${base}/${hash}`);
108+
}
109+
110+
await page.locator('a', { hasText: 'Main' }).first().click();
111+
await expect(page).toHaveURL(`${base}/#/`);
112+
113+
for (const { href } of appLinks) {
114+
await Promise.all([
115+
page.waitForNavigation({ waitUntil: 'load' }),
116+
page.locator(`a[href="${href}"]`).first().click(),
117+
]);
118+
await expect(page).toHaveURL(new RegExp(`^${escapeRegExp(href)}`));
119+
await page.goBack();
120+
await expect(page).toHaveURL(new RegExp(`^${escapeRegExp(base)}`));
121+
await expect(page.getByRole('heading', { name: 'Module Federation Demo' })).toBeVisible();
122+
}
123+
});
124+
125+
test('UI library page renders remote button', async ({ page }) => {
126+
await page.goto(`${base}/#/ui-library`);
127+
128+
await expectAppBar(page, 'UI Library Demo');
129+
130+
for (const paragraph of uiLibraryParagraphs) {
131+
await expect(page.locator('p', { hasText: paragraph })).toBeVisible();
132+
}
133+
134+
await expect(page.locator('a[href="http://localhost:3003/"]').first()).toHaveAttribute(
135+
'href',
136+
'http://localhost:3003/',
137+
);
138+
await expect(page.locator('a[href="http://localhost:3001/#/routing/foo"]').first()).toHaveAttribute(
139+
'href',
140+
'http://localhost:3001/#/routing/foo',
141+
);
142+
143+
const styledButton = page.getByRole('button', { name: '💅 Button' });
144+
await expect(styledButton).toBeVisible();
145+
await expect(styledButton).toHaveCSS('background-color', 'rgb(219, 112, 147)');
146+
});
147+
148+
test('dialog page loads and dialog opens', async ({ page }) => {
149+
await page.goto(`${base}/#/dialog`);
150+
151+
await expectAppBar(page, 'Dialog Demo');
152+
await expect(
153+
page.locator('p', {
154+
hasText:
155+
'Clicking the button below will render a Dialog using React Portal. This dialog component is being lazy loaded from the app #2.',
156+
}),
157+
).toBeVisible();
158+
159+
await page.getByRole('button', { name: 'Open Dialog' }).click();
160+
const dialog = page.locator('[role="dialog"]');
161+
await expect(dialog.getByRole('heading', { name: 'Dialog Example' })).toBeVisible();
162+
await expect(
163+
dialog.getByText('This is a dialog from the Material UI app rendered in a React Portal.'),
164+
).toBeVisible();
165+
await dialog.getByRole('button', { name: 'Nice' }).click();
166+
await expect(dialog).not.toBeVisible();
167+
});
168+
169+
test('svelte page updates greeting', async ({ page }) => {
170+
await page.goto(`${base}/#/svelte`);
171+
172+
await expectAppBar(page, 'Svelte Demo');
173+
174+
const input = page.locator('input');
175+
await expect(input).toBeVisible();
176+
await input.fill('May The Force Be With You');
177+
await expect(page.locator('h1')).toHaveText('Hello From Svelte May The Force Be With You!');
178+
});
179+
180+
test('routing page renders tabs', async ({ page }) => {
181+
await page.goto(`${base}/#/routing/foo`);
182+
183+
await expectAppBar(page, 'Routing Demo');
184+
185+
for (const paragraph of routingParagraphs) {
186+
await expect(page.locator('p', { hasText: paragraph })).toBeVisible();
187+
}
188+
189+
await expect(page.getByRole('tab', { name: 'Foo' })).toBeVisible();
190+
await expect(page.getByText('Foo Content')).toBeVisible();
191+
192+
await page.getByRole('tab', { name: 'Bar' }).click();
193+
await expect(page.getByText('Bar Content')).toBeVisible();
194+
195+
const barButton = page.getByRole('button', { name: 'Bar Button' });
196+
await expect(barButton).toBeVisible();
197+
await expect(barButton).toHaveCSS('background-color', 'rgb(219, 112, 147)');
198+
});
199+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
const base = 'http://localhost:3002';
4+
5+
test.describe('Comprehensive Demo App2', () => {
6+
test('renders blocks, dialog and tabs', async ({ page }) => {
7+
await page.goto(base);
8+
await expect(page.locator('.jss1')).toBeVisible();
9+
const appBar = page.locator('header').first();
10+
await expect(appBar).toHaveCSS('background-color', 'rgb(63, 81, 181)');
11+
await expect(page.locator('.jss2')).toHaveCSS('background-color', 'rgb(250, 250, 250)');
12+
await expect(page.locator('.jss3')).toHaveCSS('background-color', 'rgb(255, 255, 255)');
13+
14+
await expect(page.getByRole('heading', { name: 'Material UI App' })).toBeVisible();
15+
await expect(page.getByRole('heading', { name: 'Dialog Component' })).toBeVisible();
16+
const openDialogButton = page.getByRole('button', { name: 'Open Dialog' });
17+
await expect(openDialogButton).toBeVisible();
18+
await openDialogButton.click();
19+
const dialog = page.locator('[role="dialog"]');
20+
await expect(dialog.getByRole('heading', { name: 'Dialog Example' })).toBeVisible();
21+
await expect(
22+
dialog.getByText('This is a dialog from the Material UI app rendered in a React Portal.'),
23+
).toBeVisible();
24+
await dialog.getByRole('button', { name: 'Nice' }).click();
25+
await expect(dialog).not.toBeVisible();
26+
27+
await expect(page.getByRole('heading', { name: 'Tabs Component' })).toBeVisible();
28+
const fooTab = page.getByRole('tab', { name: 'Foo' });
29+
const barTab = page.getByRole('tab', { name: 'Bar' });
30+
await expect(fooTab).toBeVisible();
31+
await expect(barTab).toBeVisible();
32+
await expect(page.getByText('Foo Content')).toBeVisible();
33+
await barTab.click();
34+
await expect(page.getByText('Bar Content')).toBeVisible();
35+
await expect(page.getByRole('button', { name: 'Bar Button' })).toHaveCSS(
36+
'background-color',
37+
'rgb(219, 112, 147)',
38+
);
39+
});
40+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
const base = 'http://localhost:3003';
4+
5+
test.describe('Comprehensive Demo App3', () => {
6+
test('shows styled button', async ({ page }) => {
7+
await page.goto(base);
8+
await expect(page.locator('.jss1')).toBeVisible();
9+
const appBar = page.locator('header').first();
10+
await expect(appBar).toHaveCSS('background-color', 'rgb(63, 81, 181)');
11+
await expect(page.locator('.jss2')).toHaveCSS('background-color', 'rgb(250, 250, 250)');
12+
await expect(page.getByRole('heading', { name: 'Styled Components App' })).toBeVisible();
13+
const button = page.getByRole('button', { name: '💅 Test Button' });
14+
await expect(button).toBeVisible();
15+
await expect(button).toHaveCSS('background-color', 'rgb(219, 112, 147)');
16+
});
17+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.describe('Comprehensive Demo App4', () => {
4+
test('shows svelte greeting', async ({ page }) => {
5+
await page.goto('http://localhost:3004');
6+
await expect(page.locator('h1')).toHaveText('Hello From Svelte world!');
7+
});
8+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
const base = 'http://localhost:3005';
4+
5+
test.describe('Comprehensive Demo App5', () => {
6+
test('shows button and alert', async ({ page }) => {
7+
await page.goto(base);
8+
const button = page.locator('action-button').locator('button');
9+
await expect(button).toHaveText('bar');
10+
await expect(page.locator('.alert')).toHaveText(/Hello/);
11+
await expect(page.locator('.closebtn')).toBeVisible();
12+
});
13+
14+
test('button triggers alert and close hides it', async ({ page }) => {
15+
await page.goto(base);
16+
page.once('dialog', async dialog => {
17+
expect(dialog.message()).toBe('You have pressed a button.');
18+
await dialog.accept();
19+
});
20+
await page.locator('action-button').locator('button').click();
21+
await page.locator('.closebtn').click();
22+
await expect(page.locator('.alert')).toBeHidden();
23+
});
24+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
const apps = [
4+
{ port: 3001, name: 'App 1', selector: 'h6', text: 'Module Federation Demo' },
5+
{ port: 3002, name: 'App 2', selector: 'h6', text: 'Material UI App' },
6+
{ port: 3003, name: 'App 3', selector: 'h6', text: 'Styled Components App' },
7+
{ port: 3004, name: 'App 4', selector: 'h1', text: 'Hello From Svelte world!' },
8+
{ port: 3005, name: 'App 5', selector: 'action-button button', text: 'bar' },
9+
];
10+
11+
apps.forEach(({ port, name, selector, text }) => {
12+
test.describe(name, () => {
13+
test(`build and run ${name}`, async ({ page }) => {
14+
await page.goto(`http://localhost:${port}`);
15+
await expect(page.locator(selector, { hasText: text })).toBeVisible();
16+
});
17+
});
18+
});

0 commit comments

Comments
 (0)