forked from mui/material-ui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[wip][poc] Add Pigment CSS screenshot test (mui#43280)
- Loading branch information
1 parent
1676ff4
commit 5fce989
Showing
13 changed files
with
1,069 additions
and
20 deletions.
There are no files selected for viewing
This file contains 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
121 changes: 121 additions & 0 deletions
121
apps/pigment-css-next-app/src/app/material-ui/react-select/page.tsx
This file contains 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,121 @@ | ||
'use client'; | ||
import * as React from 'react'; | ||
import BasicSelect from '../../../../../../docs/data/material/components/selects/BasicSelect'; | ||
import ControlledOpenSelect from '../../../../../../docs/data/material/components/selects/ControlledOpenSelect'; | ||
import CustomizedSelects from '../../../../../../docs/data/material/components/selects/CustomizedSelects'; | ||
import DialogSelect from '../../../../../../docs/data/material/components/selects/DialogSelect'; | ||
import GroupedSelect from '../../../../../../docs/data/material/components/selects/GroupedSelect'; | ||
import MultipleSelect from '../../../../../../docs/data/material/components/selects/MultipleSelect'; | ||
import MultipleSelectCheckmarks from '../../../../../../docs/data/material/components/selects/MultipleSelectCheckmarks'; | ||
import MultipleSelectChip from '../../../../../../docs/data/material/components/selects/MultipleSelectChip'; | ||
import MultipleSelectNative from '../../../../../../docs/data/material/components/selects/MultipleSelectNative'; | ||
import MultipleSelectPlaceholder from '../../../../../../docs/data/material/components/selects/MultipleSelectPlaceholder'; | ||
import NativeSelectDemo from '../../../../../../docs/data/material/components/selects/NativeSelectDemo'; | ||
import SelectAutoWidth from '../../../../../../docs/data/material/components/selects/SelectAutoWidth'; | ||
import SelectLabels from '../../../../../../docs/data/material/components/selects/SelectLabels'; | ||
import SelectOtherProps from '../../../../../../docs/data/material/components/selects/SelectOtherProps'; | ||
import SelectSmall from '../../../../../../docs/data/material/components/selects/SelectSmall'; | ||
import SelectVariants from '../../../../../../docs/data/material/components/selects/SelectVariants'; | ||
|
||
export default function Selects() { | ||
return ( | ||
<React.Fragment> | ||
<section> | ||
<h2> Basic Select</h2> | ||
<div className="demo-container"> | ||
<BasicSelect /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Controlled Open Select</h2> | ||
<div className="demo-container"> | ||
<ControlledOpenSelect /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Customized Selects</h2> | ||
<div className="demo-container"> | ||
<CustomizedSelects /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Dialog Select</h2> | ||
<div className="demo-container"> | ||
<DialogSelect /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Grouped Select</h2> | ||
<div className="demo-container"> | ||
<GroupedSelect /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Multiple Select</h2> | ||
<div className="demo-container"> | ||
<MultipleSelect /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Multiple Select Checkmarks</h2> | ||
<div className="demo-container"> | ||
<MultipleSelectCheckmarks /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Multiple Select Chip</h2> | ||
<div className="demo-container"> | ||
<MultipleSelectChip /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Multiple Select Native</h2> | ||
<div className="demo-container"> | ||
<MultipleSelectNative /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Multiple Select Placeholder</h2> | ||
<div className="demo-container"> | ||
<MultipleSelectPlaceholder /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Native Select Demo</h2> | ||
<div className="demo-container"> | ||
<NativeSelectDemo /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Select Auto Width</h2> | ||
<div className="demo-container"> | ||
<SelectAutoWidth /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Select Labels</h2> | ||
<div className="demo-container"> | ||
<SelectLabels /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Select Other Props</h2> | ||
<div className="demo-container"> | ||
<SelectOtherProps /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Select Small</h2> | ||
<div className="demo-container"> | ||
<SelectSmall /> | ||
</div> | ||
</section> | ||
<section> | ||
<h2> Select Variants</h2> | ||
<div className="demo-container"> | ||
<SelectVariants /> | ||
</div> | ||
</section> | ||
</React.Fragment> | ||
); | ||
} |
This file contains 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,7 @@ | ||
module.exports = { | ||
recursive: true, | ||
slow: 500, | ||
timeout: (process.env.CIRCLECI === 'true' ? 4 : 2) * 1000, // Circle CI has low-performance CPUs. | ||
reporter: 'dot', | ||
require: ['@mui/internal-test-utils/setupBabelPlaywright'], | ||
}; |
This file contains 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
34 changes: 34 additions & 0 deletions
34
apps/pigment-css-vite-app/src/pages/fixtures/PigmentCssSelectOpen.js
This file contains 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,34 @@ | ||
import * as React from 'react'; | ||
import Box from '@mui/material/Box'; | ||
import InputLabel from '@mui/material/InputLabel'; | ||
import MenuItem from '@mui/material/MenuItem'; | ||
import FormControl from '@mui/material/FormControl'; | ||
import Select from '@mui/material/Select'; | ||
|
||
export default function BasicSelect() { | ||
const [age, setAge] = React.useState(10); | ||
|
||
const handleChange = (event) => { | ||
setAge(event.target.value); | ||
}; | ||
|
||
return ( | ||
<Box sx={{ minWidth: 120, minHeight: 250 }}> | ||
<FormControl fullWidth> | ||
<InputLabel id="demo-simple-select-label">Age</InputLabel> | ||
<Select | ||
defaultOpen | ||
labelId="demo-simple-select-label" | ||
id="demo-simple-select" | ||
value={age} | ||
label="Age" | ||
onChange={handleChange} | ||
> | ||
<MenuItem value={10}>Ten</MenuItem> | ||
<MenuItem value={20}>Twenty</MenuItem> | ||
<MenuItem value={30}>Thirty</MenuItem> | ||
</Select> | ||
</FormControl> | ||
</Box> | ||
); | ||
} |
117 changes: 117 additions & 0 deletions
117
apps/pigment-css-vite-app/src/pages/fixtures/index.test.js
This file contains 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,117 @@ | ||
import * as path from 'path'; | ||
import * as fse from 'fs-extra'; | ||
import * as playwright from 'playwright'; | ||
|
||
async function main() { | ||
const baseUrl = 'http://localhost:5001/fixtures'; | ||
const screenshotDir = path.resolve('screenshots/chrome'); | ||
const browser = await playwright.chromium.launch({ | ||
args: ['--font-render-hinting=none'], | ||
// otherwise the loaded google Roboto font isn't applied | ||
headless: false, | ||
}); | ||
// reuse viewport from `vrtest` | ||
// https://github.com/nathanmarks/vrtest/blob/1185b852a6c1813cedf5d81f6d6843d9a241c1ce/src/server/runner.js#L44 | ||
const page = await browser.newPage({ | ||
viewport: { width: 1000, height: 700 }, | ||
reducedMotion: 'reduce', | ||
}); | ||
|
||
// Block images since they slow down tests (need download). | ||
// They're also most likely decorative for documentation demos | ||
await page.route(/./, async (route, request) => { | ||
const type = await request.resourceType(); | ||
if (type === 'image') { | ||
route.abort(); | ||
} else { | ||
route.continue(); | ||
} | ||
}); | ||
|
||
// Wait for all requests to finish. | ||
// This should load shared resources such as fonts. | ||
await page.goto(`${baseUrl}`, { waitUntil: 'networkidle0' }); | ||
// If we still get flaky fonts after awaiting this try `document.fonts.ready` | ||
// await page.waitForSelector('[data-webfontloader="active"]', { state: 'attached' }); | ||
|
||
// Simulate portrait mode for date pickers. | ||
// See `useIsLandscape`. | ||
await page.evaluate(() => { | ||
Object.defineProperty(window.screen.orientation, 'angle', { | ||
get() { | ||
return 0; | ||
}, | ||
}); | ||
}); | ||
|
||
let routes = await page.$$eval('#tests a', (links) => { | ||
return links.map((link) => link.href); | ||
}); | ||
routes = routes.map((route) => route.replace(baseUrl, '')); | ||
|
||
async function renderFixture(index) { | ||
// Use client-side routing which is much faster than full page navigation via page.goto(). | ||
// Could become an issue with test isolation. | ||
// If tests are flaky due to global pollution switch to page.goto(route); | ||
// puppeteers built-in click() times out | ||
await page.$eval(`#tests li:nth-of-type(${index + 1}) a`, (link) => { | ||
link.click(); | ||
}); | ||
// Move cursor offscreen to not trigger unwanted hover effects. | ||
page.mouse.move(0, 0); | ||
|
||
const testcase = await page.waitForSelector('#root-demo'); | ||
|
||
return testcase; | ||
} | ||
|
||
async function takeScreenshot({ testcase, route }) { | ||
const screenshotPath = path.resolve(screenshotDir, `.${route}.png`); | ||
await fse.ensureDir(path.dirname(screenshotPath)); | ||
|
||
const explicitScreenshotTarget = await page.$('[data-testid="screenshot-target"]'); | ||
const screenshotTarget = explicitScreenshotTarget || testcase; | ||
|
||
await screenshotTarget.screenshot({ | ||
path: screenshotPath, | ||
type: 'png', | ||
animations: 'disabled', | ||
}); | ||
} | ||
|
||
// prepare screenshots | ||
await fse.emptyDir(screenshotDir); | ||
|
||
describe('visual regressions', () => { | ||
beforeEach(async () => { | ||
await page.evaluate(() => { | ||
localStorage.clear(); | ||
}); | ||
}); | ||
|
||
after(async () => { | ||
await browser.close(); | ||
}); | ||
|
||
routes.forEach((route, index) => { | ||
it(`creates screenshots of ${route}`, async function test() { | ||
// With the playwright inspector we might want to call `page.pause` which would lead to a timeout. | ||
if (process.env.PWDEBUG) { | ||
this.timeout(0); | ||
} | ||
|
||
const testcase = await renderFixture(index); | ||
await takeScreenshot({ testcase, route }); | ||
}); | ||
}); | ||
}); | ||
|
||
run(); | ||
} | ||
|
||
main().catch((error) => { | ||
// error during setup. | ||
// Throwing lets mocha hang. | ||
console.error(error); | ||
process.exit(1); | ||
}); |
This file contains 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,80 @@ | ||
import * as React from 'react'; | ||
import { useLocation, matchRoutes, Link } from 'react-router-dom'; | ||
import routes from '~react-pages'; | ||
import IndexLayout from '../../Layout'; | ||
|
||
export default function Layout() { | ||
const location = useLocation(); | ||
const matchedRoute = React.useMemo( | ||
() => matchRoutes(routes, location.pathname)?.[0], | ||
[location.pathname], | ||
); | ||
|
||
const materialUIRoute = React.useMemo( | ||
() => matchRoutes(routes, location.pathname.replace('fixtures', 'material-ui'))?.[0], | ||
[location.pathname], | ||
); | ||
|
||
const demo = new URLSearchParams(location.search).get('demo'); | ||
const fixturesRoutes = (matchedRoute?.route.children ?? []).filter( | ||
(item) => !!item.path && item.path !== 'index.test', | ||
); | ||
|
||
const demosRoutes = (materialUIRoute?.route.children ?? []).filter( | ||
(item) => !!item.path && item.path.indexOf('react-pagination') < 0, | ||
); | ||
|
||
return ( | ||
<IndexLayout> | ||
{demo && ( | ||
<div id="root-demo"> | ||
{fixturesRoutes.find((item) => item.path === demo)?.element} | ||
{demosRoutes.find((item) => item.path === demo)?.element} | ||
</div> | ||
)} | ||
<div> | ||
<h1>Fixtures Material UI + Pigment CSS</h1> | ||
<nav id="tests"> | ||
<ul | ||
sx={{ | ||
margin: 0, | ||
marginBlock: '1rem', | ||
padding: 0, | ||
paddingLeft: '1.5rem', | ||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: '0.5rem', | ||
}} | ||
> | ||
{fixturesRoutes.map((item) => ( | ||
<li key={item.path}> | ||
<Link | ||
to={`/fixtures/?demo=${item.path}`} | ||
sx={{ | ||
textDecoration: 'underline', | ||
fontSize: '17px', | ||
}} | ||
> | ||
{item.path} | ||
</Link> | ||
</li> | ||
))} | ||
{demosRoutes.map((item) => ( | ||
<li key={item.path}> | ||
<Link | ||
to={`/fixtures/?demo=${item.path}`} | ||
sx={{ | ||
textDecoration: 'underline', | ||
fontSize: '17px', | ||
}} | ||
> | ||
{item.path} | ||
</Link> | ||
</li> | ||
))} | ||
</ul> | ||
</nav> | ||
</div> | ||
</IndexLayout> | ||
); | ||
} |
Oops, something went wrong.