Skip to content

Commit

Permalink
Use a more sophisticated template for end-to-end testing. (facebook#1187
Browse files Browse the repository at this point in the history
)

* 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
  • Loading branch information
EnoahNetzach authored and randycoulman committed May 8, 2017
1 parent 6bb2c63 commit bc2fc25
Show file tree
Hide file tree
Showing 78 changed files with 1,653 additions and 74 deletions.
20 changes: 16 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
---
language: node_js
node_js:
- 0.10
- 4
- 6
cache:
directories:
- node_modules
- packages/create-react-app/node_modules
- packages/react-scripts/node_modules
script: tasks/e2e.sh
script:
- 'if [ $TEST_SUITE = "simple" ]; then tasks/e2e-simple.sh; fi'
- 'if [ $TEST_SUITE = "installs" ]; then tasks/e2e-installs.sh; fi'
- 'if [ $TEST_SUITE = "kitchensink" ]; then tasks/e2e-kitchensink.sh; fi'
env:
- USE_YARN=no
- USE_YARN=yes
global:
- USE_YARN=no
matrix:
- TEST_SUITE=simple
- TEST_SUITE=installs
- TEST_SUITE=kitchensink
matrix:
include:
- node_js: 0.10
env: TEST_SUITE=simple
- node_js: 6
env: USE_YARN=yes TEST_SUITE=simple
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"build": "node packages/react-scripts/scripts/build.js",
"changelog": "lerna-changelog",
"create-react-app": "tasks/cra.sh",
"e2e": "tasks/e2e.sh",
"e2e": "tasks/e2e-simple.sh",
"postinstall": "lerna bootstrap",
"publish": "tasks/release.sh",
"start": "node packages/react-scripts/scripts/start.js",
Expand Down
21 changes: 14 additions & 7 deletions packages/create-react-app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ if (currentNodeVersion.split('.')[0] < 4) {
process.exit(1);
}

var commander = require('commander');
var fs = require('fs-extra');
var path = require('path');
var execSync = require('child_process').execSync;
Expand All @@ -60,7 +61,7 @@ var semver = require('semver');

var projectName;

var program = require('commander')
var program = commander
.version(require('./package.json').version)
.arguments('<project-directory>')
.usage(chalk.green('<project-directory>') + ' [options]')
Expand All @@ -69,6 +70,7 @@ var program = require('commander')
})
.option('--verbose', 'print additional logs')
.option('--scripts-version <alternative-package>', 'use a non-standard version of react-scripts')
.allowUnknownOption()
.on('--help', function () {
console.log(' Only ' + chalk.green('<project-directory>') + ' is required.');
console.log();
Expand All @@ -82,7 +84,7 @@ var program = require('commander')
console.log(' ' + chalk.cyan('https://github.com/facebookincubator/create-react-app/issues/new'));
console.log();
})
.parse(process.argv)
.parse(process.argv);

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

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

createApp(projectName, program.verbose, program.scriptsVersion, hiddenProgram.internalTestingTemplate);

function createApp(name, verbose, version) {
function createApp(name, verbose, version, template) {
var root = path.resolve(name);
var appName = path.basename(root);

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

run(root, appName, version, verbose, originalDirectory);
run(root, appName, version, verbose, originalDirectory, template);
}

function shouldUseYarn() {
Expand Down Expand Up @@ -163,7 +170,7 @@ function install(packageToInstall, verbose, callback) {
});
}

function run(root, appName, version, verbose, originalDirectory) {
function run(root, appName, version, verbose, originalDirectory, template) {
var packageToInstall = getInstallPackage(version);
var packageName = getPackageName(packageToInstall);

Expand All @@ -183,7 +190,7 @@ function run(root, appName, version, verbose, originalDirectory) {
'init.js'
);
var init = require(scriptsPath);
init(root, appName, verbose, originalDirectory);
init(root, appName, verbose, originalDirectory, template);
});
}

Expand Down
1 change: 1 addition & 0 deletions packages/react-scripts/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/fixtures
3 changes: 3 additions & 0 deletions packages/react-scripts/fixtures/kitchensink/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["latest"]
}
1 change: 1 addition & 0 deletions packages/react-scripts/fixtures/kitchensink/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
REACT_APP_FILE_ENV_MESSAGE=fromtheenvfile
8 changes: 8 additions & 0 deletions packages/react-scripts/fixtures/kitchensink/.flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[ignore]
<PROJECT_ROOT>/node_modules/fbjs/.*

[include]

[libs]

[options]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"dependencies": {
"babel-preset-latest": "6.16.0",
"babel-register": "6.18.0",
"babel-polyfill": "6.20.0",
"chai": "3.5.0",
"jsdom": "9.8.3",
"mocha": "3.2.0"
}
}
15 changes: 15 additions & 0 deletions packages/react-scripts/fixtures/kitchensink/gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# dependencies
node_modules

# testing
coverage

# production
build

# misc
.DS_Store
.env
npm-debug.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect } from 'chai'
import initDOM from './initDOM'

describe('Integration', () => {
describe('Environment variables', () => {
it('NODE_PATH', async () => {
const doc = await initDOM('node-path')

expect(doc.getElementById('feature-node-path').childElementCount).to.equal(4)
})

it('shell env variables', async () => {
const doc = await initDOM('shell-env-variables')

expect(doc.getElementById('feature-shell-env-variables').textContent).to.equal('fromtheshell.')
})

it('file env variables', async () => {
const doc = await initDOM('file-env-variables')

expect(doc.getElementById('feature-file-env-variables').textContent).to.equal('fromtheenvfile.')
})
})
})
62 changes: 62 additions & 0 deletions packages/react-scripts/fixtures/kitchensink/integration/initDOM.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const fs = require('fs')
const http = require('http')
const jsdom = require('jsdom')
const path = require('path')

let getMarkup
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) {
const file = path.isAbsolute(process.env.E2E_FILE)
? process.env.E2E_FILE
: path.join(process.cwd(), process.env.E2E_FILE)

const markup = fs.readFileSync(file, 'utf8')
getMarkup = () => markup

resourceLoader = (resource, callback) => callback(
null,
fs.readFileSync(path.join(path.dirname(file), resource.url.pathname), 'utf8')
)

timeToWaitForJsToExecute = 0
} else if (process.env.E2E_URL) {
getMarkup = () => new Promise(resolve => {
http.get(process.env.E2E_URL, (res) => {
let rawData = ''
res.on('data', chunk => rawData += chunk)
res.on('end', () => resolve(rawData))
})
})

resourceLoader = (resource, callback) => {
return resource.defaultFetch(callback)
}

timeToWaitForJsToExecute = 100
} else {
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()
})
}

export default feature => new Promise(async resolve => {
const markup = await getMarkup()
const host = process.env.E2E_URL || 'http://localhost:3000'
const doc = jsdom.jsdom(markup, {
features : {
FetchExternalResources : ['script', 'css'],
ProcessExternalResources : ['script'],
},
resourceLoader,
url: `${host}#${feature}`,
virtualConsole: jsdom.createVirtualConsole().sendTo(console),
})

doc.defaultView.addEventListener('load', () => {
setTimeout(() => resolve(doc), timeToWaitForJsToExecute)
}, false)
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { expect } from 'chai'
import initDOM from './initDOM'

describe('Integration', () => {
describe('Language syntax', () => {
it('array destructuring', async () => {
const doc = await initDOM('array-destructuring')

expect(doc.getElementById('feature-array-destructuring').childElementCount).to.equal(4)
})

it('array spread', async () => {
const doc = await initDOM('array-spread')

expect(doc.getElementById('feature-array-spread').childElementCount).to.equal(4)
})

it('async/await', async () => {
const doc = await initDOM('async-await')

expect(doc.getElementById('feature-async-await').childElementCount).to.equal(4)
})

it('class properties', async () => {
const doc = await initDOM('class-properties')

expect(doc.getElementById('feature-class-properties').childElementCount).to.equal(4)
})

it('computed properties', async () => {
const doc = await initDOM('computed-properties')

expect(doc.getElementById('feature-computed-properties').childElementCount).to.equal(4)
})

it('custom interpolation', async () => {
const doc = await initDOM('custom-interpolation')

expect(doc.getElementById('feature-custom-interpolation').childElementCount).to.equal(4)
})

it('default parameters', async () => {
const doc = await initDOM('default-parameters')

expect(doc.getElementById('feature-default-parameters').childElementCount).to.equal(4)
})

it('destructuring and await', async () => {
const doc = await initDOM('destructuring-and-await')

expect(doc.getElementById('feature-destructuring-and-await').childElementCount).to.equal(4)
})

it('generators', async () => {
const doc = await initDOM('generators')

expect(doc.getElementById('feature-generators').childElementCount).to.equal(4)
})

it('object destructuring', async () => {
const doc = await initDOM('object-destructuring')

expect(doc.getElementById('feature-object-destructuring').childElementCount).to.equal(4)
})

it('object spread', async () => {
const doc = await initDOM('object-spread')

expect(doc.getElementById('feature-object-spread').childElementCount).to.equal(4)
})

it('promises', async () => {
const doc = await initDOM('promises')

expect(doc.getElementById('feature-promises').childElementCount).to.equal(4)
})

it('rest + default', async () => {
const doc = await initDOM('rest-and-default')

expect(doc.getElementById('feature-rest-and-default').childElementCount).to.equal(4)
})

it('rest parameters', async () => {
const doc = await initDOM('rest-parameters')

expect(doc.getElementById('feature-rest-parameters').childElementCount).to.equal(4)
})

it('template interpolation', async () => {
const doc = await initDOM('template-interpolation')

expect(doc.getElementById('feature-template-interpolation').childElementCount).to.equal(4)
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { expect } from 'chai'
import initDOM from './initDOM'

describe('Integration', () => {
describe('Webpack plugins', () => {
it('css inclusion', async () => {
const doc = await initDOM('css-inclusion')

expect(doc.getElementsByTagName('style')[0].textContent.replace(/\s/g, ''))
.to.match(/#feature-css-inclusion\{background:.+;color:.+}/)
})

it('image inclusion', async () => {
const doc = await initDOM('image-inclusion')

expect(doc.getElementById('feature-image-inclusion').src).to.match(/^data:image\/jpeg;base64.+==$/)
})

it('no ext inclusion', async () => {
const doc = await initDOM('no-ext-inclusion')

expect(doc.getElementById('feature-no-ext-inclusion').textContent)
.to.equal('This is just a file without an extension.')
})

it('json inclusion', async () => {
const doc = await initDOM('json-inclusion')

expect(doc.getElementById('feature-json-inclusion').textContent).to.equal('This is an abstract.')
})

it('svg inclusion', async () => {
const doc = await initDOM('svg-inclusion')

expect(doc.getElementById('feature-svg-inclusion').src).to.match(/\/static\/media\/logo\..+\.svg$/)
})

it('unknown ext inclusion', async () => {
const doc = await initDOM('unknown-ext-inclusion')

expect(doc.getElementById('feature-unknown-ext-inclusion').textContent).to.equal('Whoooo, spooky!.')
})
})
})
Binary file not shown.
Loading

0 comments on commit bc2fc25

Please sign in to comment.