Skip to content

Commit 3a37b99

Browse files
EnoahNetzachgaearon
authored andcommitted
Use a more sophisticated template for end-to-end testing. (facebook#1187)
* Use a more sophisticated template for end-to-end testing. * Not publish integration tests to npm * Use "commander" for cli argv handling * Handle different scripts version forms and exits without a name given * Prepare the commands for testing with a template * Fix dev "template" path * Add various features to test * Test various features separately * Test language features * Comment unused e2e.sh lines * Add "development" tests * Test environment variables * Test webpack plugins * Replace kitchensink README * Switch integration tests from jest to mocha * Use `fs-extra` * Use the correct folders * Do some cleanup * Print a better message for `--template` * Test `npm start` with and without https * Separate fast e2e testing from kitchensink testing * Hide `--internal-testing-template` (former `--template`) CLI option
1 parent 2734b6f commit 3a37b99

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1252
-2
lines changed

.npmignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/fixtures

fixtures/kitchensink/.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["latest"]
3+
}

fixtures/kitchensink/.env

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
REACT_APP_FILE_ENV_MESSAGE=fromtheenvfile

fixtures/kitchensink/.flowconfig

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[ignore]
2+
<PROJECT_ROOT>/node_modules/fbjs/.*
3+
4+
[include]
5+
6+
[libs]
7+
8+
[options]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"dependencies": {
3+
"babel-preset-latest": "6.16.0",
4+
"babel-register": "6.18.0",
5+
"babel-polyfill": "6.20.0",
6+
"chai": "3.5.0",
7+
"jsdom": "9.8.3",
8+
"mocha": "3.2.0"
9+
}
10+
}

fixtures/kitchensink/gitignore

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# See http://help.github.com/ignore-files/ for more about ignoring files.
2+
3+
# dependencies
4+
node_modules
5+
6+
# testing
7+
coverage
8+
9+
# production
10+
build
11+
12+
# misc
13+
.DS_Store
14+
.env
15+
npm-debug.log
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { expect } from 'chai'
2+
import initDOM from './initDOM'
3+
4+
describe('Integration', () => {
5+
describe('Environment variables', () => {
6+
it('NODE_PATH', async () => {
7+
const doc = await initDOM('node-path')
8+
9+
expect(doc.getElementById('feature-node-path').childElementCount).to.equal(4)
10+
})
11+
12+
it('shell env variables', async () => {
13+
const doc = await initDOM('shell-env-variables')
14+
15+
expect(doc.getElementById('feature-shell-env-variables').textContent).to.equal('fromtheshell.')
16+
})
17+
18+
it('file env variables', async () => {
19+
const doc = await initDOM('file-env-variables')
20+
21+
expect(doc.getElementById('feature-file-env-variables').textContent).to.equal('fromtheenvfile.')
22+
})
23+
})
24+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const fs = require('fs')
2+
const http = require('http')
3+
const jsdom = require('jsdom')
4+
const path = require('path')
5+
6+
let getMarkup
7+
let resourceLoader
8+
// this value could be tweaked in order to let the resource
9+
// retriever get every file and jsdom execute react
10+
let timeToWaitForJsToExecute
11+
12+
if (process.env.E2E_FILE) {
13+
const file = path.isAbsolute(process.env.E2E_FILE)
14+
? process.env.E2E_FILE
15+
: path.join(process.cwd(), process.env.E2E_FILE)
16+
17+
const markup = fs.readFileSync(file, 'utf8')
18+
getMarkup = () => markup
19+
20+
resourceLoader = (resource, callback) => callback(
21+
null,
22+
fs.readFileSync(path.join(path.dirname(file), resource.url.pathname), 'utf8')
23+
)
24+
25+
timeToWaitForJsToExecute = 0
26+
} else if (process.env.E2E_URL) {
27+
getMarkup = () => new Promise(resolve => {
28+
http.get(process.env.E2E_URL, (res) => {
29+
let rawData = ''
30+
res.on('data', chunk => rawData += chunk)
31+
res.on('end', () => resolve(rawData))
32+
})
33+
})
34+
35+
resourceLoader = (resource, callback) => {
36+
return resource.defaultFetch(callback)
37+
}
38+
39+
timeToWaitForJsToExecute = 100
40+
} else {
41+
it.only('can run jsdom (at least one of "E2E_FILE" or "E2E_URL" environment variables must be provided)', () => {
42+
expect(new Error('This isn\'t the error you are looking for.')).toBeUndefined()
43+
})
44+
}
45+
46+
export default feature => new Promise(async resolve => {
47+
const markup = await getMarkup()
48+
const host = process.env.E2E_URL || 'http://localhost:3000'
49+
const doc = jsdom.jsdom(markup, {
50+
features : {
51+
FetchExternalResources : ['script', 'css'],
52+
ProcessExternalResources : ['script'],
53+
},
54+
resourceLoader,
55+
url: `${host}#${feature}`,
56+
virtualConsole: jsdom.createVirtualConsole().sendTo(console),
57+
})
58+
59+
doc.defaultView.addEventListener('load', () => {
60+
setTimeout(() => resolve(doc), timeToWaitForJsToExecute)
61+
}, false)
62+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { expect } from 'chai'
2+
import initDOM from './initDOM'
3+
4+
describe('Integration', () => {
5+
describe('Language syntax', () => {
6+
it('array destructuring', async () => {
7+
const doc = await initDOM('array-destructuring')
8+
9+
expect(doc.getElementById('feature-array-destructuring').childElementCount).to.equal(4)
10+
})
11+
12+
it('array spread', async () => {
13+
const doc = await initDOM('array-spread')
14+
15+
expect(doc.getElementById('feature-array-spread').childElementCount).to.equal(4)
16+
})
17+
18+
it('async/await', async () => {
19+
const doc = await initDOM('async-await')
20+
21+
expect(doc.getElementById('feature-async-await').childElementCount).to.equal(4)
22+
})
23+
24+
it('class properties', async () => {
25+
const doc = await initDOM('class-properties')
26+
27+
expect(doc.getElementById('feature-class-properties').childElementCount).to.equal(4)
28+
})
29+
30+
it('computed properties', async () => {
31+
const doc = await initDOM('computed-properties')
32+
33+
expect(doc.getElementById('feature-computed-properties').childElementCount).to.equal(4)
34+
})
35+
36+
it('custom interpolation', async () => {
37+
const doc = await initDOM('custom-interpolation')
38+
39+
expect(doc.getElementById('feature-custom-interpolation').childElementCount).to.equal(4)
40+
})
41+
42+
it('default parameters', async () => {
43+
const doc = await initDOM('default-parameters')
44+
45+
expect(doc.getElementById('feature-default-parameters').childElementCount).to.equal(4)
46+
})
47+
48+
it('destructuring and await', async () => {
49+
const doc = await initDOM('destructuring-and-await')
50+
51+
expect(doc.getElementById('feature-destructuring-and-await').childElementCount).to.equal(4)
52+
})
53+
54+
it('generators', async () => {
55+
const doc = await initDOM('generators')
56+
57+
expect(doc.getElementById('feature-generators').childElementCount).to.equal(4)
58+
})
59+
60+
it('object destructuring', async () => {
61+
const doc = await initDOM('object-destructuring')
62+
63+
expect(doc.getElementById('feature-object-destructuring').childElementCount).to.equal(4)
64+
})
65+
66+
it('object spread', async () => {
67+
const doc = await initDOM('object-spread')
68+
69+
expect(doc.getElementById('feature-object-spread').childElementCount).to.equal(4)
70+
})
71+
72+
it('promises', async () => {
73+
const doc = await initDOM('promises')
74+
75+
expect(doc.getElementById('feature-promises').childElementCount).to.equal(4)
76+
})
77+
78+
it('rest + default', async () => {
79+
const doc = await initDOM('rest-and-default')
80+
81+
expect(doc.getElementById('feature-rest-and-default').childElementCount).to.equal(4)
82+
})
83+
84+
it('rest parameters', async () => {
85+
const doc = await initDOM('rest-parameters')
86+
87+
expect(doc.getElementById('feature-rest-parameters').childElementCount).to.equal(4)
88+
})
89+
90+
it('template interpolation', async () => {
91+
const doc = await initDOM('template-interpolation')
92+
93+
expect(doc.getElementById('feature-template-interpolation').childElementCount).to.equal(4)
94+
})
95+
})
96+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { expect } from 'chai'
2+
import initDOM from './initDOM'
3+
4+
describe('Integration', () => {
5+
describe('Webpack plugins', () => {
6+
it('css inclusion', async () => {
7+
const doc = await initDOM('css-inclusion')
8+
9+
expect(doc.getElementsByTagName('style')[0].textContent.replace(/\s/g, ''))
10+
.to.match(/#feature-css-inclusion\{background:.+;color:.+}/)
11+
})
12+
13+
it('image inclusion', async () => {
14+
const doc = await initDOM('image-inclusion')
15+
16+
expect(doc.getElementById('feature-image-inclusion').src).to.match(/^data:image\/jpeg;base64.+==$/)
17+
})
18+
19+
it('no ext inclusion', async () => {
20+
const doc = await initDOM('no-ext-inclusion')
21+
22+
expect(doc.getElementById('feature-no-ext-inclusion').textContent)
23+
.to.equal('This is just a file without an extension.')
24+
})
25+
26+
it('json inclusion', async () => {
27+
const doc = await initDOM('json-inclusion')
28+
29+
expect(doc.getElementById('feature-json-inclusion').textContent).to.equal('This is an abstract.')
30+
})
31+
32+
it('svg inclusion', async () => {
33+
const doc = await initDOM('svg-inclusion')
34+
35+
expect(doc.getElementById('feature-svg-inclusion').src).to.match(/\/static\/media\/logo\..+\.svg$/)
36+
})
37+
38+
it('unknown ext inclusion', async () => {
39+
const doc = await initDOM('unknown-ext-inclusion')
40+
41+
expect(doc.getElementById('feature-unknown-ext-inclusion').textContent).to.equal('Whoooo, spooky!.')
42+
})
43+
})
44+
})
24.3 KB
Binary file not shown.
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
7+
<title>React App</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
</body>
12+
</html>

fixtures/kitchensink/src/App.js

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import React from 'react';
2+
3+
class App extends React.Component {
4+
constructor(props) {
5+
super(props);
6+
7+
this.state = { feature: null };
8+
9+
this.setFeature = this.setFeature.bind(this);
10+
}
11+
12+
componentDidMount() {
13+
switch (location.hash.slice(1)) {
14+
case 'array-destructuring':
15+
require.ensure([], () => this.setFeature(require('./features/syntax/ArrayDestructuring').default));
16+
break;
17+
case 'array-spread':
18+
require.ensure([], () => this.setFeature(require('./features/syntax/ArraySpread').default));
19+
break;
20+
case 'async-await':
21+
require.ensure([], () => this.setFeature(require('./features/syntax/AsyncAwait').default));
22+
break;
23+
case 'class-properties':
24+
require.ensure([], () => this.setFeature(require('./features/syntax/ClassProperties').default));
25+
break;
26+
case 'computed-properties':
27+
require.ensure([], () => this.setFeature(require('./features/syntax/ComputedProperties').default));
28+
break;
29+
case 'css-inclusion':
30+
require.ensure([], () => this.setFeature(require('./features/webpack/CssInclusion').default));
31+
break;
32+
case 'custom-interpolation':
33+
require.ensure([], () => this.setFeature(require('./features/syntax/CustomInterpolation').default));
34+
break;
35+
case 'default-parameters':
36+
require.ensure([], () => this.setFeature(require('./features/syntax/DefaultParameters').default));
37+
break;
38+
case 'destructuring-and-await':
39+
require.ensure([], () => this.setFeature(require('./features/syntax/DestructuringAndAwait').default));
40+
break;
41+
case 'file-env-variables':
42+
require.ensure([], () => this.setFeature(require('./features/env/FileEnvVariables').default));
43+
break;
44+
case 'generators':
45+
require.ensure([], () => this.setFeature(require('./features/syntax/Generators').default));
46+
break;
47+
case 'image-inclusion':
48+
require.ensure([], () => this.setFeature(require('./features/webpack/ImageInclusion').default));
49+
break;
50+
case 'json-inclusion':
51+
require.ensure([], () => this.setFeature(require('./features/webpack/JsonInclusion').default));
52+
break;
53+
case 'node-path':
54+
require.ensure([], () => this.setFeature(require('./features/env/NodePath').default));
55+
break;
56+
case 'no-ext-inclusion':
57+
require.ensure([], () => this.setFeature(require('./features/webpack/NoExtInclusion').default));
58+
break;
59+
case 'object-destructuring':
60+
require.ensure([], () => this.setFeature(require('./features/syntax/ObjectDestructuring').default));
61+
break;
62+
case 'object-spread':
63+
require.ensure([], () => this.setFeature(require('./features/syntax/ObjectSpread').default));
64+
break;
65+
case 'promises':
66+
require.ensure([], () => this.setFeature(require('./features/syntax/Promises').default));
67+
break;
68+
case 'rest-and-default':
69+
require.ensure([], () => this.setFeature(require('./features/syntax/RestAndDefault').default));
70+
break;
71+
case 'rest-parameters':
72+
require.ensure([], () => this.setFeature(require('./features/syntax/RestParameters').default));
73+
break;
74+
case 'shell-env-variables':
75+
require.ensure([], () => this.setFeature(require('./features/env/ShellEnvVariables').default));
76+
break;
77+
case 'svg-inclusion':
78+
require.ensure([], () => this.setFeature(require('./features/webpack/SvgInclusion').default));
79+
break;
80+
case 'template-interpolation':
81+
require.ensure([], () => this.setFeature(require('./features/syntax/TemplateInterpolation').default));
82+
break;
83+
case 'unknown-ext-inclusion':
84+
require.ensure([], () => this.setFeature(require('./features/webpack/UnknownExtInclusion').default)
85+
);
86+
break;
87+
default:
88+
this.setFeature(null);
89+
break;
90+
}
91+
}
92+
93+
setFeature(feature) {
94+
this.setState({ feature });
95+
}
96+
97+
render() {
98+
const Feature = this.state.feature;
99+
return Feature ? <Feature /> : null;
100+
}
101+
}
102+
103+
export default App;

fixtures/kitchensink/src/App.test.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import App from './App';
4+
5+
it('renders without crashing', () => {
6+
const div = document.createElement('div');
7+
ReactDOM.render(<App />, div);
8+
});

0 commit comments

Comments
 (0)