Open source jest tests for open source components

Reviewed By: bestander

Differential Revision: D3424368

fbshipit-source-id: 116229b64ecc7d8846e803e29fad377a4fb800bb
This commit is contained in:
Pieter De Baets
2016-06-13 08:23:23 -07:00
committed by Facebook Github Bot 6
parent 8afcaa36e6
commit 2151dfbb24
7 changed files with 483 additions and 49 deletions

View File

@@ -1,44 +1,65 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* Copyright (c) 2013-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';
jest.unmock('MessageQueue')
.unmock('fbjs/lib/keyMirror');
var MessageQueue = require('MessageQueue');
const MessageQueueTestConfig = require('./MessageQueueTestConfig');
jest.unmock('MessageQueue');
let MODULE_IDS = 0;
let METHOD_IDS = 1;
let PARAMS = 2;
let MessageQueue;
let MessageQueueTestModule1;
let MessageQueueTestModule2;
let queue;
let TestModule = {
testHook1(){}, testHook2(){},
};
const MODULE_IDS = 0;
const METHOD_IDS = 1;
const PARAMS = 2;
let assertQueue = (flushedQueue, index, moduleID, methodID, params) => {
const assertQueue = (flushedQueue, index, moduleID, methodID, params) => {
expect(flushedQueue[MODULE_IDS][index]).toEqual(moduleID);
expect(flushedQueue[METHOD_IDS][index]).toEqual(methodID);
expect(flushedQueue[PARAMS][index]).toEqual(params);
};
var queue;
describe('MessageQueue', () => {
beforeEach(() => {
// Important things to test:
//
// [x] Calling remote method on queue actually queues it up.
//
// [x] Both error and success callbacks are invoked.
//
// [x] When simulating an error callback from remote method, both error and
// success callbacks are cleaned up.
//
// [x] Local modules can be invoked through the queue.
//
// [ ] Local modules that throw exceptions are gracefully caught. In that case
// local callbacks stored by IDs are cleaned up.
//
// [ ] Remote invocation throws if not supplying an error callback.
describe('MessageQueue', function() {
beforeEach(function() {
jest.resetModuleRegistry();
MessageQueue = require('MessageQueue');
MessageQueueTestModule1 = require('./MessageQueueTestModule1');
MessageQueueTestModule2 = require('./MessageQueueTestModule2');
queue = new MessageQueue(
() => ({ remoteModuleConfig: remoteModulesConfig })
() => MessageQueueTestConfig
);
queue.registerCallableModule('one', TestModule);
TestModule.testHook1 = jasmine.createSpy();
TestModule.testHook2 = jasmine.createSpy();
queue.registerCallableModule(
'MessageQueueTestModule1',
MessageQueueTestModule1
);
queue.registerCallableModule(
'MessageQueueTestModule2',
MessageQueueTestModule2
);
});
it('should enqueue native calls', () => {
@@ -48,59 +69,129 @@ describe('MessageQueue', () => {
});
it('should call a local function with the function name', () => {
expect(TestModule.testHook2.calls.count()).toEqual(0);
queue.__callFunction('one', 'testHook2', [2]);
expect(TestModule.testHook2.calls.count()).toEqual(1);
MessageQueueTestModule1.testHook2 = jasmine.createSpy();
expect(MessageQueueTestModule1.testHook2.calls.count()).toEqual(0);
queue.__callFunction('MessageQueueTestModule1', 'testHook2', [2]);
expect(MessageQueueTestModule1.testHook2.calls.count()).toEqual(1);
});
it('should generate native modules', () => {
queue.RemoteModules.one.remoteMethod1('foo');
queue.RemoteModules.RemoteModule1.remoteMethod1('foo');
let flushedQueue = queue.flushedQueue();
assertQueue(flushedQueue, 0, 0, 0, ['foo']);
});
it('should store callbacks', () => {
queue.RemoteModules.one.remoteMethod2('foo', () => {}, () => {});
queue.RemoteModules.RemoteModule1.remoteMethod2('foo', () => {}, () => {});
let flushedQueue = queue.flushedQueue();
assertQueue(flushedQueue, 0, 0, 1, ['foo', 0, 1]);
});
it('should call the stored callback', () => {
var done = false;
queue.RemoteModules.one.remoteMethod1(() => { done = true; });
queue.RemoteModules.RemoteModule1.remoteMethod1(() => { done = true; });
queue.__invokeCallback(1);
expect(done).toEqual(true);
});
it('should throw when calling the same callback twice', () => {
queue.RemoteModules.one.remoteMethod1(() => {});
queue.RemoteModules.RemoteModule1.remoteMethod1(() => {});
queue.__invokeCallback(1);
expect(() => queue.__invokeCallback(1)).toThrow();
});
it('should throw when calling both success and failure callback', () => {
queue.RemoteModules.one.remoteMethod1(() => {}, () => {});
queue.RemoteModules.RemoteModule1.remoteMethod1(() => {}, () => {});
queue.__invokeCallback(1);
expect(() => queue.__invokeCallback(0)).toThrow();
});
it('should make round trip and clear memory', function() {
// Perform communication
// First we're going to call into this (overriden) test hook pretending to
// be a remote module making a "local" invocation into JS.
let onFail = jasmine.createSpy();
let onSucc = jasmine.createSpy();
MessageQueueTestModule1.testHook1 = function() {
// Then inside of this local module, we're going to fire off a remote
// request.
queue.__nativeCall(
0,
0,
Array.prototype.slice.apply(arguments),
onFail,
onSucc,
);
};
// The second test hook does the same thing as the first, but fires off a
// remote request to a different remote module/method.
MessageQueueTestModule1.testHook2 = function() {
queue.__nativeCall(
1,
1,
Array.prototype.slice.apply(arguments),
onFail,
onSucc,
);
};
/* MessageQueueTestModule1.testHook1 */
queue.__callFunction('MessageQueueTestModule1', 'testHook1', ['paloAlto', 'menloPark']);
/* MessageQueueTestModule1.testHook2 */
queue.__callFunction('MessageQueueTestModule1', 'testHook2', ['mac', 'windows']);
// And how do we know that it executed those local modules correctly? Well,
// these particular test method echo their arguments back to remote methods!
var resultingRemoteInvocations = queue.flushedQueue();
// As always, the message queue has five fields
expect(resultingRemoteInvocations.length).toBe(4);
expect(resultingRemoteInvocations[0].length).toBe(2);
expect(resultingRemoteInvocations[1].length).toBe(2);
expect(resultingRemoteInvocations[2].length).toBe(2);
expect(typeof resultingRemoteInvocations[3]).toEqual('number');
expect(resultingRemoteInvocations[0][0]).toBe(0); // `RemoteModule1`
expect(resultingRemoteInvocations[1][0]).toBe(0); // `remoteMethod1`
expect([ // the arguments
resultingRemoteInvocations[2][0][0],
resultingRemoteInvocations[2][0][1]
]).toEqual(['paloAlto', 'menloPark']);
// Callbacks ids are tacked onto the end of the remote arguments.
var firstFailCBID = resultingRemoteInvocations[2][0][2];
var firstSuccCBID = resultingRemoteInvocations[2][0][3];
expect(resultingRemoteInvocations[0][1]).toBe(1); // `RemoteModule2`
expect(resultingRemoteInvocations[1][1]).toBe(1); // `remoteMethod2`
expect([ // the arguments
resultingRemoteInvocations[2][1][0],
resultingRemoteInvocations[2][1][1]
]).toEqual(['mac', 'windows']);
var secondFailCBID = resultingRemoteInvocations[2][1][2];
var secondSuccCBID = resultingRemoteInvocations[2][1][3];
// Trigger init
queue.RemoteModules
// Handle the first remote invocation by signaling failure.
// -------------------------------------------------------
queue.__invokeCallback(firstFailCBID, ['firstFailure']);
// The failure callback was already invoked, the success is no longer valid
expect(function() {
queue.__invokeCallback(firstSuccCBID, ['firstSucc']);
}).toThrow();
expect(onFail.calls.count()).toBe(1);
expect(onSucc.calls.count()).toBe(0);
// Handle the second remote invocation by signaling success.
// -------------------------------------------------------
queue.__invokeCallback(secondSuccCBID, ['secondSucc']);
// The success callback was already invoked, the fail cb is no longer valid
expect(function() {
queue.__invokeCallback(secondFailCBID, ['secondFail']);
}).toThrow();
expect(onFail.calls.count()).toBe(1);
expect(onSucc.calls.count()).toBe(1);
});
});
var remoteModulesConfig = {
'one': {
'moduleID':0,
'methods': {
'remoteMethod1':{ 'type': 'remote', 'methodID': 0 },
'remoteMethod2':{ 'type': 'remote', 'methodID': 1 },
}
},
};
var localModulesConfig = {
'one': {
'moduleID': 0,
'methods': {
'testHook1':{ 'type': 'local', 'methodID': 0 },
'testHook2':{ 'type': 'local', 'methodID': 1 },
}
},
};

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2013-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.
*
* These don't actually exist anywhere in the code.
*/
'use strict';
var remoteModulesConfig = {
"RemoteModule1": {
"moduleID":0,
"methods":{
"remoteMethod1":{
"type":"remote",
"methodID":0
},
"remoteMethod2":{
"type":"remote",
"methodID":1
}
}
},
"RemoteModule2":{
"moduleID":1,
"methods":{
"remoteMethod1":{
"type":"remote",
"methodID":0
},
"remoteMethod2":{
"type":"remote",
"methodID":1
}
}
}
};
/**
* These actually exist in the __tests__ folder.
*/
var localModulesConfig = {
"MessageQueueTestModule1": {
"moduleID":"MessageQueueTestModule1",
"methods":{
"testHook1":{
"type":"local",
"methodID":"testHook1"
},
"testHook2":{
"type":"local",
"methodID":"testHook2"
}
}
},
"MessageQueueTestModule2": {
"moduleID":"MessageQueueTestModule2",
"methods": {
"runLocalCode":{
"type":"local",
"methodID":"runLocalCode"
},
"runLocalCode2":{
"type":"local",
"methodID":"runLocalCode2"
}
}
}
};
var MessageQueueTestConfig = {
localModuleConfig: localModulesConfig,
remoteModuleConfig: remoteModulesConfig,
};
module.exports = MessageQueueTestConfig;

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2013-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.
*
* @providesModule MessageQueueTestModule1
*/
'use strict';
/**
* Dummy module that only exists for the sake of proving that the message queue
* correctly dispatches to commonJS modules. The `testHook` is overriden by test
* cases.
*/
var MessageQueueTestModule1 = {
testHook1: function() {
},
testHook2: function() {
}
};
module.exports = MessageQueueTestModule1;

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2013-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.
*
* @providesModule MessageQueueTestModule2
*/
'use strict';
var MessageQueueTestModule2 = {
runLocalCode: function() {
},
runLocalCode2: function() {
}
};
module.exports = MessageQueueTestModule2;

View File

@@ -0,0 +1,121 @@
/**
* Copyright (c) 2013-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.
*
*/
jest.unmock('deepFreezeAndThrowOnMutationInDev');
var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
describe('deepFreezeAndThrowOnMutationInDev', function() {
it('should be a noop on non object values', () => {
__DEV__ = true;
expect(() => deepFreezeAndThrowOnMutationInDev('')).not.toThrow();
expect(() => deepFreezeAndThrowOnMutationInDev(null)).not.toThrow();
expect(() => deepFreezeAndThrowOnMutationInDev(false)).not.toThrow();
expect(() => deepFreezeAndThrowOnMutationInDev(5)).not.toThrow();
expect(() => deepFreezeAndThrowOnMutationInDev()).not.toThrow();
__DEV__ = false;
expect(() => deepFreezeAndThrowOnMutationInDev('')).not.toThrow();
expect(() => deepFreezeAndThrowOnMutationInDev(null)).not.toThrow();
expect(() => deepFreezeAndThrowOnMutationInDev(false)).not.toThrow();
expect(() => deepFreezeAndThrowOnMutationInDev(5)).not.toThrow();
expect(() => deepFreezeAndThrowOnMutationInDev()).not.toThrow();
});
it('should throw on mutation in dev with strict', () => {
'use strict';
__DEV__ = true;
var o = {key: 'oldValue'};
deepFreezeAndThrowOnMutationInDev(o);
expect(() => { o.key = 'newValue'; }).toThrowError(
'You attempted to set the key `key` with the value `"newValue"` ' +
'on an object that is meant to be immutable and has been frozen.'
);
expect(o.key).toBe('oldValue');
});
it('should throw on mutation in dev without strict', () => {
__DEV__ = true;
var o = {key: 'oldValue'};
deepFreezeAndThrowOnMutationInDev(o);
expect(() => { o.key = 'newValue'; }).toThrowError(
'You attempted to set the key `key` with the value `"newValue"` ' +
'on an object that is meant to be immutable and has been frozen.'
);
expect(o.key).toBe('oldValue');
});
it('should throw on nested mutation in dev with strict', () => {
'use strict';
__DEV__ = true;
var o = {key1: {key2: {key3: 'oldValue'}}};
deepFreezeAndThrowOnMutationInDev(o);
expect(() => { o.key1.key2.key3 = 'newValue'; }).toThrowError(
'You attempted to set the key `key3` with the value `"newValue"` ' +
'on an object that is meant to be immutable and has been frozen.'
);
expect(o.key1.key2.key3).toBe('oldValue');
});
it('should throw on nested mutation in dev without strict', () => {
__DEV__ = true;
var o = {key1: {key2: {key3: 'oldValue'}}};
deepFreezeAndThrowOnMutationInDev(o);
expect(() => { o.key1.key2.key3 = 'newValue'; }).toThrowError(
'You attempted to set the key `key3` with the value `"newValue"` ' +
'on an object that is meant to be immutable and has been frozen.'
);
expect(o.key1.key2.key3).toBe('oldValue');
});
it('should throw on insertion in dev with strict', () => {
'use strict';
__DEV__ = true;
var o = {oldKey: 'value'};
deepFreezeAndThrowOnMutationInDev(o);
expect(() => { o.newKey = 'value'; })
.toThrowError('Can\'t add property newKey, object is not extensible');
expect(o.newKey).toBe(undefined);
});
it('should not throw on insertion in dev without strict', () => {
__DEV__ = true;
var o = {oldKey: 'value'};
deepFreezeAndThrowOnMutationInDev(o);
expect(() => { o.newKey = 'value'; }).not.toThrow();
expect(o.newKey).toBe(undefined);
});
it('should mutate and not throw on mutation in prod', () => {
'use strict';
__DEV__ = false;
var o = {key: 'oldValue'};
deepFreezeAndThrowOnMutationInDev(o);
expect(() => { o.key = 'newValue'; }).not.toThrow();
expect(o.key).toBe('newValue');
});
// This is a limitation of the technique unfortunately
it('should not deep freeze already frozen objects', () => {
'use strict';
__DEV__ = true;
var o = {key1: {key2: 'oldValue'}};
Object.freeze(o);
deepFreezeAndThrowOnMutationInDev(o);
expect(() => { o.key1.key2 = 'newValue'; }).not.toThrow();
expect(o.key1.key2).toBe('newValue');
});
it("shouldn't recurse infinitely", () => {
__DEV__ = true;
var o = {};
o.circular = o;
deepFreezeAndThrowOnMutationInDev(o);
});
});

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2013-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';
jest.unmock('groupByEveryN');
describe('groupByEveryN', () => {
var groupByEveryN = require('groupByEveryN');
it ('should group by with different n', () => {
expect(groupByEveryN([1, 2, 3, 4, 5, 6, 7, 8, 9], 1))
.toEqual([[1], [2], [3], [4], [5], [6], [7], [8], [9]]);
expect(groupByEveryN([1, 2, 3, 4, 5, 6, 7, 8, 9], 2))
.toEqual([[1, 2], [3, 4], [5, 6], [7, 8], [9, null]]);
expect(groupByEveryN([1, 2, 3, 4, 5, 6, 7, 8, 9], 3))
.toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
expect(groupByEveryN([1, 2, 3, 4, 5, 6, 7, 8, 9], 4))
.toEqual([[1, 2, 3, 4], [5, 6, 7, 8], [9, null, null, null]]);
});
it ('should fill with null', () => {
expect(groupByEveryN([], 4))
.toEqual([]);
expect(groupByEveryN([1], 4))
.toEqual([[1, null, null, null]]);
expect(groupByEveryN([1, 2], 4))
.toEqual([[1, 2, null, null]]);
expect(groupByEveryN([1, 2, 3], 4))
.toEqual([[1, 2, 3, null]]);
expect(groupByEveryN([1, 2, 3, 4], 4))
.toEqual([[1, 2, 3, 4]]);
});
});

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2013-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";
jest.unmock('truncate');
describe('truncate', () => {
var truncate = require('truncate');
it ('should truncate', () => {
expect(truncate('Hello, world.', 5))
.toBe('He...');
});
it ('should not truncate', () => {
expect(truncate('Hello, world.', 50))
.toBe('Hello, world.');
});
it ('should not truncate more than minDelta chars.', () => {
expect(truncate('Hello, world.', 7, {minDelta: 10}))
.toBe('Hello, world.');
expect(truncate('Hello, world.', 7, {minDelta: 9}))
.toBe('Hell...');
});
it ('should break in the middle of words', () => {
expect(truncate('Hello, world. How are you?', 18, {breakOnWords: false}))
.toBe('Hello, world. H...');
expect(truncate('Hello, world.\nHow are you?', 18, {breakOnWords: false}))
.toBe('Hello, world.\nHo...');
});
it ('should break at word boundaries', () => {
expect(truncate('Hello, world. How are you?', 18, {breakOnWords: true}))
.toBe('Hello, world....');
expect(truncate('Hello, world.\nHow are you?', 18, {breakOnWords: true}))
.toBe('Hello, world....');
});
it ('should uses custom elipses', () => {
expect(truncate('Hello, world.', 9, {elipsis: '&middot'}))
.toBe('He&middot');
});
it ('shouldn\'t barf with weird input', () => {
expect(truncate('Hello, world.', 0))
.toBe('Hello,...');
expect(truncate('Hello, world.', -132))
.toBe('...');
expect(truncate('', 0))
.toBe('');
expect(truncate(null, 0))
.toBe(null);
});
});