-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat: port comprehensive demo react16 tests to playwright #4378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ScriptedAlchemy
merged 5 commits into
master
from
codex/port-cypress-specs-to-playwright
Sep 16, 2025
Merged
Changes from 4 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
07c2d86
feat: port comprehensive demo react16 tests to playwright
ScriptedAlchemy 743f858
test: fix comprehensive demo tests
ScriptedAlchemy f8d550d
chore: regenerate lockfile after playwright migration
ScriptedAlchemy 04241c0
test: expand comprehensive demo playwright coverage
ScriptedAlchemy 68fd7f8
chore: regenerate lockfile and fix regex escape
ScriptedAlchemy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
import { test, expect } from '@playwright/test'; | ||
import type { Page } from '@playwright/test'; | ||
|
||
const base = 'http://localhost:3001'; | ||
|
||
const demoPages = [ | ||
{ name: 'Main', hash: '#/' }, | ||
{ name: 'UI Library', hash: '#/ui-library' }, | ||
{ name: 'Dialog', hash: '#/dialog' }, | ||
{ name: 'Svelte Page', hash: '#/svelte' }, | ||
{ name: 'Routing', hash: '#/routing/foo' }, | ||
]; | ||
|
||
const appLinks = [ | ||
{ name: 'App #1', href: 'http://localhost:3001' }, | ||
{ name: 'App #2', href: 'http://localhost:3002' }, | ||
{ name: 'App #3', href: 'http://localhost:3003' }, | ||
{ name: 'App #4', href: 'http://localhost:3004' }, | ||
{ name: 'App #5', href: 'http://localhost:3005' }, | ||
]; | ||
|
||
const mainPageParagraphs = [ | ||
'Welcome to the Module Federation Demo!', | ||
'Click any of the items on the left to get started.', | ||
'Feel free to leave me feedback', | ||
]; | ||
|
||
const uiLibraryParagraphs = [ | ||
'Simple example showing host app and external component using separate CSS solutions.', | ||
'This Button component can be found in App #3.', | ||
'This button is also used in the routing demo.', | ||
]; | ||
|
||
const routingParagraphs = [ | ||
'The following tab components are being imported remotely from "bravo-app".', | ||
"Notice that your browser's route is /routing/<foo|bar> depending on which tab is active.", | ||
'If you open http://localhost:3002 you will see the same tab components at the root level', | ||
'The "Bar" tab also lazily renders the styled-component Button from the UI Library demo only when rendered.', | ||
]; | ||
|
||
const escapeRegExp = (value: string) => value.replace(/[-/\^$*+?.()|[\]{}]/g, '\$&'); | ||
|
||
const expectAppBar = async (page: Page, title: string) => { | ||
const appBar = page.locator('header').first(); | ||
await expect(appBar).toBeVisible(); | ||
await expect(appBar).toHaveCSS('background-color', 'rgb(63, 81, 181)'); | ||
await expect(page.getByRole('heading', { name: title })).toBeVisible(); | ||
}; | ||
|
||
test.describe('Comprehensive Demo App1', () => { | ||
test('main page displays sidebar links and elements', async ({ page }) => { | ||
await page.goto(base); | ||
|
||
await expect(page.getByRole('heading', { name: 'SideNav' })).toBeVisible(); | ||
await expect(page.getByText('Demo Pages')).toBeVisible(); | ||
await expect(page.getByText('Apps')).toBeVisible(); | ||
|
||
for (const { name, hash } of demoPages) { | ||
const link = page.locator('a', { hasText: name }).first(); | ||
await expect(link).toBeVisible(); | ||
await expect(link).toHaveAttribute('href', hash); | ||
} | ||
|
||
for (const { name, href } of appLinks) { | ||
const link = page.locator(`a[href="${href}"]`).first(); | ||
await expect(link).toBeVisible(); | ||
await expect(link).toHaveAttribute('href', href); | ||
await expect(link).toContainText(name); | ||
await expect(link).toContainText(href); | ||
} | ||
|
||
await expectAppBar(page, 'Module Federation Demo'); | ||
|
||
const alert = page.locator('.alert'); | ||
await expect(alert).toBeVisible(); | ||
await expect(alert).toHaveText(/Alert from LitElement/); | ||
await expect(page.locator('.closebtn')).toBeVisible(); | ||
|
||
for (const paragraph of mainPageParagraphs) { | ||
await expect(page.locator('p', { hasText: paragraph })).toBeVisible(); | ||
} | ||
|
||
await expect( | ||
page.getByRole('link', { name: 'https://github.com/module-federation/mfe-webpack-demo' }), | ||
).toHaveAttribute('href', 'https://github.com/module-federation/mfe-webpack-demo'); | ||
|
||
const actionButton = page.locator('action-button button'); | ||
await expect(actionButton).toHaveText('Lit Element Action'); | ||
await expect(actionButton).toHaveCSS('background-color', 'rgb(219, 112, 147)'); | ||
}); | ||
|
||
test('main tab functionality', async ({ page }) => { | ||
await page.goto(base); | ||
|
||
page.once('dialog', async dialog => { | ||
expect(dialog.message()).toBe('You have pressed a button.'); | ||
await dialog.accept(); | ||
}); | ||
|
||
await page.locator('action-button button').click(); | ||
await page.locator('.closebtn').click(); | ||
await expect(page.locator('.alert')).toBeHidden(); | ||
|
||
for (const { name, hash } of demoPages) { | ||
await page.locator('a', { hasText: name }).first().click(); | ||
await expect(page).toHaveURL(`${base}/${hash}`); | ||
} | ||
|
||
await page.locator('a', { hasText: 'Main' }).first().click(); | ||
await expect(page).toHaveURL(`${base}/#/`); | ||
|
||
for (const { href } of appLinks) { | ||
await Promise.all([ | ||
page.waitForNavigation({ waitUntil: 'load' }), | ||
page.locator(`a[href="${href}"]`).first().click(), | ||
]); | ||
await expect(page).toHaveURL(new RegExp(`^${escapeRegExp(href)}`)); | ||
await page.goBack(); | ||
await expect(page).toHaveURL(new RegExp(`^${escapeRegExp(base)}`)); | ||
await expect(page.getByRole('heading', { name: 'Module Federation Demo' })).toBeVisible(); | ||
} | ||
}); | ||
|
||
test('UI library page renders remote button', async ({ page }) => { | ||
await page.goto(`${base}/#/ui-library`); | ||
|
||
await expectAppBar(page, 'UI Library Demo'); | ||
|
||
for (const paragraph of uiLibraryParagraphs) { | ||
await expect(page.locator('p', { hasText: paragraph })).toBeVisible(); | ||
} | ||
|
||
await expect(page.locator('a[href="http://localhost:3003/"]').first()).toHaveAttribute( | ||
'href', | ||
'http://localhost:3003/', | ||
); | ||
await expect(page.locator('a[href="http://localhost:3001/#/routing/foo"]').first()).toHaveAttribute( | ||
'href', | ||
'http://localhost:3001/#/routing/foo', | ||
); | ||
|
||
const styledButton = page.getByRole('button', { name: '💅 Button' }); | ||
await expect(styledButton).toBeVisible(); | ||
await expect(styledButton).toHaveCSS('background-color', 'rgb(219, 112, 147)'); | ||
}); | ||
|
||
test('dialog page loads and dialog opens', async ({ page }) => { | ||
await page.goto(`${base}/#/dialog`); | ||
|
||
await expectAppBar(page, 'Dialog Demo'); | ||
await expect( | ||
page.locator('p', { | ||
hasText: | ||
'Clicking the button below will render a Dialog using React Portal. This dialog component is being lazy loaded from the app #2.', | ||
}), | ||
).toBeVisible(); | ||
|
||
await page.getByRole('button', { name: 'Open Dialog' }).click(); | ||
const dialog = page.locator('[role="dialog"]'); | ||
await expect(dialog.getByRole('heading', { name: 'Dialog Example' })).toBeVisible(); | ||
await expect( | ||
dialog.getByText('This is a dialog from the Material UI app rendered in a React Portal.'), | ||
).toBeVisible(); | ||
await dialog.getByRole('button', { name: 'Nice' }).click(); | ||
await expect(dialog).not.toBeVisible(); | ||
}); | ||
|
||
test('svelte page updates greeting', async ({ page }) => { | ||
await page.goto(`${base}/#/svelte`); | ||
|
||
await expectAppBar(page, 'Svelte Demo'); | ||
|
||
const input = page.locator('input'); | ||
await expect(input).toBeVisible(); | ||
await input.fill('May The Force Be With You'); | ||
await expect(page.locator('h1')).toHaveText('Hello From Svelte May The Force Be With You!'); | ||
}); | ||
|
||
test('routing page renders tabs', async ({ page }) => { | ||
await page.goto(`${base}/#/routing/foo`); | ||
|
||
await expectAppBar(page, 'Routing Demo'); | ||
|
||
for (const paragraph of routingParagraphs) { | ||
await expect(page.locator('p', { hasText: paragraph })).toBeVisible(); | ||
} | ||
|
||
await expect(page.getByRole('tab', { name: 'Foo' })).toBeVisible(); | ||
await expect(page.getByText('Foo Content')).toBeVisible(); | ||
|
||
await page.getByRole('tab', { name: 'Bar' }).click(); | ||
await expect(page.getByText('Bar Content')).toBeVisible(); | ||
|
||
const barButton = page.getByRole('button', { name: 'Bar Button' }); | ||
await expect(barButton).toBeVisible(); | ||
await expect(barButton).toHaveCSS('background-color', 'rgb(219, 112, 147)'); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
const base = 'http://localhost:3002'; | ||
|
||
test.describe('Comprehensive Demo App2', () => { | ||
test('renders blocks, dialog and tabs', async ({ page }) => { | ||
await page.goto(base); | ||
await expect(page.locator('.jss1')).toBeVisible(); | ||
const appBar = page.locator('header').first(); | ||
await expect(appBar).toHaveCSS('background-color', 'rgb(63, 81, 181)'); | ||
await expect(page.locator('.jss2')).toHaveCSS('background-color', 'rgb(250, 250, 250)'); | ||
await expect(page.locator('.jss3')).toHaveCSS('background-color', 'rgb(255, 255, 255)'); | ||
|
||
await expect(page.getByRole('heading', { name: 'Material UI App' })).toBeVisible(); | ||
await expect(page.getByRole('heading', { name: 'Dialog Component' })).toBeVisible(); | ||
const openDialogButton = page.getByRole('button', { name: 'Open Dialog' }); | ||
await expect(openDialogButton).toBeVisible(); | ||
await openDialogButton.click(); | ||
const dialog = page.locator('[role="dialog"]'); | ||
await expect(dialog.getByRole('heading', { name: 'Dialog Example' })).toBeVisible(); | ||
await expect( | ||
dialog.getByText('This is a dialog from the Material UI app rendered in a React Portal.'), | ||
).toBeVisible(); | ||
await dialog.getByRole('button', { name: 'Nice' }).click(); | ||
await expect(dialog).not.toBeVisible(); | ||
|
||
await expect(page.getByRole('heading', { name: 'Tabs Component' })).toBeVisible(); | ||
const fooTab = page.getByRole('tab', { name: 'Foo' }); | ||
const barTab = page.getByRole('tab', { name: 'Bar' }); | ||
await expect(fooTab).toBeVisible(); | ||
await expect(barTab).toBeVisible(); | ||
await expect(page.getByText('Foo Content')).toBeVisible(); | ||
await barTab.click(); | ||
await expect(page.getByText('Bar Content')).toBeVisible(); | ||
await expect(page.getByRole('button', { name: 'Bar Button' })).toHaveCSS( | ||
'background-color', | ||
'rgb(219, 112, 147)', | ||
); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
const base = 'http://localhost:3003'; | ||
|
||
test.describe('Comprehensive Demo App3', () => { | ||
test('shows styled button', async ({ page }) => { | ||
await page.goto(base); | ||
await expect(page.locator('.jss1')).toBeVisible(); | ||
const appBar = page.locator('header').first(); | ||
await expect(appBar).toHaveCSS('background-color', 'rgb(63, 81, 181)'); | ||
await expect(page.locator('.jss2')).toHaveCSS('background-color', 'rgb(250, 250, 250)'); | ||
await expect(page.getByRole('heading', { name: 'Styled Components App' })).toBeVisible(); | ||
const button = page.getByRole('button', { name: '💅 Test Button' }); | ||
await expect(button).toBeVisible(); | ||
await expect(button).toHaveCSS('background-color', 'rgb(219, 112, 147)'); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
test.describe('Comprehensive Demo App4', () => { | ||
test('shows svelte greeting', async ({ page }) => { | ||
await page.goto('http://localhost:3004'); | ||
await expect(page.locator('h1')).toHaveText('Hello From Svelte world!'); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
const base = 'http://localhost:3005'; | ||
|
||
test.describe('Comprehensive Demo App5', () => { | ||
test('shows button and alert', async ({ page }) => { | ||
await page.goto(base); | ||
const button = page.locator('action-button').locator('button'); | ||
await expect(button).toHaveText('bar'); | ||
await expect(page.locator('.alert')).toHaveText(/Hello/); | ||
await expect(page.locator('.closebtn')).toBeVisible(); | ||
}); | ||
|
||
test('button triggers alert and close hides it', async ({ page }) => { | ||
await page.goto(base); | ||
page.once('dialog', async dialog => { | ||
expect(dialog.message()).toBe('You have pressed a button.'); | ||
await dialog.accept(); | ||
}); | ||
await page.locator('action-button').locator('button').click(); | ||
await page.locator('.closebtn').click(); | ||
await expect(page.locator('.alert')).toBeHidden(); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
const apps = [ | ||
{ port: 3001, name: 'App 1', selector: 'h6', text: 'Module Federation Demo' }, | ||
{ port: 3002, name: 'App 2', selector: 'h6', text: 'Material UI App' }, | ||
{ port: 3003, name: 'App 3', selector: 'h6', text: 'Styled Components App' }, | ||
{ port: 3004, name: 'App 4', selector: 'h1', text: 'Hello From Svelte world!' }, | ||
{ port: 3005, name: 'App 5', selector: 'action-button button', text: 'bar' }, | ||
]; | ||
|
||
apps.forEach(({ port, name, selector, text }) => { | ||
test.describe(name, () => { | ||
test(`build and run ${name}`, async ({ page }) => { | ||
await page.goto(`http://localhost:${port}`); | ||
await expect(page.locator(selector, { hasText: text })).toBeVisible(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.