Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit a58aac6

Browse files
committed
init
0 parents  commit a58aac6

File tree

4 files changed

+358
-0
lines changed

4 files changed

+358
-0
lines changed

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.idea
2+
.project
3+
.DS_Store
4+
*.sublime*
5+
*.seed
6+
*.log
7+
*.csv
8+
*.dat
9+
*.out
10+
*.pid
11+
*.swp
12+
*.swo
13+
node_modules

index.js

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
var _describe = {};
2+
var _it = {};
3+
var _beforeEach = {};
4+
var helpers = module.exports = {
5+
describe: _describe,
6+
it: _it,
7+
beforeEach: _beforeEach
8+
};
9+
var loopback = require('loopback');
10+
var assert = require('assert');
11+
var request = require('supertest');
12+
13+
_beforeEach.withApp = function(app) {
14+
beforeEach(function() {
15+
this.app = app;
16+
var _request = this.request = request(app);
17+
this.post = _request.post;
18+
this.get = _request.get;
19+
this.put = _request.put;
20+
this.del = _request.del;
21+
});
22+
}
23+
24+
_beforeEach.withDefaultDataSource = function(dataSource) {
25+
beforeEach(function() {
26+
this.dataSource = dataSource;
27+
28+
// TODO(ritch) this should not be required
29+
loopback.AccessToken.attachTo(dataSource);
30+
});
31+
}
32+
33+
_describe.model = function describeModel(app, modelName, cb) {
34+
assert(app, 'describeModel called without app');
35+
36+
describe('Model: ' + modelName, function() {
37+
beforeEach(function() {
38+
this.app = app;
39+
this.model = loopback.getModel(modelName);
40+
assert(this.model, 'you must define a model before testing it');
41+
});
42+
cb();
43+
});
44+
}
45+
46+
function mixin(obj, into) {
47+
Object.keys(obj).forEach(function(key) {
48+
if(typeof obj[key] === 'function') {
49+
into[key] = obj[key];
50+
}
51+
});
52+
}
53+
54+
_describe.staticMethod = function(methodName, cb) {
55+
describe('.' + methodName, function() {
56+
beforeEach(function() {
57+
this.method = methodName;
58+
this.isStaticMethod = true;
59+
});
60+
cb();
61+
});
62+
}
63+
64+
_describe.instanceMethod = function(methodName, cb) {
65+
describe('.prototype.' + methodName, function() {
66+
beforeEach(function() {
67+
this.method = methodName;
68+
this.isInstanceMethod = true;
69+
});
70+
cb();
71+
});
72+
}
73+
74+
_beforeEach.withArgs = function() {
75+
var args = Array.prototype.slice.call(arguments, 0);
76+
beforeEach(function() {
77+
this.args = args;
78+
});
79+
}
80+
81+
_beforeEach.givenModel = function(modelName, attrs, optionalHandler) {
82+
if(typeof attrs === 'funciton') {
83+
optionalHandler = attrs;
84+
attrs = undefined;
85+
}
86+
87+
attrs = attrs || {};
88+
89+
var model = loopback.getModel(modelName);
90+
91+
beforeEach(function(done) {
92+
var test = this;
93+
94+
if(!model.dataSource) {
95+
model.attachTo(this.dataSource);
96+
}
97+
98+
assert(model.dataSource, modelName + ' is not attached to a dataSource');
99+
assert(
100+
typeof model.create === 'function',
101+
modelName + ' does not have a create method'
102+
);
103+
104+
model.create(attrs, function(err, result) {
105+
if(err) {
106+
done(err);
107+
} else {
108+
test[modelName] = result;
109+
done();
110+
}
111+
});
112+
});
113+
114+
if(typeof optionalHandler === 'function') {
115+
beforeEach(optionalHandler);
116+
}
117+
118+
afterEach(function(done) {
119+
this[modelName].destroy(done);
120+
});
121+
}
122+
123+
_beforeEach.givenUser = function(attrs, optionalHandler) {
124+
_beforeEach.givenModel('user', attrs, optionalHandler);
125+
}
126+
127+
_beforeEach.givenAnUnauthenticatedToken = function(attrs, optionalHandler) {
128+
_beforeEach.givenModel('accessToken', attrs, optionalHandler);
129+
}
130+
131+
_beforeEach.givenAnAnonymousToken = function(attrs, optionalHandler) {
132+
_beforeEach.givenModel('accessToken', {id: '$anonymous'}, optionalHandler);
133+
}
134+
135+
_describe.whenCalledRemotely = function(verb, url, cb) {
136+
var urlStr = url;
137+
if(typeof url === 'function') {
138+
urlStr = '/<dynamic>';
139+
}
140+
describe(verb.toUpperCase() + ' ' + urlStr, function() {
141+
beforeEach(function(cb) {
142+
if(typeof url === 'function') {
143+
url = url.call(this);
144+
}
145+
this.remotely = true;
146+
this.verb = verb.toUpperCase();
147+
this.url = url;
148+
var methodForVerb = verb.toLowerCase();
149+
if(methodForVerb === 'delete') methodForVerb = 'del';
150+
151+
this.http = this.request[methodForVerb](url);
152+
this.http.set('Accept', 'application/json');
153+
this.req = this.http.req;
154+
var test = this;
155+
this.http.end(function(err) {
156+
test.res = test.http.res;
157+
cb();
158+
});
159+
});
160+
161+
cb();
162+
});
163+
}
164+
165+
_describe.whenCalledByUser = function(credentials, verb, url, cb) {
166+
_describe.whenLoggedInAsUser(credentials, function() {
167+
_describe.whenCalledRemotely(verb, url, cb);
168+
});
169+
}
170+
171+
_describe.whenCalledAnonymously = function(verb, url, cb) {
172+
_describe.whenCalledRemotely(verb, url, function() {
173+
_beforeEach.givenAnAnonymousToken();
174+
cb();
175+
});
176+
}
177+
178+
_describe.whenCalledUnauthenticated = function(verb, url, cb) {
179+
_describe.whenCalledRemotely(verb, url, function() {
180+
_beforeEach.givenAnUnauthenticatedToken();
181+
cb();
182+
});
183+
}
184+
185+
_describe.whenLoggedInAsUser = function(credentials, cb) {
186+
describe('when logged in user', function() {
187+
_beforeEach.givenUser(credentials, function(done) {
188+
var test = this;
189+
this.remotely = true;
190+
this.user.constructor.login(credentials, function(err, token) {
191+
if(err) {
192+
done(err);
193+
} else {
194+
test.accessToken = token;
195+
test.req.set('authorization', token.id);
196+
done();
197+
}
198+
});
199+
});
200+
201+
afterEach(function(done) {
202+
this.accessToken.destroy(done);
203+
});
204+
});
205+
}
206+
207+
_it.shouldBeAllowed = function() {
208+
it('should be allowed', function() {
209+
assert(this.req);
210+
assert(this.res);
211+
assert.notEqual(this.res.statusCode, 401);
212+
});
213+
}
214+
215+
_it.shouldBeDenied = function() {
216+
it('should not be allowed', function() {
217+
assert(this.res);
218+
assert.equal(this.res.statusCode, 401);
219+
});
220+
}
221+
222+
_it.shouldBeAllowedWhenCalledAnonymously =
223+
function(verb, url) {
224+
_describe.whenCalledAnonymously(verb, url, function() {
225+
_it.shouldBeAllowed();
226+
});
227+
}
228+
229+
_it.shouldBeDeniedWhenCalledAnonymously =
230+
function(verb, url) {
231+
_describe.whenCalledAnonymously(verb, url, function() {
232+
_it.shouldBeDenied();
233+
});
234+
}
235+
236+
_it.shouldBeAllowedWhenCalledUnauthenticated =
237+
function(verb, url) {
238+
_describe.whenCalledUnauthenticated(verb, url, function() {
239+
_it.shouldBeAllowed();
240+
});
241+
}
242+
243+
_it.shouldBeDeniedWhenCalledUnauthenticated =
244+
function(verb, url) {
245+
_describe.whenCalledUnauthenticated(verb, url, function() {
246+
_it.shouldBeDenied();
247+
});
248+
}
249+
250+
_it.shouldBeAllowedWhenCalledByUser =
251+
function(credentials, verb, url) {
252+
_describe.whenCalledByUser(credentials, verb, url, function() {
253+
_it.shouldBeAllowed();
254+
});
255+
}
256+
257+
_it.shouldBeDeniedWhenCalledByUser =
258+
function(credentials, verb, url) {
259+
_describe.whenCalledByUser(credentials, verb, url, function() {
260+
_it.shouldBeDenied();
261+
});
262+
}

package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "loopback-testing",
3+
"version": "0.0.0",
4+
"description": "Utilities for testing LoopBack applications",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "mocha"
8+
},
9+
"repository": "",
10+
"author": "Ritchie Martori",
11+
"license": "MIT",
12+
"dependencies": {
13+
"supertest": "~0.8.2",
14+
"mocha": "~1.15.1",
15+
"loopback-datasource-juggler": "~1.2.7",
16+
"loopback": "~1.3.3"
17+
}
18+
}

test/test.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
var loopback = require('loopback');
2+
var helpers = require('../');
3+
var assert = require('assert');
4+
5+
describe('helpers', function () {
6+
var testApp = loopback();
7+
var db = testApp.dataSource('db', {connector: loopback.Memory});
8+
var testModel = testApp.model('xxx-test-model', {dataSource: 'db'});
9+
10+
testApp.use(loopback.rest());
11+
helpers.beforeEach.withApp(testApp);
12+
helpers.beforeEach.withDefaultDataSource(loopback.memory());
13+
14+
describe('helpers.it', function() {
15+
['shouldBeAllowed',
16+
'shouldBeDenied']
17+
.forEach(function(func) {
18+
it('should have a method named ' + func, function () {
19+
assert.equal(typeof helpers.it[func], 'function');
20+
});
21+
});
22+
});
23+
24+
describe('helpers.describe', function() {
25+
['staticMethod',
26+
'whenLoggedInAsUser',
27+
'whenCalledAnonymously',
28+
'model']
29+
.forEach(function(func) {
30+
it('should have a method named ' + func, function () {
31+
assert.equal(typeof helpers.describe[func], 'function');
32+
});
33+
});
34+
});
35+
36+
describe('helpers.beforeEach', function() {
37+
['withArgs',
38+
'givenModel',
39+
'givenUser']
40+
.forEach(function(func) {
41+
it('should have a helper method named ' + func, function () {
42+
assert.equal(typeof helpers.beforeEach[func], 'function');
43+
});
44+
});
45+
});
46+
47+
describe('helpers.beforeEach.givenModel', function() {
48+
helpers.beforeEach.givenModel('AccessToken');
49+
it('should have an AccessToken property', function () {
50+
assert(this.AccessToken);
51+
assert(this.AccessToken.id);
52+
});
53+
});
54+
55+
describe('whenCalledRemotely', function() {
56+
helpers.describe.staticMethod('create', function() {
57+
helpers.beforeEach.withArgs({foo: 'bar'});
58+
helpers.describe.whenCalledRemotely('POST', '/xxx-test-models', function() {
59+
it('should call the method over rest', function () {
60+
assert.equal(this.res.statusCode, 200);
61+
});
62+
});
63+
});
64+
});
65+
});

0 commit comments

Comments
 (0)