Skip to content

Commit d825f55

Browse files
Migrate comprehensive-demo-react18 E2E tests to Playwright (#4380)
* chore: migrate comprehensive demo react18 e2e to playwright * chore: regenerate pnpm lockfile
1 parent 2e5ac10 commit d825f55

16 files changed

+369
-593
lines changed

comprehensive-demo-react18/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ Included apps:
1818
- App #5 (LitElement): [http://localhost:3005](http://localhost:3005)
1919
<img src="https://ssl.google-analytics.com/collect?v=1&t=event&ec=email&ea=open&t=event&tid=UA-120967034-1&z=1589682154&cid=ae045149-9d17-0367-bbb0-11c41d92b411&dt=ModuleFederationExamples&dp=/email/ComprehensiveDemo">
2020

21-
# Running Cypress E2E Tests
21+
# Running Playwright E2E Tests
2222

23-
To run tests in interactive mode, run `npm run cypress:debug` from the root directory of the project. It will open Cypress Test Runner and allow to run tests in interactive mode. [More info about "How to run tests"](../../cypress-e2e/README.md#how-to-run-tests)
23+
To run the Playwright test suite locally in headless mode, execute `pnpm test:e2e` from this workspace. The tests automatically start the demo and verify each application.
2424

25-
To build app and run test in headless mode, run `yarn e2e:ci`. It will build app and run tests for this workspace in headless mode. If tets failed cypress will create `cypress` directory in sample root folder with screenshots and videos.
25+
For an interactive UI to debug or explore tests, run `pnpm test:e2e:ui`.
2626

27-
["Best Practices, Rules amd more interesting information here](../../cypress-e2e/README.md)
27+
In CI scenarios run `pnpm e2e:ci`. This command builds the applications, installs the required Playwright browsers and runs the tests with a concise reporter.
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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 expectAppBar = async (page: Page, title: string) => {
42+
const appBar = page.locator('header').first();
43+
await expect(appBar).toBeVisible();
44+
await expect(appBar).toHaveCSS('background-color', 'rgb(63, 81, 181)');
45+
await expect(page.getByRole('heading', { name: title })).toBeVisible();
46+
};
47+
48+
test.describe('Comprehensive Demo App1', () => {
49+
test('main page displays sidebar links and elements', async ({ page }) => {
50+
await page.goto(base);
51+
52+
await expect(page.getByRole('heading', { name: 'SideNav' })).toBeVisible();
53+
await expect(page.getByText('Demo Pages')).toBeVisible();
54+
await expect(page.getByText('Apps')).toBeVisible();
55+
56+
for (const { name, hash } of demoPages) {
57+
const link = page.locator('a', { hasText: name }).first();
58+
await expect(link).toBeVisible();
59+
await expect(link).toHaveAttribute('href', hash);
60+
}
61+
62+
for (const { name, href } of appLinks) {
63+
const link = page.locator(`a[href="${href}"]`).first();
64+
await expect(link).toBeVisible();
65+
await expect(link).toHaveAttribute('href', href);
66+
await expect(link).toContainText(name);
67+
await expect(link).toContainText(href);
68+
}
69+
70+
await expectAppBar(page, 'Module Federation Demo');
71+
72+
const alert = page.locator('.alert');
73+
await expect(alert).toBeVisible();
74+
await expect(alert).toHaveText(/Alert from LitElement/);
75+
await expect(page.locator('.closebtn')).toBeVisible();
76+
77+
for (const paragraph of mainPageParagraphs) {
78+
await expect(page.locator('p', { hasText: paragraph })).toBeVisible();
79+
}
80+
81+
await expect(
82+
page.getByRole('link', { name: 'https://github.com/module-federation/mfe-webpack-demo' }),
83+
).toHaveAttribute('href', 'https://github.com/module-federation/mfe-webpack-demo');
84+
85+
const actionButton = page.locator('action-button button');
86+
await expect(actionButton).toHaveText('Lit Element Action');
87+
await expect(actionButton).toHaveCSS('background-color', 'rgb(219, 112, 147)');
88+
});
89+
90+
test('main tab functionality', async ({ page }) => {
91+
await page.goto(base);
92+
93+
page.once('dialog', async dialog => {
94+
expect(dialog.message()).toBe('You have pressed a button.');
95+
await dialog.accept();
96+
});
97+
98+
await page.locator('action-button button').click();
99+
await page.locator('.closebtn').click();
100+
await expect(page.locator('.alert')).toBeHidden();
101+
102+
for (const { name, hash } of demoPages) {
103+
await page.locator('a', { hasText: name }).first().click();
104+
await expect(page).toHaveURL(`${base}/${hash}`);
105+
}
106+
107+
await page.locator('a', { hasText: 'Main' }).first().click();
108+
await expect(page).toHaveURL(`${base}/#/`);
109+
110+
for (const { href } of appLinks) {
111+
const response = await page.request.get(href);
112+
expect(response.ok()).toBeTruthy();
113+
}
114+
});
115+
116+
test('UI library page renders remote button', async ({ page }) => {
117+
await page.goto(`${base}/#/ui-library`);
118+
119+
await expectAppBar(page, 'UI Library Demo');
120+
121+
for (const paragraph of uiLibraryParagraphs) {
122+
await expect(page.locator('p', { hasText: paragraph })).toBeVisible();
123+
}
124+
125+
await expect(page.locator('a[href="http://localhost:3003/"]').first()).toHaveAttribute(
126+
'href',
127+
'http://localhost:3003/',
128+
);
129+
await expect(page.locator('a[href="http://localhost:3001/#/routing/foo"]').first()).toHaveAttribute(
130+
'href',
131+
'http://localhost:3001/#/routing/foo',
132+
);
133+
134+
const styledButton = page.getByRole('button', { name: '💅 Button' });
135+
await expect(styledButton).toBeVisible();
136+
await expect(styledButton).toHaveCSS('background-color', 'rgb(219, 112, 147)');
137+
});
138+
139+
test('dialog page loads and dialog opens', async ({ page }) => {
140+
await page.goto(`${base}/#/dialog`);
141+
142+
await expectAppBar(page, 'Dialog Demo');
143+
await expect(
144+
page.locator('p', {
145+
hasText:
146+
'Clicking the button below will render a Dialog using React Portal. This dialog component is being lazy loaded from the app #2.',
147+
}),
148+
).toBeVisible();
149+
150+
await page.getByRole('button', { name: 'Open Dialog' }).click();
151+
const dialog = page.locator('[role="dialog"]');
152+
await expect(dialog.getByRole('heading', { name: 'Dialog Example' })).toBeVisible();
153+
await expect(
154+
dialog.getByText('This is a dialog from the Material UI app rendered in a React Portal.'),
155+
).toBeVisible();
156+
await dialog.getByRole('button', { name: 'Nice' }).click();
157+
await expect(dialog).not.toBeVisible();
158+
});
159+
160+
test('svelte page updates greeting', async ({ page }) => {
161+
await page.goto(`${base}/#/svelte`);
162+
163+
await expectAppBar(page, 'Svelte Demo');
164+
165+
const input = page.locator('input');
166+
await expect(input).toBeVisible();
167+
await input.fill('May The Force Be With You');
168+
await expect(page.locator('h1')).toHaveText('Hello From Svelte May The Force Be With You!');
169+
});
170+
171+
test('routing page renders tabs', async ({ page }) => {
172+
await page.goto(`${base}/#/routing/foo`);
173+
174+
await expectAppBar(page, 'Routing Demo');
175+
176+
for (const paragraph of routingParagraphs) {
177+
await expect(page.locator('p', { hasText: paragraph })).toBeVisible();
178+
}
179+
180+
await expect(page.getByRole('tab', { name: 'Foo' })).toBeVisible();
181+
await expect(page.getByText('Foo Content')).toBeVisible();
182+
183+
await page.getByRole('tab', { name: 'Bar' }).click();
184+
await expect(page.getByText('Bar Content')).toBeVisible();
185+
186+
const barButton = page.getByRole('button', { name: 'Bar Button' });
187+
await expect(barButton).toBeVisible();
188+
await expect(barButton).toHaveCSS('background-color', 'rgb(219, 112, 147)');
189+
});
190+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
9+
await expect(page.locator('header').first()).toHaveCSS('background-color', 'rgb(63, 81, 181)');
10+
await expect(page.getByRole('heading', { name: 'Material UI App' })).toBeVisible();
11+
await expect(page.getByRole('heading', { name: 'Dialog Component' })).toBeVisible();
12+
13+
const openDialogButton = page.getByRole('button', { name: 'Open Dialog' });
14+
await expect(openDialogButton).toBeVisible();
15+
await openDialogButton.click();
16+
17+
const dialog = page.locator('[role="dialog"]');
18+
await expect(dialog.getByRole('heading', { name: 'Dialog Example' })).toBeVisible();
19+
await expect(
20+
dialog.getByText('This is a dialog from the Material UI app rendered in a React Portal.'),
21+
).toBeVisible();
22+
await dialog.getByRole('button', { name: 'Nice' }).click();
23+
await expect(dialog).not.toBeVisible();
24+
25+
await expect(page.getByRole('heading', { name: 'Tabs Component' })).toBeVisible();
26+
const fooTab = page.getByRole('tab', { name: 'Foo' });
27+
const barTab = page.getByRole('tab', { name: 'Bar' });
28+
await expect(fooTab).toBeVisible();
29+
await expect(barTab).toBeVisible();
30+
await expect(page.getByText('Foo Content')).toBeVisible();
31+
32+
await barTab.click();
33+
await expect(page.getByText('Bar Content')).toBeVisible();
34+
await expect(page.getByRole('button', { name: 'Bar Button' })).toHaveCSS(
35+
'background-color',
36+
'rgb(219, 112, 147)',
37+
);
38+
});
39+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
9+
await expect(page.locator('header').first()).toHaveCSS('background-color', 'rgb(63, 81, 181)');
10+
await expect(page.getByRole('heading', { name: 'Styled Components App' })).toBeVisible();
11+
12+
const button = page.getByRole('button', { name: '💅 Test Button' });
13+
await expect(button).toBeVisible();
14+
await expect(button).toHaveCSS('background-color', 'rgb(219, 112, 147)');
15+
});
16+
});
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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
9+
const button = page.locator('action-button').locator('button');
10+
await expect(button).toHaveText('bar');
11+
await expect(page.locator('.alert')).toHaveText(/Hello/);
12+
await expect(page.locator('.closebtn')).toBeVisible();
13+
});
14+
15+
test('button triggers alert and close hides it', async ({ page }) => {
16+
await page.goto(base);
17+
18+
page.once('dialog', async dialog => {
19+
expect(dialog.message()).toBe('You have pressed a button.');
20+
await dialog.accept();
21+
});
22+
23+
await page.locator('action-button').locator('button').click();
24+
await page.locator('.closebtn').click();
25+
await expect(page.locator('.alert')).toBeHidden();
26+
});
27+
});
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)