Skip to content
This repository was archived by the owner on Nov 18, 2022. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
nene committed Feb 6, 2017
0 parents commit 1e921ef
Show file tree
Hide file tree
Showing 8 changed files with 3,111 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compact": false,
"presets": [
["es2015"]
]
}
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[*]
indent_style = space
end_of_line = lf
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true

[*.md]
max_line_length = 0
trim_trailing_whitespace = false

[*.json]
indent_size = 2
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lib/
node_modules/
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Strict spies

A strict alternative for [Jasmine spies][].

## Install

yarn add --dev strict-spies

## Usage

Include in your Jasmine setup file that's run before all tests:

```js
import StrictSpies, {toHaveCalls, toHaveSingleCall, toHaveAnyCalls} from "../src/StrictSpies";

beforeEach(function() {
// Initialize spies container for each test run.
this.spies = new StrictSpies();

// Register Spies-specific assertions to be used insited of Jasmine built-in spy-assertions
jasmine.addMatchers({toHaveCalls, toHaveSingleCall, toHaveAnyCalls});
});
```


[Jasmine spies]: https://jasmine.github.io/2.5/introduction#section-Spies
38 changes: 38 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "strict-spies",
"version": "1.0.0",
"description": "A strict alternative for Jasmine spies",
"author": "Rene Saarsoo <[email protected]>",
"keywords": [
"unit-testing",
"jasmine",
"spy"
],
"license": "MIT",
"homepage": "http://github.com/nene/spies",
"repository": {
"type": "git",
"url": "git://github.com/nene/strict-spies.git"
},
"main": "lib/Spies.js",
"files": ["lib/"],
"engines": {
"node": ">= 4.0"
},
"jest": {
"testRegex": "test/.*Test.js$",
"modulePaths": [
"src/"
]
},
"devDependencies": {
"babel-cli": "^6.22.2",
"babel-jest": "^18.0.0",
"babel-preset-es2015": "^6.22.0",
"jest": "^18.1.0"
},
"scripts": {
"prepublish": "rm -rf lib/ && babel src/ --out-dir lib/",
"test": "jest"
}
}
153 changes: 153 additions & 0 deletions src/StrictSpies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/**
* Helper for creating spies.
*/
export default class Spies {
constructor() {
this.allCalls = [];
}

/**
* Creates a single spy function.
*
* Note that the created spy does not store information about the `this` value,
* to gather `this` values, use createScoped().
*
* @param {String} name Unique name for the function
* @param {Function} [mockFunc] A mocked function to call
* @return {Function}
*/
create(name, mockFunc) {
return (...args) => {
this.allCalls.push([name, ...args]);

if (mockFunc) {
return mockFunc(...args);
}
};
}

/**
* Creates a single spy function that also monitors its `this` parameter.
*
* When a scoped spy is called, the calls() array will contain: [name, this, arg1, arg2, ...]
*
* @param {String} name Unique name for the function
* @return {Function}
*/
createScoped(name) {
const self = this;
return function(...args) {
self.allCalls.push([name, this, ...args]);
};
}

/**
* Creates a Spy object with set of methods to spy on.
* @param {String} prefix A prefix for each spy method name.
* This helps us differenciate between different spy objects
* that have methods with same names.
* @param {String[]} methodNames Array of method names to spy
* @return {Object}
*/
createObj(prefix, methodNames) {
const obj = {};
methodNames.forEach((name) => {
obj[name] = this.create(prefix + "." + name);
});
return obj;
}

/**
* Clears all gathered spying data.
*/
reset() {
this.allCalls = [];
}

/**
* Returns array of all calls to all the spies.
* Each call being represented by array of: [name, arg1, arg2, ...]
* @return {Array}
*/
calls() {
return this.allCalls;
}
}

/**
* Jasmine assertion to check all calls to spies.
*
* For example:
*
* expect(this.spies).toHaveCalls([
* ["myFunc1", "arg1", "arg2"],
* ["myFunc2", "arg3", "arg4"],
* ]);
*/
export function toHaveCalls(...helpers) {
return {
compare(spies, expected) {
return compareCalls(spies, expected, ...helpers);
}
};
}

/**
* Jasmine assertion to check single call of a spy.
* Will also fail when more than other spies have also been called.
*
* For example:
*
* expect(this.spies).toHaveSingleCall("myFunc", "arg1", "arg2");
*/
export function toHaveSingleCall(...helpers) {
return {
compare(spies, ...expected) {
return compareCalls(spies, [expected], ...helpers);
}
};
}

function compareCalls(spies, expected, util, customEqualityTesters) {
const actual = spies.calls();
const result = {};
result.pass = util.equals(actual, expected, customEqualityTesters);

if (result.pass) {
result.message = () => {
throw "Do not use negative assertions with spies";
};
}
else {
result.message = () => "Expected calls\n" + jasmine.pp(actual) + "\nto equal\n" + jasmine.pp(expected);
}

return result;
}

/**
* Jasmine assertion to check whether any calls have happened at all.
*
* Mainly to be used for checking that no calls have happened.
*
* expect(this.spies).not.toHaveAnyCalls();
*/
export function toHaveAnyCalls() {
return {
compare(spies) {
const actual = spies.calls();

const result = {};
result.pass = actual.length > 0;

if (result.pass) {
result.message = () => "Expected no calls, but found\n" + jasmine.pp(actual);
}
else {
result.message = () => "Expected at least some calls, but found none";
}

return result;
}
};
}
Loading

0 comments on commit 1e921ef

Please sign in to comment.