Commit 1d586aaf authored by Fabrizio Castellarin's avatar Fabrizio Castellarin Committed by Dan Abramov
Browse files

E2e jsdom fix (#1470)

* E2E: run tests when react is ready

* Entangle e2e with callbacks

* Remove unused e2e lines
parent bc2fc808
Showing with 124 additions and 72 deletions
+124 -72
{ {
"dependencies": { "dependencies": {
"babel-preset-latest": "6.16.0", "babel-preset-latest": "6.16.0",
"babel-register": "6.18.0", "babel-register": "6.22.0",
"babel-polyfill": "6.20.0", "babel-polyfill": "6.20.0",
"chai": "3.5.0", "chai": "3.5.0",
"jsdom": "9.8.3", "jsdom": "9.8.3",
......
...@@ -5,9 +5,6 @@ const path = require('path') ...@@ -5,9 +5,6 @@ const path = require('path')
let getMarkup let getMarkup
let resourceLoader let resourceLoader
// this value could be tweaked in order to let the resource
// retriever get every file and jsdom execute react
let timeToWaitForJsToExecute
if (process.env.E2E_FILE) { if (process.env.E2E_FILE) {
const file = path.isAbsolute(process.env.E2E_FILE) const file = path.isAbsolute(process.env.E2E_FILE)
...@@ -21,8 +18,6 @@ if (process.env.E2E_FILE) { ...@@ -21,8 +18,6 @@ if (process.env.E2E_FILE) {
null, null,
fs.readFileSync(path.join(path.dirname(file), resource.url.pathname), 'utf8') fs.readFileSync(path.join(path.dirname(file), resource.url.pathname), 'utf8')
) )
timeToWaitForJsToExecute = 0
} else if (process.env.E2E_URL) { } else if (process.env.E2E_URL) {
getMarkup = () => new Promise(resolve => { getMarkup = () => new Promise(resolve => {
http.get(process.env.E2E_URL, (res) => { http.get(process.env.E2E_URL, (res) => {
...@@ -32,11 +27,7 @@ if (process.env.E2E_FILE) { ...@@ -32,11 +27,7 @@ if (process.env.E2E_FILE) {
}) })
}) })
resourceLoader = (resource, callback) => { resourceLoader = (resource, callback) => resource.defaultFetch(callback)
return resource.defaultFetch(callback)
}
timeToWaitForJsToExecute = 100
} else { } else {
it.only('can run jsdom (at least one of "E2E_FILE" or "E2E_URL" environment variables must be provided)', () => { it.only('can run jsdom (at least one of "E2E_FILE" or "E2E_URL" environment variables must be provided)', () => {
expect(new Error('This isn\'t the error you are looking for.')).toBeUndefined() expect(new Error('This isn\'t the error you are looking for.')).toBeUndefined()
...@@ -47,16 +38,16 @@ export default feature => new Promise(async resolve => { ...@@ -47,16 +38,16 @@ export default feature => new Promise(async resolve => {
const markup = await getMarkup() const markup = await getMarkup()
const host = process.env.E2E_URL || 'http://localhost:3000' const host = process.env.E2E_URL || 'http://localhost:3000'
const doc = jsdom.jsdom(markup, { const doc = jsdom.jsdom(markup, {
features : { features: {
FetchExternalResources : ['script', 'css'], FetchExternalResources: ['script', 'css'],
ProcessExternalResources : ['script'], ProcessExternalResources: ['script'],
}, },
created: (_, win) => win.addEventListener('ReactFeatureDidMount', () => resolve(doc), true),
deferClose: true,
resourceLoader, resourceLoader,
url: `${host}#${feature}`, url: `${host}#${feature}`,
virtualConsole: jsdom.createVirtualConsole().sendTo(console), virtualConsole: jsdom.createVirtualConsole().sendTo(console),
}) })
doc.defaultView.addEventListener('load', () => { doc.close()
setTimeout(() => resolve(doc), timeToWaitForJsToExecute)
}, false)
}) })
import React from 'react'; import React from 'react';
class BuiltEmitter extends React.Component {
constructor(props) {
super(props)
this.callWhenDone = done => done();
}
componentDidMount() {
this.callWhenDone(() => document.dispatchEvent(new Event('ReactFeatureDidMount')));
}
render() {
const feature = React.cloneElement(React.Children.only(this.props.children), {
setCallWhenDone: done => {
this.callWhenDone = done;
}
});
return <div>{feature}</div>;
}
}
class App extends React.Component { class App extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
...@@ -96,7 +118,7 @@ class App extends React.Component { ...@@ -96,7 +118,7 @@ class App extends React.Component {
render() { render() {
const Feature = this.state.feature; const Feature = this.state.feature;
return Feature ? <Feature /> : null; return Feature ? <BuiltEmitter><Feature /></BuiltEmitter> : null;
} }
} }
......
...@@ -5,12 +5,17 @@ export default class extends React.Component { ...@@ -5,12 +5,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load(); const users = load();
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load(); const users = load();
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load([{ id: 42, name: '42' }]); const users = load([{ id: 42, name: '42' }]);
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = await load(); const users = await load();
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load('user_'); const users = load('user_');
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -18,12 +18,17 @@ export default class extends React.Component { ...@@ -18,12 +18,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load(); const users = load();
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load(); const users = load();
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const { users } = await load(); const { users } = await load();
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -12,6 +12,11 @@ export default class extends React.Component { ...@@ -12,6 +12,11 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
...@@ -20,7 +25,7 @@ export default class extends React.Component { ...@@ -20,7 +25,7 @@ export default class extends React.Component {
for (let user of load(4)) { for (let user of load(4)) {
users.push(user); users.push(user);
} }
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load(); const users = load();
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load({ age: 42 }); const users = load({ age: 42 });
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
componentDidMount() { componentDidMount() {
load().then(users => { load().then(users => {
this.setState({ users }); this.setState({ users }, () => this.done());
}); });
} }
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load(); const users = load();
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load({ id: 0, user: { id: 42, name: '42' } }); const users = load({ id: 0, user: { id: 42, name: '42' } });
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -13,12 +13,17 @@ export default class extends React.Component { ...@@ -13,12 +13,17 @@ export default class extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.done = () => {};
this.props.setCallWhenDone && this.props.setCallWhenDone((done) => {
this.done = done;
});
this.state = { users: [] }; this.state = { users: [] };
} }
async componentDidMount() { async componentDidMount() {
const users = load('user_'); const users = load('user_');
this.setState({ users }); this.setState({ users }, () => this.done());
} }
render() { render() {
......
...@@ -22,8 +22,6 @@ temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'` ...@@ -22,8 +22,6 @@ temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'`
function cleanup { function cleanup {
echo 'Cleaning up.' echo 'Cleaning up.'
cd $root_path cd $root_path
# Uncomment when snapshot testing is enabled by default:
# rm ./packages/react-scripts/template/src/__snapshots__/App.test.js.snap
rm -rf $temp_cli_path $temp_app_path rm -rf $temp_cli_path $temp_app_path
} }
...@@ -60,14 +58,6 @@ root_path=$PWD ...@@ -60,14 +58,6 @@ root_path=$PWD
npm install npm install
# If the node version is < 4, the script should just give an error.
if [ `node --version | sed -e 's/^v//' -e 's/\..\+//g'` -lt 4 ]
then
cd $temp_app_path
err_output=`node "$root_path"/packages/create-react-app/index.js test-node-version 2>&1 > /dev/null || echo ''`
[[ $err_output =~ You\ are\ running\ Node ]] && exit 0 || exit 1
fi
if [ "$USE_YARN" = "yes" ] if [ "$USE_YARN" = "yes" ]
then then
# Install Yarn so that the test can use it to install packages. # Install Yarn so that the test can use it to install packages.
......
...@@ -22,8 +22,6 @@ temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'` ...@@ -22,8 +22,6 @@ temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'`
function cleanup { function cleanup {
echo 'Cleaning up.' echo 'Cleaning up.'
cd $root_path cd $root_path
# Uncomment when snapshot testing is enabled by default:
# rm ./packages/react-scripts/template/src/__snapshots__/App.test.js.snap
rm -rf $temp_cli_path $temp_app_path rm -rf $temp_cli_path $temp_app_path
} }
...@@ -60,14 +58,6 @@ root_path=$PWD ...@@ -60,14 +58,6 @@ root_path=$PWD
npm install npm install
# If the node version is < 4, the script should just give an error.
if [ `node --version | sed -e 's/^v//' -e 's/\..\+//g'` -lt 4 ]
then
cd $temp_app_path
err_output=`node "$root_path"/packages/create-react-app/index.js test-node-version 2>&1 > /dev/null || echo ''`
[[ $err_output =~ You\ are\ running\ Node ]] && exit 0 || exit 1
fi
if [ "$USE_YARN" = "yes" ] if [ "$USE_YARN" = "yes" ]
then then
# Install Yarn so that the test can use it to install packages. # Install Yarn so that the test can use it to install packages.
...@@ -93,9 +83,6 @@ cp package.json package.json.orig ...@@ -93,9 +83,6 @@ cp package.json package.json.orig
# of those packages. # of those packages.
node $root_path/tasks/replace-own-deps.js node $root_path/tasks/replace-own-deps.js
# Remove .npmignore so the test template is added
rm $root_path/packages/react-scripts/.npmignore
# Finally, pack react-scripts # Finally, pack react-scripts
scripts_path=$root_path/packages/react-scripts/`npm pack` scripts_path=$root_path/packages/react-scripts/`npm pack`
...@@ -151,14 +138,7 @@ E2E_URL="http://localhost:3001" \ ...@@ -151,14 +138,7 @@ E2E_URL="http://localhost:3001" \
E2E_FILE=./build/index.html \ E2E_FILE=./build/index.html \
CI=true \ CI=true \
NODE_PATH=src \ NODE_PATH=src \
node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.js node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js
# Uncomment when snapshot testing is enabled by default:
# test -e src/__snapshots__/App.test.js.snap
# Test the server
REACT_APP_SHELL_ENV_MESSAGE=fromtheshell NODE_PATH=src npm start -- --smoke-test
REACT_APP_SHELL_ENV_MESSAGE=fromtheshell HTTPS=true NODE_PATH=src npm start -- --smoke-test
# ****************************************************************************** # ******************************************************************************
# Finally, let's check that everything still works after ejecting. # Finally, let's check that everything still works after ejecting.
...@@ -199,20 +179,14 @@ E2E_URL="http://localhost:3002" \ ...@@ -199,20 +179,14 @@ E2E_URL="http://localhost:3002" \
REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \
CI=true NODE_PATH=src \ CI=true NODE_PATH=src \
NODE_ENV=production \ NODE_ENV=production \
node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.js node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js
# Test "production" environment # Test "production" environment
E2E_FILE=./build/index.html \ E2E_FILE=./build/index.html \
CI=true \ CI=true \
NODE_ENV=production \ NODE_ENV=production \
NODE_PATH=src \ NODE_PATH=src \
node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.js node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js
# Uncomment when snapshot testing is enabled by default:
# test -e src/__snapshots__/App.test.js.snap
# Test the server
REACT_APP_SHELL_ENV_MESSAGE=fromtheshell NODE_PATH=src npm start -- --smoke-test
# Cleanup # Cleanup
cleanup cleanup
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment