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.
[test] Create end-to-end testing CI job (mui#25405)
- Loading branch information
Showing
18 changed files
with
327 additions
and
293 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
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
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
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
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
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,8 @@ | ||
module.exports = { | ||
extension: ['js', 'ts', 'tsx'], | ||
recursive: true, | ||
slow: 500, | ||
timeout: (process.env.CIRCLECI === 'true' ? 4 : 2) * 1000, // Circle CI has low-performance CPUs. | ||
reporter: 'dot', | ||
require: [require.resolve('../utils/setupBabel')], | ||
}; |
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,29 @@ | ||
# end-to-end testing | ||
|
||
End-to-end tests (short <abbr title="end-to-end">e2e</abbr>) are split into two parts: | ||
|
||
1. The rendered UI (short: fixture) | ||
2. Instrumentation of that UI | ||
|
||
## Rendered UI | ||
|
||
The rendered UI is located inside a separate file in `./tests` and written as a React component. | ||
If you're adding a new test prefer a new component instead of adding existing files since that might unknowingly alter existing tests. | ||
|
||
## Instrumentation | ||
|
||
We're using [`playwright`](https://playwright.dev) to replay user actions. | ||
Each test tests only a single fixture. | ||
A fixture can be loaded with `await renderFixture(fixturePath)` e.g. `renderFixture('Unstable_TrapFocus/OpenTrapFocus')`. | ||
|
||
## Commands | ||
|
||
For development `yarn test:e2e:dev` and `yarn test:e2e:run --watch` in separate terminals is recommended. | ||
|
||
| command | description | | ||
| ---------------------- | --------------------------------------------------------------------------------------------- | | ||
| `yarn test:e2e` | Full run | | ||
| `yarn test:e2e:dev` | Prepares the fixtures to be able to test in watchmode | | ||
| `yarn test:e2e:run` | Runs the tests (requires `yarn test:e2e:dev` or `yarn test:e2e:build`+`yarn test:e2e:server`) | | ||
| `yarn test:e2e:build` | Builds the webpack bundle for viewing the fixtures | | ||
| `yarn test:e2e:server` | Serves the fixture bundle. | |
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,30 @@ | ||
import * as React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import StyledEngineProvider from '@material-ui/core/StyledEngineProvider'; | ||
|
||
function TestViewer(props) { | ||
const { children } = props; | ||
|
||
// We're simulating `act(() => ReactDOM.render(children))` | ||
// In the end children passive effects should've been flushed. | ||
// React doesn't have any such guarantee outside of `act()` so we're approximating it. | ||
const [ready, setReady] = React.useState(false); | ||
React.useEffect(() => { | ||
setReady(true); | ||
}, []); | ||
|
||
return ( | ||
// TODO v5: remove once migration to emotion is completed | ||
<StyledEngineProvider injectFirst> | ||
<div aria-busy={!ready} data-testid="testcase"> | ||
{children} | ||
</div> | ||
</StyledEngineProvider> | ||
); | ||
} | ||
|
||
TestViewer.propTypes = { | ||
children: PropTypes.node.isRequired, | ||
}; | ||
|
||
export default TestViewer; |
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,94 @@ | ||
import * as React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'; | ||
import TestViewer from './TestViewer'; | ||
|
||
const fixtures = []; | ||
|
||
const requireFixtures = require.context('./tests', true, /\.(js|ts|tsx)$/); | ||
requireFixtures.keys().forEach((path) => { | ||
const [suite, name] = path | ||
.replace('./', '') | ||
.replace(/\.\w+$/, '') | ||
.split('/'); | ||
fixtures.push({ | ||
path, | ||
suite: `e2e/${suite}`, | ||
name, | ||
case: requireFixtures(path).default, | ||
}); | ||
}); | ||
|
||
function App() { | ||
function computeIsDev() { | ||
if (window.location.hash === '#dev') { | ||
return true; | ||
} | ||
if (window.location.hash === '#no-dev') { | ||
return false; | ||
} | ||
return process.env.NODE_ENV === 'development'; | ||
} | ||
const [isDev, setDev] = React.useState(computeIsDev); | ||
React.useEffect(() => { | ||
function handleHashChange() { | ||
setDev(computeIsDev()); | ||
} | ||
window.addEventListener('hashchange', handleHashChange); | ||
|
||
return () => { | ||
window.removeEventListener('hashchange', handleHashChange); | ||
}; | ||
}, []); | ||
|
||
function computePath(test) { | ||
return `/${test.suite}/${test.name}`; | ||
} | ||
|
||
return ( | ||
<Router> | ||
<Switch> | ||
{fixtures.map((test) => { | ||
const path = computePath(test); | ||
const TestCase = test.case; | ||
if (TestCase === undefined) { | ||
console.warn('Missing test.case for ', test); | ||
return null; | ||
} | ||
|
||
return ( | ||
<Route key={path} exact path={path}> | ||
<TestViewer> | ||
<TestCase /> | ||
</TestViewer> | ||
</Route> | ||
); | ||
})} | ||
</Switch> | ||
<div hidden={!isDev}> | ||
<p> | ||
Devtools can be enabled by appending <code>#dev</code> in the addressbar or disabled by | ||
appending <code>#no-dev</code>. | ||
</p> | ||
<a href="#no-dev">Hide devtools</a> | ||
<details> | ||
<summary id="my-test-summary">nav for all tests</summary> | ||
<nav id="tests"> | ||
<ol> | ||
{fixtures.map((test) => { | ||
const path = computePath(test); | ||
return ( | ||
<li key={path}> | ||
<Link to={path}>{path}</Link> | ||
</li> | ||
); | ||
})} | ||
</ol> | ||
</nav> | ||
</details> | ||
</div> | ||
</Router> | ||
); | ||
} | ||
|
||
ReactDOM.render(<App />, document.getElementById('react-root')); |
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,62 @@ | ||
import { expect } from 'chai'; | ||
import * as playwright from 'playwright'; | ||
|
||
describe('e2e', () => { | ||
const baseUrl = 'http://localhost:5000'; | ||
let browser: playwright.Browser; | ||
let page: playwright.Page; | ||
|
||
async function renderFixture(fixturePath: string) { | ||
await page.goto(`${baseUrl}/e2e/${fixturePath}#no-dev`); | ||
} | ||
|
||
before(async () => { | ||
browser = await playwright.chromium.launch({ | ||
headless: true, | ||
}); | ||
page = await browser.newPage(); | ||
await page.goto(`${baseUrl}#no-dev`); | ||
}); | ||
|
||
after(async () => { | ||
await browser.close(); | ||
}); | ||
|
||
describe('<TrapFocus />', () => { | ||
it('should loop the tab key', async () => { | ||
await renderFixture('Unstable_TrapFocus/OpenTrapFocus'); | ||
|
||
expect( | ||
await page.evaluate(() => document.activeElement?.getAttribute('data-testid')), | ||
).to.equal('root'); | ||
|
||
await page.keyboard.press('Tab'); | ||
expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('x'); | ||
await page.keyboard.press('Tab'); | ||
expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('cancel'); | ||
await page.keyboard.press('Tab'); | ||
expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('ok'); | ||
await page.keyboard.press('Tab'); | ||
expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('x'); | ||
|
||
await page.focus('[data-testid="initial-focus"]'); | ||
expect( | ||
await page.evaluate(() => document.activeElement?.getAttribute('data-testid')), | ||
).to.equal('root'); | ||
await page.focus('text=x'); | ||
await page.keyboard.press('Shift+Tab'); | ||
expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('ok'); | ||
}); | ||
|
||
it('should focus on first focus element after last has received a tab click', async () => { | ||
await renderFixture('Unstable_TrapFocus/OpenTrapFocus'); | ||
|
||
await page.keyboard.press('Tab'); | ||
expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('x'); | ||
await page.keyboard.press('Tab'); | ||
expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('cancel'); | ||
await page.keyboard.press('Tab'); | ||
expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('ok'); | ||
}); | ||
}); | ||
}); |
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,4 @@ | ||
{ | ||
"public": "build", | ||
"rewrites": [{ "source": "**", "destination": "index.html" }] | ||
} |
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,16 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>vrtest</title> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" /> | ||
<style> | ||
body { | ||
background-color: white; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div id="react-root"></div> | ||
</body> | ||
</html> |
Oops, something went wrong.