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
-});