init project
Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled

This commit is contained in:
how2ice
2025-12-12 14:26:25 +09:00
commit 005cf56baf
43188 changed files with 1079531 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
# Docs end-to-end testing
## Running locally
1. Run `pnpm docs:dev` to start the development docs server.
2. Run `pnpm test:e2e-website` in a separate terminal to run the test suites (`*.spec.ts`) inside `test/e2e-website` folder.
> use --headed to run tests in headed browsers, check out [Playwright CLI](https://playwright.dev/docs/intro#command-line) for more options
## CI
After Netlify deploys the preview site, the `netlify/functions/deploy-succeeded.js` hook calls CircleCI API to run the `e2e-website` workflow against the deployed URL.

View File

@@ -0,0 +1,82 @@
import { test as base, expect } from '@playwright/test';
import { TestFixture } from './playwright.config';
const test = base.extend<TestFixture>({});
test.describe('Demo docs', () => {
test('mode toggle demos should work', async ({ page }) => {
await page.goto('/experiments/docs/demos/');
await expect(page.locator('div:has(> [data-element="demo-mode-toggle-paper"])')).toHaveClass(
/light/,
);
await expect(page.locator('[data-element="demo-mode-toggle-paper"]')).toHaveCSS(
'background-color',
'rgb(255, 255, 255)',
);
// Toggle dark mode
await page
.getByRole('radiogroup', { name: /^demo-mode-toggle$/ })
.locator('label:nth-child(3)')
.click();
await expect(page.locator('div:has(> [data-element="demo-mode-toggle-paper"])')).toHaveClass(
/dark/,
);
await expect(page.locator('[data-element="demo-mode-toggle-paper"]')).toHaveCSS(
'background-color',
'rgb(18, 18, 18)',
);
});
test('mode toggle custom theme demos should work', async ({ page }) => {
await page.goto('/experiments/docs/demos/');
await expect(
page.locator('div:has(> [data-element="demo-mode-toggle-custom-theme-paper"])'),
).toHaveClass(/light/);
await expect(page.locator('[data-element="demo-mode-toggle-custom-theme-paper"]')).toHaveCSS(
'background-color',
'rgb(239, 154, 154)',
);
// Toggle dark mode
await page
.getByRole('radiogroup', { name: /^demo-mode-toggle-custom-theme$/ })
.locator('label:nth-child(3)')
.click();
await expect(
page.locator('div:has(> [data-element="demo-mode-toggle-custom-theme-paper"])'),
).toHaveClass(/dark/);
await expect(page.locator('[data-element="demo-mode-toggle-custom-theme-paper"]')).toHaveCSS(
'background-color',
'rgb(183, 28, 28)',
);
});
test('mode toggle iframe demos should work', async ({ page }) => {
await page.goto('/experiments/docs/demos/');
const iframe = page.locator('iframe[title*="DemoModeToggleIframe"]').contentFrame();
await expect(iframe.locator('html')).toHaveClass(/light/);
await expect(iframe.locator('[data-element="demo-mode-toggle-iframe-paper"]')).toHaveCSS(
'background-color',
'rgb(255, 255, 255)',
);
// Toggle dark mode
await iframe
.getByRole('radiogroup', { name: /^demo-mode-toggle-iframe$/ })
.locator('label:nth-child(3)')
.click();
await expect(iframe.locator('html')).toHaveClass(/dark/);
await expect(iframe.locator('[data-element="demo-mode-toggle-iframe-paper"]')).toHaveCSS(
'background-color',
'rgb(18, 18, 18)',
);
});
});

View File

@@ -0,0 +1,29 @@
import { test as base, expect } from '@playwright/test';
import { kebabCase } from 'es-toolkit/string';
import { TestFixture } from './playwright.config';
const test = base.extend<TestFixture>({});
test.describe('Joy docs', () => {
test('should have correct link with hash in the TOC', async ({ page }) => {
await page.goto('/joy-ui/getting-started/installation/');
const anchors = page.locator('[aria-label="Page table of contents"] ul a');
const firstAnchor = anchors.first();
const textContent = await firstAnchor.textContent();
await expect(firstAnchor).toHaveAttribute(
'href',
`/joy-ui/getting-started/installation/#${kebabCase(textContent || '')}`,
);
});
test('should be able to see demos', async ({ page }) => {
await page.goto('/joy-ui/react-button/');
const headline = page.locator('main h1');
await expect(headline).toHaveText('Button');
});
});

View File

@@ -0,0 +1,21 @@
import { test as base, expect } from '@playwright/test';
import FEATURE_TOGGLE from 'docs/src/featureToggle';
import { TestFixture } from './playwright.config';
const test = base.extend<TestFixture>({});
test('should be able to change color without crash', async ({ page }) => {
await page.goto('/material-ui/customization/color/#playground', { waitUntil: 'networkidle' });
await page.fill('#primary', ''); // clear the input
await page.type('#primary', '#e91e63');
await page.fill('#secondary', ''); // clear the input
await page.type('#secondary', '#ffc400');
await page.click('button:has-text("Set Docs Colors")');
await page.click('#mui-version-selector'); // can open any menu, just to make sure that it does not break
await expect(page.locator('#mui-version-menu')).toBeVisible();
});

View File

@@ -0,0 +1,194 @@
import { test as base, expect, Page } from '@playwright/test';
import { kebabCase } from 'es-toolkit/string';
import { TestFixture } from './playwright.config';
const test = base.extend<TestFixture>({});
test.describe('Material docs', () => {
test('should have correct link with hash in the TOC', async ({ page }) => {
await page.goto('/material-ui/getting-started/installation/');
const anchors = page.locator('[aria-label="Page table of contents"] ul a');
const firstAnchor = anchors.first();
const textContent = await firstAnchor.textContent();
await expect(firstAnchor).toHaveAttribute(
'href',
`/material-ui/getting-started/installation/#${kebabCase(textContent || '')}`,
);
});
test('[zh] should have correct link with hash in the TOC', async ({ page }) => {
test.skip(
(process.env.CIRCLE_BRANCH || '').startsWith('pull'),
'There is no languages on the deploy preview',
);
await page.goto('/zh/material-ui/getting-started/installation/');
const anchors = page.locator('main nav ul a');
const firstAnchor = anchors.first();
const textContent = await firstAnchor.textContent();
await expect(firstAnchor).toHaveAttribute(
'href',
`/zh/material-ui/getting-started/installation/#${kebabCase(textContent || '')}`,
);
});
test.describe('Demo page', () => {
test('should have correct link for API section', async ({ page }) => {
await page.goto('/material-ui/react-card/');
const anchors = page.locator('div > h2#api ~ ul a');
const firstAnchor = anchors.first();
const textContent = await firstAnchor.textContent();
await expect(firstAnchor).toHaveAttribute(
'href',
`/material-ui/api/${kebabCase(textContent || '')}/`,
);
});
test('should have correct link for sidebar anchor', async ({ page }) => {
await page.goto('/material-ui/react-card/');
const anchor = page.locator('nav[aria-label="documentation"] .app-drawer-active');
await expect(anchor).toHaveAttribute('href', '/material-ui/react-card/');
await expect(anchor).toHaveText('Card');
});
test('should have plural url for Tabs', async ({ page }) => {
await page.goto('/material-ui/react-tabs/');
const anchor = page.locator('nav[aria-label="documentation"] .app-drawer-active');
await expect(anchor).toHaveAttribute('href', '/material-ui/react-tabs/');
await expect(anchor).toHaveText('Tabs');
});
test('should have plural url for Breadcrumbs', async ({ page }) => {
await page.goto('/material-ui/react-breadcrumbs/');
const anchor = page.locator('nav[aria-label="documentation"] .app-drawer-active');
await expect(anchor).toHaveAttribute('href', '/material-ui/react-breadcrumbs/');
await expect(anchor).toHaveText('Breadcrumbs');
});
test('should not have react- prefix for icons', async ({ page }) => {
await page.goto('/material-ui/icons/');
const anchor = page.locator('nav[aria-label="documentation"] .app-drawer-active');
await expect(anchor).toHaveAttribute('href', '/material-ui/icons/');
await expect(anchor).toHaveText('Icons');
});
test('should not have react- prefix for material-icons', async ({ page }) => {
await page.goto('/material-ui/material-icons/');
const anchor = page.locator('nav[aria-label="documentation"] .app-drawer-active');
await expect(anchor).toHaveAttribute('href', '/material-ui/material-icons/');
await expect(anchor).toHaveText('Material Icons');
});
test('should have correct API links when name of components conflicts with Base UI', async ({
page,
}) => {
await page.goto('/material-ui/react-button/');
const anchors = page.locator('div > h2#api ~ ul a');
const firstAnchor = anchors.first();
const textContent = await firstAnchor.textContent();
await expect(textContent).toEqual('<Button />');
await expect(firstAnchor).toHaveAttribute('href', '/material-ui/api/button/');
});
});
test.describe('API page', () => {
test('should have correct link for sidebar anchor', async ({ page }) => {
await page.goto('/material-ui/api/card/');
const anchor = page.locator('nav[aria-label="documentation"] ul a:text-is("Card")');
await expect(anchor).toHaveClass(/app-drawer-active/);
await expect(anchor).toHaveAttribute('href', '/material-ui/api/card/');
});
test('all the links in the main content should have correct prefix', async ({ page }) => {
await page.goto('/material-ui/api/card/');
const anchors = page.locator('div#main-content a');
const handles = await anchors.elementHandles();
const links = await Promise.all(handles.map((elm) => elm.getAttribute('href')));
links.forEach((link) => {
if (
link &&
['/getting-started', '/customization', '/guides', '/discover-more'].some((path) =>
link.includes(path),
)
) {
expect(link).toMatch(/^\/(material-ui|system)/);
}
expect(link).not.toMatch(/\/components/); // there should be no `/components` in the url anymore
if (link && link.startsWith('/system')) {
expect(link.startsWith('/system')).toBeTruthy();
expect(link.match(/\/system{1}/g)).toHaveLength(1); // should not have repeated `/system/system/*`
}
});
});
});
test.describe('Search', () => {
const retryToggleSearch = async (page: Page, count = 3) => {
try {
await page.keyboard.press('Meta+k');
await page.waitForSelector('input#docsearch-input', { timeout: 2000 });
} catch (error) {
if (count === 0) {
throw error;
}
await retryToggleSearch(page, count - 1);
}
};
test('should have correct link when searching component', async ({ page }) => {
await page.goto('/material-ui/getting-started/installation/');
await page.waitForLoadState('networkidle'); // wait for docsearch
await retryToggleSearch(page);
await page.type('input#docsearch-input', 'card', { delay: 50 });
const anchor = page.locator('.DocSearch-Hits a:has-text("Card")');
await expect(anchor.first()).toHaveAttribute('href', '/material-ui/react-card/');
});
test('should have correct link when searching API', async ({ page }) => {
await page.goto('/material-ui/getting-started/installation/');
await page.waitForLoadState('networkidle'); // wait for docsearch
await retryToggleSearch(page);
await page.type('input#docsearch-input', 'card api', { delay: 50 });
const anchor = page.locator('.DocSearch-Hits a:has-text("Card API")');
await expect(anchor.first()).toHaveAttribute('href', '/material-ui/api/card/');
});
});
});

View File

@@ -0,0 +1,10 @@
import { test as base, expect } from '@playwright/test';
import { TestFixture } from './playwright.config';
const test = base.extend<TestFixture>({});
test('should see the selected icon popup that match the query', async ({ page }) => {
await page.goto('/material-ui/material-icons/?selected=AcUnit');
await expect(page.locator('.MuiDialog-container h2:has-text("AcUnit")')).toBeVisible();
});

View File

@@ -0,0 +1,15 @@
import { PlaywrightTestConfig } from '@playwright/test';
export type TestFixture = {};
const config: PlaywrightTestConfig<TestFixture> = {
reportSlowTests: {
max: 1,
threshold: 60 * 1000, // 1min
},
use: {
baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || 'https://mui.com',
},
};
export default config;

View File

@@ -0,0 +1,29 @@
import { test as base, expect } from '@playwright/test';
import FEATURE_TOGGLE from 'docs/src/featureToggle';
import { TestFixture } from './playwright.config';
const test = base.extend<TestFixture>({});
test('able to navigate between products', async ({ page }) => {
await page.goto('/material-ui/getting-started/installation/');
await page.click('#mui-product-selector');
await expect(page.locator('#mui-product-menu')).toBeVisible();
await expect(
page.locator('#mui-product-menu a[href^="/material-ui/getting-started/"]'),
).toBeVisible();
await expect(page.locator('#mui-product-menu a[href^="/system/"]')).toHaveAttribute(
'href',
'/system/getting-started/',
);
await expect(page.locator('#mui-product-menu a[href^="/x/react-data-grid/"]')).toBeVisible();
await expect(page.locator('#mui-product-menu a[href^="/x/react-date-pickers/"]')).toBeVisible();
await expect(
page.locator('#mui-product-menu a[href^="/base-ui/getting-started/"]'),
).toBeVisible();
});