Skip to content

Commit

Permalink
ReactClassEquivalence
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronabramov committed May 20, 2016
1 parent 530ad40 commit 4c8a46c
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 210 deletions.
7 changes: 6 additions & 1 deletion grunt/tasks/jest.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@ function run(done, configPath) {
grunt.util.spawn({
cmd: 'node',
args: args,
opts: { stdio: 'inherit', env: { NODE_ENV: 'test' } },
opts: {
stdio: 'inherit',
env: Object.assign({}, process.env, {
NODE_ENV: 'test',
}),
},
}, function(spawnErr, result, code) {
if (spawnErr) {
onError(spawnErr);
Expand Down
62 changes: 50 additions & 12 deletions src/isomorphic/modern/class/__tests__/ReactClassEquivalence-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,62 @@

'use strict';

var MetaMatchers = require('MetaMatchers');
var spawnSync = require('child_process').spawnSync;
var path = require('path');

describe('ReactClassEquivalence', function() {

beforeEach(function() {
this.addMatchers(MetaMatchers);
});

var es6 = () => require('./ReactES6Class-test.js');
var coffee = () => require('./ReactCoffeeScriptClass-test.coffee');
var ts = () => require('./ReactTypeScriptClass-test.ts');

it('tests the same thing for es6 classes and CoffeeScript', function() {
expect(coffee).toEqualSpecsIn(es6);
var result1 = runJest('ReactCoffeeScriptClass-test.coffee');
var result2 = runJest('ReactES6Class-test.js');
compareResults(result1, result2);
});

it('tests the same thing for es6 classes and TypeScript', function() {
expect(ts).toEqualSpecsIn(es6);
var result1 = runJest('ReactTypeScriptClass-test.ts');
var result2 = runJest('ReactES6Class-test.js');
compareResults(result1, result2);
});

});

function runJest(testFile) {
var cwd = process.cwd();
var jestBin = path.resolve('node_modules', '.bin', 'jest');
var setupFile = path.resolve(__dirname, 'setupSpecEquivalenceReporter.js');
var result = spawnSync('node', [
jestBin,
testFile,
'--setupTestFrameworkScriptFile',
setupFile,
], {cwd});

if (result.error) {
throw result.error;
}

if (result.status !== 0) {
throw new Error(
'jest process exited with: ' +
result.status +
'\n' +
'stdout: ' +
result.stdout.toString() +
'stderr: ' +
result.stderr.toString()
);
}

return result.stdout.toString();
}

function compareResults(a, b) {
var regexp = /^EQUIVALENCE.*$/gm;
var aSpecs = (a.match(regexp) || []).sort().join('\n');
var bSpecs = (b.match(regexp) || []).sort().join('\n');

if (aSpecs.length === 0 && bSpecs.length === 0) {
throw new Error('No spec results found in the output');
}

expect(aSpecs).toEqual(bSpecs);
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ describe 'ReactCoffeeScriptClass', ->
).toThrow()
expect(console.error.calls.count()).toBe(1)
expect(console.error.calls.argsFor(0)[0]).toContain('No `render` method found on the returned component instance')
undefined

it 'renders a simple stateless component with prop', ->
class Foo extends React.Component
Expand All @@ -62,6 +63,7 @@ describe 'ReactCoffeeScriptClass', ->

test React.createElement(Foo, bar: 'foo'), 'DIV', 'foo'
test React.createElement(Foo, bar: 'bar'), 'DIV', 'bar'
undefined

it 'renders based on state using initial values in this.props', ->
class Foo extends React.Component
Expand All @@ -74,6 +76,7 @@ describe 'ReactCoffeeScriptClass', ->
className: @state.bar

test React.createElement(Foo, initialValue: 'foo'), 'SPAN', 'foo'
undefined

it 'renders based on state using props in the constructor', ->
class Foo extends React.Component
Expand All @@ -94,6 +97,7 @@ describe 'ReactCoffeeScriptClass', ->
instance = test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
instance.changeState()
test React.createElement(Foo), 'SPAN', 'bar'
undefined

it 'renders based on context in the constructor', ->
class Foo extends React.Component
Expand Down Expand Up @@ -125,6 +129,7 @@ describe 'ReactCoffeeScriptClass', ->
React.createElement Foo

test React.createElement(Outer), 'SPAN', 'foo'
undefined

it 'renders only once when setting state in componentWillMount', ->
renderCount = 0
Expand All @@ -141,6 +146,7 @@ describe 'ReactCoffeeScriptClass', ->

test React.createElement(Foo, initialValue: 'foo'), 'SPAN', 'bar'
expect(renderCount).toBe 1
undefined

it 'should throw with non-object in the initial state property', ->
[['an array'], 'a string', 1234].forEach (state) ->
Expand All @@ -156,6 +162,7 @@ describe 'ReactCoffeeScriptClass', ->
).toThrowError(
'Foo.state: must be set to an object or null'
)
undefined

it 'should render with null in the initial state property', ->
class Foo extends React.Component
Expand All @@ -166,6 +173,7 @@ describe 'ReactCoffeeScriptClass', ->
span()

test React.createElement(Foo), 'SPAN', ''
undefined

it 'setState through an event handler', ->
class Foo extends React.Component
Expand All @@ -183,6 +191,7 @@ describe 'ReactCoffeeScriptClass', ->
test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
attachedListener()
expect(renderedName).toBe 'bar'
undefined

it 'should not implicitly bind event handlers', ->
class Foo extends React.Component
Expand All @@ -199,6 +208,7 @@ describe 'ReactCoffeeScriptClass', ->

test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
expect(attachedListener).toThrow()
undefined

it 'renders using forceUpdate even when there is no state', ->
class Foo extends React.Component
Expand All @@ -217,6 +227,7 @@ describe 'ReactCoffeeScriptClass', ->
test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
attachedListener()
expect(renderedName).toBe 'bar'
undefined

it 'will call all the normal life cycle methods', ->
lifeCycles = []
Expand Down Expand Up @@ -266,6 +277,7 @@ describe 'ReactCoffeeScriptClass', ->
lifeCycles = [] # reset
ReactDOM.unmountComponentAtNode container
expect(lifeCycles).toEqual ['will-unmount']
undefined

it 'warns when classic properties are defined on the instance,
but does not invoke them.', ->
Expand Down Expand Up @@ -305,6 +317,7 @@ describe 'ReactCoffeeScriptClass', ->
expect(console.error.calls.argsFor(3)[0]).toContain(
'contextTypes was defined as an instance property on Foo.'
)
undefined

it 'should warn when misspelling shouldComponentUpdate', ->
spyOn console, 'error'
Expand All @@ -323,6 +336,7 @@ describe 'ReactCoffeeScriptClass', ->
Did you mean shouldComponentUpdate()? The name is phrased as a
question because the function is expected to return a value.'
)
undefined

it 'should warn when misspelling componentWillReceiveProps', ->
spyOn console, 'error'
Expand All @@ -340,6 +354,7 @@ describe 'ReactCoffeeScriptClass', ->
'Warning: NamedComponent has a method called componentWillRecieveProps().
Did you mean componentWillReceiveProps()?'
)
undefined

it 'should throw AND warn when trying to access classic APIs', ->
spyOn console, 'error'
Expand All @@ -356,6 +371,7 @@ describe 'ReactCoffeeScriptClass', ->
expect(console.error.calls.argsFor(1)[0]).toContain(
'isMounted(...) is deprecated in plain JavaScript React classes'
)
undefined

it 'supports this.context passed via getChildContext', ->
class Bar extends React.Component
Expand All @@ -373,6 +389,7 @@ describe 'ReactCoffeeScriptClass', ->
React.createElement Bar

test React.createElement(Foo), 'DIV', 'bar-through-context'
undefined

it 'supports classic refs', ->
class Foo extends React.Component
Expand All @@ -383,8 +400,10 @@ describe 'ReactCoffeeScriptClass', ->

instance = test(React.createElement(Foo), 'DIV', 'foo')
expect(instance.refs.inner.getName()).toBe 'foo'
undefined

it 'supports drilling through to the DOM using findDOMNode', ->
instance = test Inner(name: 'foo'), 'DIV', 'foo'
node = ReactDOM.findDOMNode(instance)
expect(node).toBe container.firstChild
undefined
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*!
* Copyright 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

'use strict';

var expect = global.expect;

var numExpectations = 0;

global.expect = function() {
numExpectations += 1;
return expect.apply(this, arguments);
};

beforeEach(() => numExpectations = 0);

jasmine.currentEnv_.addReporter({
specDone: (spec) => {
console.log(
`EQUIVALENCE: ${spec.description}, ` +
`status: ${spec.status}, ` +
`numExpectations: ${numExpectations}`
);
},
});
Loading

0 comments on commit 4c8a46c

Please sign in to comment.