Skip to content

Commit 1903ad3

Browse files
EnoahNetzachbondz
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 820a648 commit 1903ad3

Some content is hidden

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

78 files changed

+1653
-74
lines changed

.travis.yml

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
---
22
language: node_js
33
node_js:
4-
- 0.10
54
- 4
65
- 6
76
cache:
87
directories:
98
- node_modules
109
- packages/create-react-app/node_modules
1110
- packages/react-scripts/node_modules
12-
script: tasks/e2e.sh
11+
script:
12+
- 'if [ $TEST_SUITE = "simple" ]; then tasks/e2e-simple.sh; fi'
13+
- 'if [ $TEST_SUITE = "installs" ]; then tasks/e2e-installs.sh; fi'
14+
- 'if [ $TEST_SUITE = "kitchensink" ]; then tasks/e2e-kitchensink.sh; fi'
1315
env:
14-
- USE_YARN=no
15-
- USE_YARN=yes
16+
global:
17+
- USE_YARN=no
18+
matrix:
19+
- TEST_SUITE=simple
20+
- TEST_SUITE=installs
21+
- TEST_SUITE=kitchensink
22+
matrix:
23+
include:
24+
- node_js: 0.10
25+
env: TEST_SUITE=simple
26+
- node_js: 6
27+
env: USE_YARN=yes TEST_SUITE=simple

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"build": "node packages/react-scripts/scripts/build.js",
55
"changelog": "lerna-changelog",
66
"create-react-app": "tasks/cra.sh",
7-
"e2e": "tasks/e2e.sh",
7+
"e2e": "tasks/e2e-simple.sh",
88
"postinstall": "lerna bootstrap",
99
"publish": "tasks/release.sh",
1010
"start": "node packages/react-scripts/scripts/start.js",

packages/create-react-app/index.js

+14-7
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ if (currentNodeVersion.split('.')[0] < 4) {
5252
process.exit(1);
5353
}
5454

55+
var commander = require('commander');
5556
var fs = require('fs-extra');
5657
var path = require('path');
5758
var execSync = require('child_process').execSync;
@@ -60,7 +61,7 @@ var semver = require('semver');
6061

6162
var projectName;
6263

63-
var program = require('commander')
64+
var program = commander
6465
.version(require('./package.json').version)
6566
.arguments('<project-directory>')
6667
.usage(chalk.green('<project-directory>') + ' [options]')
@@ -69,6 +70,7 @@ var program = require('commander')
6970
})
7071
.option('--verbose', 'print additional logs')
7172
.option('--scripts-version <alternative-package>', 'use a non-standard version of react-scripts')
73+
.allowUnknownOption()
7274
.on('--help', function () {
7375
console.log(' Only ' + chalk.green('<project-directory>') + ' is required.');
7476
console.log();
@@ -82,7 +84,7 @@ var program = require('commander')
8284
console.log(' ' + chalk.cyan('https://github.com/facebookincubator/create-react-app/issues/new'));
8385
console.log();
8486
})
85-
.parse(process.argv)
87+
.parse(process.argv);
8688

8789
if (typeof projectName === 'undefined') {
8890
console.error('Please specify the project directory:');
@@ -95,9 +97,14 @@ if (typeof projectName === 'undefined') {
9597
process.exit(1);
9698
}
9799

98-
createApp(projectName, program.verbose, program.scriptsVersion);
100+
var hiddenProgram = new commander.Command()
101+
.option('--internal-testing-template <path-to-template>', '(internal usage only, DO NOT RELY ON THIS) ' +
102+
'use a non-standard application template')
103+
.parse(process.argv)
104+
105+
createApp(projectName, program.verbose, program.scriptsVersion, hiddenProgram.internalTestingTemplate);
99106

100-
function createApp(name, verbose, version) {
107+
function createApp(name, verbose, version, template) {
101108
var root = path.resolve(name);
102109
var appName = path.basename(root);
103110

@@ -130,7 +137,7 @@ function createApp(name, verbose, version) {
130137
console.log('Installing ' + chalk.cyan('react-scripts') + '...');
131138
console.log();
132139

133-
run(root, appName, version, verbose, originalDirectory);
140+
run(root, appName, version, verbose, originalDirectory, template);
134141
}
135142

136143
function shouldUseYarn() {
@@ -163,7 +170,7 @@ function install(packageToInstall, verbose, callback) {
163170
});
164171
}
165172

166-
function run(root, appName, version, verbose, originalDirectory) {
173+
function run(root, appName, version, verbose, originalDirectory, template) {
167174
var packageToInstall = getInstallPackage(version);
168175
var packageName = getPackageName(packageToInstall);
169176

@@ -183,7 +190,7 @@ function run(root, appName, version, verbose, originalDirectory) {
183190
'init.js'
184191
);
185192
var init = require(scriptsPath);
186-
init(root, appName, verbose, originalDirectory);
193+
init(root, appName, verbose, originalDirectory, template);
187194
});
188195
}
189196

packages/react-scripts/.npmignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/fixtures
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["latest"]
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
REACT_APP_FILE_ENV_MESSAGE=fromtheenvfile
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+
}
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+
})
Binary file not shown.

0 commit comments

Comments
 (0)