react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / test / mocks.js
81152 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule mocks9*/1011function isA(typeName, value) {12return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';13}1415function getType(ref) {16if (isA('RegExp', ref)) {17return 'regexp';18}1920if (isA('Array', ref)) {21return 'array';22}2324if (isA('Function', ref)) {25return 'function';26}2728if (isA('Object', ref)) {29return 'object';30}3132// consider number and string fields to be constants that we want to33// pick up as they are34if (isA('Number', ref) || isA('String', ref)) {35return 'constant';36}3738return null;39}4041function makeComponent(metadata) {42switch (metadata.type) {43case 'object':44return {};4546case 'array':47return [];4849case 'regexp':50return new RegExp();5152case 'constant':53return metadata.value;5455case 'function':56var defaultReturnValue;57var specificReturnValues = [];58var mockImpl;59var isReturnValueLastSet = false;60var calls = [];61var instances = [];62var prototype =63(metadata.members && metadata.members.prototype &&64metadata.members.prototype.members) || {};6566var f = function() {67global.dirtyMocks.push(f);6869instances.push(this);70calls.push(Array.prototype.slice.call(arguments));71if (this instanceof arguments.callee) {72// This is probably being called as a constructor73for (var slot in prototype) {74// Copy prototype methods to the instance to make75// it easier to interact with mock instance call and76// return values77if (prototype[slot].type == 'function') {78var protoImpl = this[slot];79this[slot] = generateFromMetadata(prototype[slot]);80this[slot]._protoImpl = protoImpl;81}82}8384// Run the mock constructor implementation85mockImpl && mockImpl.apply(this, arguments);86return;87}8889var returnValue;90// If return value is last set, either specific or default, i.e.91// mockReturnValueOnce()/mockReturnValue() is called and no92// mockImplementation() is called after that.93// use the set return value.94if (isReturnValueLastSet) {95returnValue = specificReturnValues.shift();96if (returnValue === undefined) {97returnValue = defaultReturnValue;98}99}100101// If mockImplementation() is last set, or specific return values102// are used up, use the mock implementation.103if (mockImpl && returnValue === undefined) {104return mockImpl.apply(this, arguments);105}106107// Otherwise use prototype implementation108if (returnValue === undefined && arguments.callee._protoImpl) {109return arguments.callee._protoImpl.apply(this, arguments);110}111112return returnValue;113};114115f._isMockFunction = true;116117f.mock = {118calls : calls,119instances : instances120};121122f.mockClear = function() {123calls.length = 0;124instances.length = 0;125};126127f.mockReturnValueOnce = function(value) {128// next function call will return this value or default return value129isReturnValueLastSet = true;130specificReturnValues.push(value);131return f;132};133134f.mockReturnValue = function(value) {135// next function call will return specified return value or this one136isReturnValueLastSet = true;137defaultReturnValue = value;138return f;139};140141f.mockImplementation = function(fn) {142// next function call will use mock implementation return value143isReturnValueLastSet = false;144mockImpl = fn;145return f;146};147148f.mockReturnThis = function() {149return f.mockImplementation(function() {150return this;151});152};153154f._getMockImplementation = function() {155return mockImpl;156};157158if (metadata.mockImpl) {159f.mockImplementation(metadata.mockImpl);160}161162return f;163}164165throw new Error('Unrecognized type ' + metadata.type);166}167168function generateFromMetadata(_metadata) {169var callbacks = [];170var refs = {};171172function generateMock(metadata) {173var mock = makeComponent(metadata);174if (metadata.ref_id != null) {175refs[metadata.ref_id] = mock;176}177178function getRefCallback(slot, ref) {179return function() {180mock[slot] = refs[ref];181};182}183184for (var slot in metadata.members) {185var slotMetadata = metadata.members[slot];186if (slotMetadata.ref != null) {187callbacks.push(getRefCallback(slot, slotMetadata.ref));188} else {189mock[slot] = generateMock(slotMetadata);190}191}192193return mock;194}195196var mock = generateMock(_metadata);197callbacks.forEach(function(setter) {198setter();199});200201return mock;202}203204205function _getMetadata(component, _refs) {206var refs = _refs || [];207208// This is a potential performance drain, since the whole list is scanned209// for every component210var ref = refs.indexOf(component);211if (ref > -1) {212return {ref: ref};213}214215var type = getType(component);216if (!type) {217return null;218}219220var metadata = {type : type};221if (type == 'constant') {222metadata.value = component;223return metadata;224} else if (type == 'function') {225if (component._isMockFunction) {226metadata.mockImpl = component._getMockImplementation();227}228}229230metadata.ref_id = refs.length;231refs.push(component);232233var members = null;234235function addMember(slot, data) {236if (!data) {237return;238}239if (!members) {240members = {};241}242members[slot] = data;243}244245// Leave arrays alone246if (type != 'array') {247for (var slot in component) {248if (slot.charAt(0) == '_' ||249(type == 'function' && component._isMockFunction &&250slot.match(/^mock/))) {251continue;252}253254if (component.hasOwnProperty(slot) ||255(type == 'object' && component[slot] != Object.prototype[slot])) {256addMember(slot, _getMetadata(component[slot], refs));257}258}259260// If component is native code function, prototype might be undefined261if (type == 'function' && component.prototype) {262var prototype = _getMetadata(component.prototype, refs);263if (prototype && prototype.members) {264addMember('prototype', prototype);265}266}267}268269if (members) {270metadata.members = members;271}272273return metadata;274}275276function removeUnusedRefs(metadata) {277function visit(md, f) {278f(md);279if (md.members) {280for (var slot in md.members) {281visit(md.members[slot], f);282}283}284}285286var usedRefs = {};287visit(metadata, function(md) {288if (md.ref != null) {289usedRefs[md.ref] = true;290}291});292293visit(metadata, function(md) {294if (!usedRefs[md.ref_id]) {295delete md.ref_id;296}297});298}299300var global = Function("return this")();301global.dirtyMocks = global.dirtyMocks || [];302303module.exports = {304/**305* Invokes the .mockClear method of all function mocks that have been306* called since the last time clear was called.307*/308clear: function() {309var old = global.dirtyMocks;310global.dirtyMocks = [];311old.forEach(function(mock) {312mock.mockClear();313});314},315316/**317* Generates a mock based on the given metadata. Mocks treat functions318* specially, and all mock functions have additional members, described in the319* documentation for getMockFunction in this module.320*321* One important note: function prototoypes are handled specially by this322* mocking framework. For functions with prototypes, when called as a323* constructor, the mock will install mocked function members on the instance.324* This allows different instances of the same constructor to have different325* values for its mocks member and its return values.326*327* @param metadata Metadata for the mock in the schema returned by the328* getMetadata method of this module.329*330*/331generateFromMetadata: generateFromMetadata,332333/**334* Inspects the argument and returns its schema in the following recursive335* format:336* {337* type: ...338* members : {}339* }340*341* Where type is one of 'array', 'object', 'function', or 'ref', and members342* is an optional dictionary where the keys are member names and the values343* are metadata objects. Function prototypes are defined simply by defining344* metadata for the member.prototype of the function. The type of a function345* prototype should always be "object". For instance, a simple class might be346* defined like this:347*348* {349* type: 'function',350* members: {351* staticMethod: {type: 'function'},352* prototype: {353* type: 'object',354* members: {355* instanceMethod: {type: 'function'}356* }357* }358* }359* }360*361* Metadata may also contain references to other objects defined within the362* same metadata object. The metadata for the referent must be marked with363* 'ref_id' key and an arbitrary value. The referer must be marked with a364* 'ref' key that has the same value as object with ref_id that it refers to.365* For instance, this metadata blob:366* {367* type: 'object',368* ref_id: 1,369* members: {370* self: {ref: 1}371* }372* }373*374* defines an object with a slot named 'self' that refers back to the object.375*376* @param component The component for which to retrieve metadata.377*/378getMetadata: function(component) {379var metadata = _getMetadata(component);380// to make it easier to work with mock metadata, only preserve references381// that are actually used382removeUnusedRefs(metadata);383return metadata;384},385386/**387* Generates a stand-alone function with members that help drive unit tests or388* confirm expectations. Specifically, functions returned by this method have389* the following members:390*391* .mock:392* An object with two members, "calls", and "instances", which are both393* lists. The items in the "calls" list are the arguments with which the394* function was called. The "instances" list stores the value of 'this' for395* each call to the function. This is useful for retrieving instances from a396* constructor.397*398* .mockReturnValueOnce(value)399* Pushes the given value onto a FIFO queue of return values for the400* function.401*402* .mockReturnValue(value)403* Sets the default return value for the function.404*405* .mockImplementation(function)406* Sets a mock implementation for the function.407*408* .mockReturnThis()409* Syntactic sugar for .mockImplementation(function() {return this;})410*411* In case both mockImplementation() and412* mockReturnValueOnce()/mockReturnValue() are called. The priority of413* which to use is based on what is the last call:414* - if the last call is mockReturnValueOnce() or mockReturnValue(),415* use the specific return specific return value or default return value.416* If specific return values are used up or no default return value is set,417* fall back to try mockImplementation();418* - if the last call is mockImplementation(), run the given implementation419* and return the result.420*/421getMockFunction: function() {422return makeComponent({type: 'function'});423}424};425426427