@samitouri / QOS-React-2 / commits / 5d332f22b4

ReactClassEquivalence

Dmitrii Abramov committed May 13, 2016 at 23:07 UTC 5d332f22b4e5df466d1d41828242b663021c3de6
7 files changed +108 -212
grunt/tasks/jest.js
+6 -1
@@ -83,7 +83,12 @@ function run(done, configPath) {
83 grunt.util.spawn({
84 cmd: 'node',
85 args: args,
86 - opts: { stdio: 'inherit', env: { NODE_ENV: 'test' } },
86 + opts: {
87 + stdio: 'inherit',
88 + env: Object.assign({}, process.env, {
89 + NODE_ENV: 'test',
90 + }),
91 + },
92 }, function(spawnErr, result, code) {
93 if (spawnErr) {
94 onError(spawnErr);
src/isomorphic/modern/class/__tests__/ReactClassEquivalence-test.js
+50 -12
@@ -11,24 +11,62 @@
11
12 'use strict';
13
14 -var MetaMatchers = require('MetaMatchers');
14 +var spawnSync = require('child_process').spawnSync;
15 +var path = require('path');
16
17 describe('ReactClassEquivalence', function() {
17 -
18 - beforeEach(function() {
19 - this.addMatchers(MetaMatchers);
20 - });
21 -
22 - var es6 = () => require('./ReactES6Class-test.js');
23 - var coffee = () => require('./ReactCoffeeScriptClass-test.coffee');
24 - var ts = () => require('./ReactTypeScriptClass-test.ts');
25 -
18 it('tests the same thing for es6 classes and CoffeeScript', function() {
27 - expect(coffee).toEqualSpecsIn(es6);
19 + var result1 = runJest('ReactCoffeeScriptClass-test.coffee');
20 + var result2 = runJest('ReactES6Class-test.js');
21 + compareResults(result1, result2);
22 });
23
24 it('tests the same thing for es6 classes and TypeScript', function() {
31 - expect(ts).toEqualSpecsIn(es6);
25 + var result1 = runJest('ReactTypeScriptClass-test.ts');
26 + var result2 = runJest('ReactES6Class-test.js');
27 + compareResults(result1, result2);
28 });
29
30 });
31 +
32 +function runJest(testFile) {
33 + var cwd = process.cwd();
34 + var jestBin = path.resolve('node_modules', '.bin', 'jest');
35 + var setupFile = path.resolve(__dirname, 'setupSpecEquivalenceReporter.js');
36 + var result = spawnSync('node', [
37 + jestBin,
38 + testFile,
39 + '--setupTestFrameworkScriptFile',
40 + setupFile,
41 + ], {cwd});
42 +
43 + if (result.error) {
44 + throw result.error;
45 + }
46 +
47 + if (result.status !== 0) {
48 + throw new Error(
49 + 'jest process exited with: ' +
50 + result.status +
51 + '\n' +
52 + 'stdout: ' +
53 + result.stdout.toString() +
54 + 'stderr: ' +
55 + result.stderr.toString()
56 + );
57 + }
58 +
59 + return result.stdout.toString();
60 +}
61 +
62 +function compareResults(a, b) {
63 + var regexp = /^EQUIVALENCE.*$/gm;
64 + var aSpecs = (a.match(regexp) || []).sort().join('\n');
65 + var bSpecs = (b.match(regexp) || []).sort().join('\n');
66 +
67 + if (aSpecs.length === 0 && bSpecs.length === 0) {
68 + throw new Error('No spec results found in the output');
69 + }
70 +
71 + expect(aSpecs).toEqual(bSpecs);
72 +}
src/isomorphic/modern/class/__tests__/ReactCoffeeScriptClass-test.coffee
+19
@@ -53,6 +53,7 @@ describe 'ReactCoffeeScriptClass', ->
53 ).toThrow()
54 expect(console.error.calls.count()).toBe(1)
55 expect(console.error.calls.argsFor(0)[0]).toContain('No `render` method found on the returned component instance')
56 + undefined
57
58 it 'renders a simple stateless component with prop', ->
59 class Foo extends React.Component
@@ -62,6 +63,7 @@ describe 'ReactCoffeeScriptClass', ->
63
64 test React.createElement(Foo, bar: 'foo'), 'DIV', 'foo'
65 test React.createElement(Foo, bar: 'bar'), 'DIV', 'bar'
66 + undefined
67
68 it 'renders based on state using initial values in this.props', ->
69 class Foo extends React.Component
@@ -74,6 +76,7 @@ describe 'ReactCoffeeScriptClass', ->
76 className: @state.bar
77
78 test React.createElement(Foo, initialValue: 'foo'), 'SPAN', 'foo'
79 + undefined
80
81 it 'renders based on state using props in the constructor', ->
82 class Foo extends React.Component
@@ -94,6 +97,7 @@ describe 'ReactCoffeeScriptClass', ->
97 instance = test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
98 instance.changeState()
99 test React.createElement(Foo), 'SPAN', 'bar'
100 + undefined
101
102 it 'renders based on context in the constructor', ->
103 class Foo extends React.Component
@@ -125,6 +129,7 @@ describe 'ReactCoffeeScriptClass', ->
129 React.createElement Foo
130
131 test React.createElement(Outer), 'SPAN', 'foo'
132 + undefined
133
134 it 'renders only once when setting state in componentWillMount', ->
135 renderCount = 0
@@ -141,6 +146,7 @@ describe 'ReactCoffeeScriptClass', ->
146
147 test React.createElement(Foo, initialValue: 'foo'), 'SPAN', 'bar'
148 expect(renderCount).toBe 1
149 + undefined
150
151 it 'should throw with non-object in the initial state property', ->
152 [['an array'], 'a string', 1234].forEach (state) ->
@@ -156,6 +162,7 @@ describe 'ReactCoffeeScriptClass', ->
162 ).toThrowError(
163 'Foo.state: must be set to an object or null'
164 )
165 + undefined
166
167 it 'should render with null in the initial state property', ->
168 class Foo extends React.Component
@@ -166,6 +173,7 @@ describe 'ReactCoffeeScriptClass', ->
173 span()
174
175 test React.createElement(Foo), 'SPAN', ''
176 + undefined
177
178 it 'setState through an event handler', ->
179 class Foo extends React.Component
@@ -183,6 +191,7 @@ describe 'ReactCoffeeScriptClass', ->
191 test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
192 attachedListener()
193 expect(renderedName).toBe 'bar'
194 + undefined
195
196 it 'should not implicitly bind event handlers', ->
197 class Foo extends React.Component
@@ -199,6 +208,7 @@ describe 'ReactCoffeeScriptClass', ->
208
209 test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
210 expect(attachedListener).toThrow()
211 + undefined
212
213 it 'renders using forceUpdate even when there is no state', ->
214 class Foo extends React.Component
@@ -217,6 +227,7 @@ describe 'ReactCoffeeScriptClass', ->
227 test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
228 attachedListener()
229 expect(renderedName).toBe 'bar'
230 + undefined
231
232 it 'will call all the normal life cycle methods', ->
233 lifeCycles = []
@@ -266,6 +277,7 @@ describe 'ReactCoffeeScriptClass', ->
277 lifeCycles = [] # reset
278 ReactDOM.unmountComponentAtNode container
279 expect(lifeCycles).toEqual ['will-unmount']
280 + undefined
281
282 it 'warns when classic properties are defined on the instance,
283 but does not invoke them.', ->
@@ -305,6 +317,7 @@ describe 'ReactCoffeeScriptClass', ->
317 expect(console.error.calls.argsFor(3)[0]).toContain(
318 'contextTypes was defined as an instance property on Foo.'
319 )
320 + undefined
321
322 it 'should warn when misspelling shouldComponentUpdate', ->
323 spyOn console, 'error'
@@ -323,6 +336,7 @@ describe 'ReactCoffeeScriptClass', ->
336 Did you mean shouldComponentUpdate()? The name is phrased as a
337 question because the function is expected to return a value.'
338 )
339 + undefined
340
341 it 'should warn when misspelling componentWillReceiveProps', ->
342 spyOn console, 'error'
@@ -340,6 +354,7 @@ describe 'ReactCoffeeScriptClass', ->
354 'Warning: NamedComponent has a method called componentWillRecieveProps().
355 Did you mean componentWillReceiveProps()?'
356 )
357 + undefined
358
359 it 'should throw AND warn when trying to access classic APIs', ->
360 spyOn console, 'error'
@@ -356,6 +371,7 @@ describe 'ReactCoffeeScriptClass', ->
371 expect(console.error.calls.argsFor(1)[0]).toContain(
372 'isMounted(...) is deprecated in plain JavaScript React classes'
373 )
374 + undefined
375
376 it 'supports this.context passed via getChildContext', ->
377 class Bar extends React.Component
@@ -373,6 +389,7 @@ describe 'ReactCoffeeScriptClass', ->
389 React.createElement Bar
390
391 test React.createElement(Foo), 'DIV', 'bar-through-context'
392 + undefined
393
394 it 'supports classic refs', ->
395 class Foo extends React.Component
@@ -383,8 +400,10 @@ describe 'ReactCoffeeScriptClass', ->
400
401 instance = test(React.createElement(Foo), 'DIV', 'foo')
402 expect(instance.refs.inner.getName()).toBe 'foo'
403 + undefined
404
405 it 'supports drilling through to the DOM using findDOMNode', ->
406 instance = test Inner(name: 'foo'), 'DIV', 'foo'
407 node = ReactDOM.findDOMNode(instance)
408 expect(node).toBe container.firstChild
409 + undefined
src/isomorphic/modern/class/__tests__/setupSpecEquivalenceReporter.js new
+31
@@ -0,0 +1,31 @@
1 +/*!
2 + * Copyright 2015-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + */
9 +
10 +'use strict';
11 +
12 +var expect = global.expect;
13 +
14 +var numExpectations = 0;
15 +
16 +global.expect = function() {
17 + numExpectations += 1;
18 + return expect.apply(this, arguments);
19 +};
20 +
21 +beforeEach(() => numExpectations = 0);
22 +
23 +jasmine.currentEnv_.addReporter({
24 + specDone: (spec) => {
25 + console.log(
26 + `EQUIVALENCE: ${spec.description}, ` +
27 + `status: ${spec.status}, ` +
28 + `numExpectations: ${numExpectations}`
29 + );
30 + },
31 +});
src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
+2 -2
@@ -1324,7 +1324,7 @@ describe('ReactDOMComponent', function() {
1324 it('should warn about incorrect casing on properties', function() {
1325 spyOn(console, 'error');
1326 ReactTestUtils.renderIntoDocument(React.createElement('input', {type: 'text', tabindex: '1'}));
1327 - expect(console.error.calls.count(0)).toBe(1);
1327 + expect(console.error.calls.count()).toBe(1);
1328 expect(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
1329 });
1330
@@ -1332,7 +1332,7 @@ describe('ReactDOMComponent', function() {
1332 spyOn(console, 'error');
1333 ReactTestUtils.renderIntoDocument(React.createElement('input', {type: 'text', onclick: '1'}));
1334 ReactTestUtils.renderIntoDocument(React.createElement('input', {type: 'text', onKeydown: '1'}));
1335 - expect(console.error.calls.count(0)).toBe(2);
1335 + expect(console.error.calls.count()).toBe(2);
1336 expect(console.error.calls.argsFor(0)[0]).toContain('onClick');
1337 expect(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
1338 });
src/test/MetaMatchers.js deleted
-135
@@ -1,135 +0,0 @@
1 -/**
2 - * Copyright 2015-present, Facebook, Inc.
3 - * All rights reserved.
4 - *
5 - * This source code is licensed under the BSD-style license found in the
6 - * LICENSE file in the root directory of this source tree. An additional grant
7 - * of patent rights can be found in the PATENTS file in the same directory.
8 - *
9 - * @providesModule MetaMatchers
10 - */
11 -
12 -'use strict';
13 -
14 -/**
15 - * This modules adds a jasmine matcher toEqualSpecsIn that can be used to
16 - * compare the specs in two different "describe" functions and their result.
17 - * It can be used to test a test.
18 - */
19 -
20 -function getRunnerWithResults(describeFunction) {
21 - if (describeFunction._cachedRunner) {
22 - // Cached result of execution. This is a convenience way to test against
23 - // the same authoritative function multiple times.
24 - return describeFunction._cachedRunner;
25 - }
26 - // Patch the current global environment.
27 - var env = new jasmine.Env();
28 - // Execute the tests synchronously.
29 - env.updateInterval = 0;
30 - var outerGetEnv = jasmine.getEnv;
31 - jasmine.getEnv = function() {
32 - return env;
33 - };
34 - // TODO: Bring over matchers from the existing environment.
35 - console.error(env);
36 - var runner = env.currentRunner();
37 - try {
38 - env.describe('', describeFunction);
39 - env.execute();
40 - } finally {
41 - // Restore the environment.
42 - jasmine.getEnv = outerGetEnv;
43 - }
44 - describeFunction._cachedRunner = runner;
45 - return runner;
46 -}
47 -
48 -function compareSpec(actual, expected) {
49 - if (actual.results().totalCount !== expected.results().totalCount) {
50 - return (
51 - 'Expected ' + expected.results().totalCount + ' expects, ' +
52 - 'but got ' + actual.results().totalCount + ':' +
53 - actual.getFullName()
54 - );
55 - }
56 - return null;
57 -}
58 -
59 -function includesDescription(specs, description, startIndex) {
60 - for (var i = startIndex; i < specs.length; i++) {
61 - if (specs[i].description === description) {
62 - return true;
63 - }
64 - }
65 - return false;
66 -}
67 -
68 -function compareSpecs(actualSpecs, expectedSpecs) {
69 - for (var i = 0; i < actualSpecs.length && i < expectedSpecs.length; i++) {
70 - var actual = actualSpecs[i];
71 - var expected = expectedSpecs[i];
72 - if (actual.description === expected.description) {
73 - var errorMessage = compareSpec(actual, expected);
74 - if (errorMessage) {
75 - return errorMessage;
76 - }
77 - continue;
78 - } else if (includesDescription(actualSpecs, expected.description, i)) {
79 - return 'Did not expect the spec:' + actualSpecs[i].getFullName();
80 - } else {
81 - return 'Expected an equivalent to:' + expectedSpecs[i].getFullName();
82 - }
83 - }
84 - if (i < actualSpecs.length) {
85 - return 'Did not expect the spec:' + actualSpecs[i].getFullName();
86 - }
87 - if (i < expectedSpecs.length) {
88 - return 'Expected an equivalent to:' + expectedSpecs[i].getFullName();
89 - }
90 - return null;
91 -}
92 -
93 -function compareDescription(a, b) {
94 - if (a.description === b.description) {
95 - return 0;
96 - }
97 - return a.description < b.description ? -1 : 1;
98 -}
99 -
100 -function compareRunners(actual, expected) {
101 - return compareSpecs(
102 - actual.specs().sort(compareDescription),
103 - expected.specs().sort(compareDescription)
104 - );
105 -}
106 -
107 -var MetaMatchers = {
108 - toEqualSpecsIn(/* util, customEqualityMatcher*/) {
109 - return {
110 - compare(actualDescribeFunction, expectedDescribeFunction) {
111 - if (typeof actualDescribeFunction !== 'function') {
112 - throw Error('toEqualSpecsIn() should be used on a describe function');
113 - }
114 - if (typeof expectedDescribeFunction !== 'function') {
115 - throw Error('toEqualSpecsIn() should be passed a describe function');
116 - }
117 - var actual = getRunnerWithResults(actualDescribeFunction);
118 - var expected = getRunnerWithResults(expectedDescribeFunction);
119 - var errorMessage = compareRunners(actual, expected);
120 -
121 - return {
122 - passed: !errorMessage,
123 - message: function() {
124 - return [
125 - errorMessage,
126 - 'The specs are equal. Expected them to be different.',
127 - ];
128 - },
129 - };
130 - },
131 - };
132 - },
133 -};
134 -
135 -module.exports = MetaMatchers;
src/test/__tests__/MetaMatchers-test.js deleted
-62
@@ -1,62 +0,0 @@
1 -/**
2 - * Copyright 2015-present, Facebook, Inc.
3 - * All rights reserved.
4 - *
5 - * This source code is licensed under the BSD-style license found in the
6 - * LICENSE file in the root directory of this source tree. An additional grant
7 - * of patent rights can be found in the PATENTS file in the same directory.
8 - *
9 - * @emails react-core
10 - */
11 -
12 -'use strict';
13 -
14 -var MetaMatchers = require('MetaMatchers');
15 -
16 -describe('meta-matchers', function() {
17 -
18 - beforeEach(function() {
19 - jasmine.addMatchers(MetaMatchers);
20 - });
21 -
22 - function a() {
23 - it('should add 1 and 2', function() {
24 - expect(1 + 2).toBe(3);
25 - });
26 - }
27 -
28 - function b() {
29 - it('should add 1 and 2', function() {
30 - expect(1 + 2).toBe(3);
31 - });
32 - }
33 -
34 - function c() {
35 - it('should add 1 and 2', function() {
36 - expect(1 + 2).toBe(3);
37 - });
38 - it('should mutiply 1 and 2', function() {
39 - expect(1 * 2).toBe(2);
40 - });
41 - }
42 -
43 - function d() {
44 - it('should add 1 and 2', function() {
45 - expect(1 + 2).toBe(3);
46 - });
47 - it('should mutiply 1 and 2', function() {
48 - expect(1 * 2).toBe(2);
49 - expect(2 * 1).toBe(2);
50 - });
51 - }
52 -
53 - it('tests equality of specs', function() {
54 - expect(a).toEqualSpecsIn(b);
55 - });
56 -
57 - it('tests inequality of specs and expects', function() {
58 - expect(b).not.toEqualSpecsIn(c);
59 - expect(c).not.toEqualSpecsIn(d);
60 - });
61 -
62 -});