Skip to content

Commit 882ca8e

Browse files
authored
Merge pull request #164 from Kashoo/issue-163-static-simple-mock
Issue 163: Include simple-mock as a static dependency for test-helper
2 parents d628d1f + ab8f62a commit 882ca8e

9 files changed

Lines changed: 279 additions & 15 deletions

File tree

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@ language: node_js
22
node_js:
33
- "node"
44
- "lts/*"
5-
- "0.10"

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
55
## [Unreleased]
66

77
### Changed
8-
- [#156](https://github.com/Kashoo/synctos/issues/156): Fix for an edge case where users assigned a replace role may gain the privilege of removing a document erroneously under certain document definition conditions
98
- [#157](https://github.com/Kashoo/synctos/issues/157): Swap in Chai as the assertion library used in specs throughout the project
9+
- [#163](https://github.com/Kashoo/synctos/issues/163): Embed simple-mock as a static development dependency
1010

1111
### Fixed
12+
- [#156](https://github.com/Kashoo/synctos/issues/156): Users with a replace role may erroneously gain the privilege of removing a document under certain conditions
1213
- [#160](https://github.com/Kashoo/synctos/issues/160): Unable to import document if it was deleted via Couchbase SDK
1314

1415
## [1.9.3] - 2017-10-23

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -660,24 +660,25 @@ The synctos project includes a variety of specifications/test cases to verify th
660660

661661
The post [Testing your Sync Gateway functions with synctos](https://blog.couchbase.com/testing-sync-gateway-functions-synctos/) on the official Couchbase blog provides a detailed walkthrough, with examples, for setting up and running tests. The following section also provides a brief overview of the process.
662662

663-
To include the test helper module in your own sync function test cases, you must first ensure that your project [includes](https://docs.npmjs.com/getting-started/using-a-package.json) the development dependencies it relies upon. Update your project's `devDependencies` to include the following packages:
663+
The synctos project uses the [mocha](https://mochajs.org/) test framework for writing and executing test cases, and the following instructions assume that you will too. Similarly, the [Chai](http://chaijs.com/) assertion library is used by synctos. But, by no means are your projects restricted to using either of these libraries for their own tests.
664664

665-
* [simple-mock](https://www.npmjs.com/package/simple-mock) for mocking/stubbing the built-in Sync Gateway functions `requireAccess`, `channel`, `access`, etc.
666-
* [mocha](https://mochajs.org/) or another JavaScript test runner/framework that supports `expect.js`
665+
Some other test runners/frameworks that might be of interest:
667666

668-
The synctos project uses `mocha` for writing and executing test cases and the following instructions assume that you will too, but you are free to substitute something else if you like.
667+
* [Jasmine](https://jasmine.github.io/)
668+
* [Vows](http://vowsjs.org/)
669669

670-
Similarly, the `Chai` assertion library is used in test cases on the synctos project, with the exception of several assertions made in `etc/test-helper.js`, which uses the Node.js assert API. This by no means ties a client project to either of these dependencies and any assertion library may be used for writing test cases in a dependent project, including:
670+
And some alternate assertion libraries:
671671

672-
* [Chai](http://chaijs.com/)
673672
* [Node.js Assertions](https://nodejs.org/dist/latest/docs/api/assert.html)
674673
* [expect.js](https://www.npmjs.com/package/expect.js)
675674

676-
Once your dev dependencies have been set up, run `npm install` to download the extra dependencies.
675+
Once your testing libraries have been set up as development dependencies in your project's [package.json](https://docs.npmjs.com/getting-started/using-a-package.json), run `npm install` to download them.
677676

678-
After that, create a new spec file in your project's `test/` directory (e.g. `test/foobar-spec.js`) and import the test helper module into the empty spec:
677+
After that, create a new specification file in your project's `test/` directory (e.g. `test/foobar-spec.js`) and import the test helper module into the empty spec:
679678

680-
var testHelper = require('../node_modules/synctos/etc/test-helper.js');
679+
```
680+
var testHelper = require('../node_modules/synctos/etc/test-helper.js');
681+
```
681682

682683
Create a new `describe` block to encapsulate the forthcoming test cases and also initialize the synctos test helper before each test case using the `beforeEach` function. For example:
683684

etc/sync-function-template.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ function synctos(doc, oldDoc) {
8282
// is enabled and the document was deleted via the Couchbase SDK. Skip everything else and simply assign the
8383
// public channel
8484
// (https://developer.couchbase.com/documentation/mobile/current/guides/sync-gateway/channels/index.html#special-channels)
85-
// to the document so that it can be replaced in the future.
85+
// to the document so that users will get a 404 Not Found if they attempt to fetch (i.e. "view") the deleted
86+
// document rather than a 403 Forbidden.
8687
requireAccess('!');
8788
channel('!');
8889

etc/test-helper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ exports.verifyAccessDenied = verifyAccessDenied;
234234
exports.verifyUnknownDocumentType = verifyUnknownDocumentType;
235235

236236
var assert = require('assert');
237-
var simple = require('simple-mock');
237+
var simple = require('../lib/simple-mock/index.js');
238238
var fs = require('fs');
239239
var syncFunctionLoader = require('./sync-function-loader.js');
240240

lib/simple-mock/LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2014 Pieter Raubenheimer
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+

lib/simple-mock/VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.8.0

lib/simple-mock/index.js

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
/* global define */
2+
(function (global, module) { // Browser compatibility
3+
var simple = module.exports
4+
var mocks = []
5+
var totalCalls = 0
6+
7+
/**
8+
* Restore the current simple and create a new one
9+
*
10+
* @param {Object} [obj]
11+
* @param {String} [key]
12+
* @api public
13+
*/
14+
simple.restore = function (obj, key) {
15+
if (obj && key) {
16+
mocks.some(function (mock, i) {
17+
if (mock.obj !== obj || mock.key !== key) return
18+
19+
mock.restore()
20+
mocks.splice(i, 1)
21+
return true
22+
})
23+
return
24+
}
25+
26+
mocks.forEach(_restoreMock)
27+
mocks = []
28+
}
29+
30+
/**
31+
* Create a mocked value on an object
32+
*
33+
* @param {Object} obj
34+
* @param {String} key
35+
* @param {Function|Value} mockValue
36+
* @return {Function} mock
37+
* @api public
38+
*/
39+
simple.mock = function (obj, key, mockValue) {
40+
if (!arguments.length) {
41+
return simple.spyOrStub()
42+
} else if (arguments.length === 1) {
43+
return simple.spyOrStub(obj)
44+
} else if (isFunction(mockValue)) {
45+
mockValue = simple.spyOrStub(mockValue)
46+
} else if (arguments.length === 2) {
47+
mockValue = simple.spyOrStub(obj, key)
48+
}
49+
50+
var mock = {
51+
obj: obj,
52+
key: key,
53+
mockValue: mockValue,
54+
oldValue: obj[key],
55+
oldValueHasKey: (key in obj)
56+
}
57+
58+
mock.restore = _restoreMock.bind(null, mock)
59+
mocks.unshift(mock)
60+
61+
obj[key] = mockValue
62+
return mockValue
63+
}
64+
65+
/**
66+
* Create a stub function
67+
*
68+
* @param {Function|Object} wrappedFn
69+
* @param {String} [key]
70+
* @return {Function} spy
71+
* @api public
72+
*/
73+
simple.spyOrStub = simple.stub = simple.spy = function (wrappedFn, key) {
74+
if (arguments.length === 2) {
75+
wrappedFn = wrappedFn[key]
76+
}
77+
78+
var originalFn = wrappedFn
79+
80+
var stubFn = function () {
81+
var action = (newFn.loop) ? newFn.actions[(newFn.callCount - 1) % newFn.actions.length] : newFn.actions.shift()
82+
83+
if (!action) return
84+
if (action.throwError) throw action.throwError
85+
if ('returnValue' in action) return action.returnValue
86+
if ('fn' in action) return action.fn.apply(action.ctx || this, arguments)
87+
88+
var cb = ('cbArgIndex' in action) ? arguments[action.cbArgIndex] : arguments[arguments.length - 1]
89+
if (action.cbArgs) return cb.apply(action.ctx || null, action.cbArgs)
90+
}
91+
92+
var newFn = function () {
93+
var call = {
94+
k: totalCalls++, // Keep count of calls to record the absolute order of calls
95+
args: Array.prototype.slice.call(arguments, 0),
96+
arg: arguments[0],
97+
context: this
98+
}
99+
newFn.calls.push(call)
100+
newFn.firstCall = newFn.firstCall || call
101+
newFn.lastCall = call
102+
newFn.callCount++
103+
newFn.called = true
104+
105+
try {
106+
call.returned = (wrappedFn || _noop).apply(this, arguments)
107+
} catch(e) {
108+
call.threw = e
109+
throw e
110+
}
111+
return call.returned
112+
}
113+
114+
// Spy
115+
newFn.reset = function () {
116+
newFn.calls = []
117+
newFn.lastCall = { args: [] } // For dot-safety
118+
newFn.callCount = 0
119+
newFn.called = false
120+
}
121+
newFn.reset()
122+
123+
// Stub
124+
newFn.actions = []
125+
newFn.loop = true
126+
127+
newFn.callbackWith = newFn.callback = function () {
128+
wrappedFn = stubFn
129+
newFn.actions.push({ cbArgs: arguments })
130+
return newFn // Chainable
131+
}
132+
133+
newFn.callbackArgWith = newFn.callbackAtIndex = function () {
134+
wrappedFn = stubFn
135+
newFn.actions.push({
136+
cbArgs: Array.prototype.slice.call(arguments, 1),
137+
cbArgIndex: arguments[0]
138+
})
139+
return newFn // Chainable
140+
}
141+
142+
newFn.inThisContext = function (obj) {
143+
var action = newFn.actions[newFn.actions.length - 1]
144+
action.ctx = obj
145+
return newFn // Chainable
146+
}
147+
148+
newFn.withArgs = function () {
149+
var action = newFn.actions[newFn.actions.length - 1]
150+
action.cbArgs = arguments
151+
return newFn // Chainable
152+
}
153+
154+
newFn.returnWith = function (returnValue) {
155+
wrappedFn = stubFn
156+
newFn.actions.push({ returnValue: returnValue })
157+
return newFn // Chainable
158+
}
159+
160+
newFn.throwWith = function (err) {
161+
wrappedFn = stubFn
162+
newFn.actions.push({ throwError: err })
163+
return newFn // Chainable
164+
}
165+
166+
newFn.resolveWith = function (value) {
167+
if (simple.Promise.when) return newFn.callFn(function createResolvedPromise () { return simple.Promise.when(value) })
168+
return newFn.callFn(function createResolvedPromise () { return simple.Promise.resolve(value) })
169+
}
170+
171+
newFn.rejectWith = function (value) {
172+
return newFn.callFn(function createRejectedPromise () { return simple.Promise.reject(value) })
173+
}
174+
175+
newFn.callFn = function (fn) {
176+
wrappedFn = stubFn
177+
newFn.actions.push({ fn: fn })
178+
return newFn // Chainable
179+
}
180+
181+
newFn.withActions = function (actions) {
182+
wrappedFn = stubFn
183+
if (actions && actions.length >= 0) {
184+
Array.prototype.push.apply(newFn.actions, actions)
185+
}
186+
return newFn // Chainable
187+
}
188+
189+
newFn.callOriginal = newFn.callOriginalFn = function () {
190+
wrappedFn = stubFn
191+
newFn.actions.push({ fn: originalFn })
192+
return newFn // Chainable
193+
}
194+
195+
newFn.withLoop = function () {
196+
newFn.loop = true
197+
return newFn // Chainable
198+
}
199+
200+
newFn.noLoop = function () {
201+
newFn.loop = false
202+
return newFn // Chainable
203+
}
204+
return newFn
205+
}
206+
207+
function _restoreMock (mock) {
208+
if (!mock.oldValueHasKey) {
209+
delete mock.obj[mock.key]
210+
return
211+
}
212+
mock.obj[mock.key] = mock.oldValue
213+
}
214+
215+
function _noop () {}
216+
217+
/**
218+
* Return whether the passed variable is a function
219+
*
220+
* @param {Mixed} value
221+
* @return {Boolean}
222+
* @api private
223+
*/
224+
function isFunction (value) {
225+
return Object.prototype.toString.call(value) === funcClass
226+
}
227+
var funcClass = '[object Function]'
228+
229+
// Browser compatibility
230+
if (typeof define === 'function' && define.amd) {
231+
define(function () {
232+
return simple
233+
})
234+
} else if (typeof window !== 'undefined') {
235+
window.simple = simple
236+
}
237+
238+
// Native Promise support
239+
if (typeof Promise !== 'undefined') simple.Promise = Promise
240+
})(this, typeof module !== 'undefined' ? module : {exports: {}})

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
"devDependencies": {
88
"chai": "^4.1.2",
99
"jshint": "^2.9.4",
10-
"mocha": "^3.3.0",
11-
"simple-mock": "^0.7.3"
10+
"mocha": "^3.3.0"
1211
},
1312
"scripts": {
1413
"test": "etc/prepare-tests.sh && node_modules/.bin/mocha",

0 commit comments

Comments
 (0)