Skip to content

Commit 9196d3d

Browse files
authored
Merge pull request #31 from embermap/nock-server
nock server
2 parents 03311a1 + 94241cd commit 9196d3d

22 files changed

Lines changed: 488 additions & 67 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { fetch } from 'whatwg-fetch';
2+
3+
let createMock = async function(path, method, statusCode, response) {
4+
return await fetch('/__mock-request', {
5+
method: 'post',
6+
headers: {
7+
"Content-Type": "application/json",
8+
},
9+
body: JSON.stringify({
10+
path,
11+
method,
12+
statusCode,
13+
response
14+
}),
15+
});
16+
}
17+
18+
export let mockServer = {
19+
async get(path, response, status = 200) {
20+
return createMock(path, "GET", status, response);
21+
},
22+
23+
async post(path, response, status = 200) {
24+
return createMock(path, "POST", status, response);
25+
},
26+
27+
async patch(path, response, status = 200) {
28+
return createMock(path, "PATCH", status, response);
29+
},
30+
31+
async put(path, response, status = 200) {
32+
return createMock(path, "PUT", status, response);
33+
},
34+
35+
async delete(path, response, status = 200) {
36+
return createMock(path, "DELETE", status, response);
37+
},
38+
39+
async cleanUp() {
40+
return fetch('/__cleanup-mocks');
41+
}
42+
};

addon-test-support/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { fetch } from 'whatwg-fetch';
22
import { setupContext, teardownContext } from '@ember/test-helpers';
3+
import { mockServer } from './-private/mock-server';
34
import param from 'jquery-param';
45

56
export function setup(hooks) {
67
hooks.beforeEach(function() {
78
return setupContext(this);
89
});
910

10-
hooks.afterEach(function() {
11+
hooks.afterEach(async function() {
12+
await mockServer.cleanUp();
1113
return teardownContext(this);
1214
});
1315
}
@@ -34,6 +36,8 @@ export async function visit(url, options = {}) {
3436
return result;
3537
}
3638

39+
export { mockServer };
40+
3741
// private
3842

3943
let fetchFromEmberCli = async function(url, headers) {

blueprints/fastboot-test/files/tests/fastboot/__name__-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { module, test } from 'qunit';
2-
import { setup, visit } from 'ember-cli-fastboot-testing/test-support';
2+
import { setup, visit, /* mockServer */ } from 'ember-cli-fastboot-testing/test-support';
33

44
module('FastBoot | <%= dasherizedModuleName %> test', function(hooks) {
55
setup(hooks);

index.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
let FastBoot = require('fastboot');
44
let url = require('url');
55
let resolve = require('resolve');
6+
let nock = require('nock');
7+
let bodyParser = require('body-parser')
68

79
module.exports = {
810
name: 'ember-cli-fastboot-testing',
@@ -39,6 +41,23 @@ module.exports = {
3941
},
4042

4143
_fastbootRenderingMiddleware(app) {
44+
45+
app.use(bodyParser.json());
46+
app.post('/__mock-request', (req, res) => {
47+
let mock = nock(req.headers.origin)
48+
.persist()
49+
.intercept(req.body.path, req.body.method)
50+
.reply(req.body.statusCode, req.body.response);
51+
52+
res.json({ mocks: mock.pendingMocks() });
53+
});
54+
55+
app.use('/__cleanup-mocks', (req, res) => {
56+
nock.cleanAll()
57+
58+
res.json({ ok: true });
59+
});
60+
4261
app.use('/__fastboot-testing', (req, res) => {
4362
let urlToVisit = decodeURIComponent(req.query.url);
4463
let parsed = url.parse(urlToVisit, true);

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
"test:all": "ember try:each"
2222
},
2323
"dependencies": {
24+
"body-parser": "^1.18.3",
2425
"ember-auto-import": "^1.2.15",
2526
"ember-cli-babel": "^6.6.0",
2627
"fastboot": "1.2.* || 2.*",
2728
"jquery-param": "^1.0.1",
2829
"resolve": "^1.10.0",
30+
"nock": "^10.0.6",
2931
"whatwg-fetch": "^3.0.0"
3032
},
3133
"devDependencies": {
@@ -53,7 +55,7 @@
5355
"ember-cli-uglify": "^2.0.0",
5456
"ember-disable-prototype-extensions": "^1.1.2",
5557
"ember-export-application-global": "^2.0.0",
56-
"ember-fetch": "^6.4.0",
58+
"ember-fetch": "^6.5.0",
5759
"ember-load-initializers": "^1.1.0",
5860
"ember-maybe-import-regenerator": "^0.1.6",
5961
"ember-resolver": "^4.0.0",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import DS from 'ember-data';
2+
3+
export default DS.JSONAPIAdapter.extend({
4+
namespace: 'api'
5+
});

tests/dummy/app/models/post.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import DS from 'ember-data';
2+
3+
export default DS.Model.extend({
4+
title: DS.attr('string'),
5+
});

tests/dummy/app/pods/application/route.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,4 @@ export default Route.extend({
77
afterModel() {
88
this.set('headData.title', 'Fastboot testing');
99
}
10-
1110
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Network mocking
2+
3+
Just about every Ember application ends up having pages that depend on data from an external API. It's common to use tools like [Mirage](https://www.ember-cli-mirage.com/) to mock the network when testing these applications.
4+
5+
It makes sense that we would also want to mock the network while running FastBoot tests. However, since FastBoot runs inside of Node.js, we'll need to use a network mocking library that is written for node.
6+
7+
FastBoot Testing exposes an API for intercepting requests made from your FastBoot app.
8+
9+
The following example shows how to mock a test that fetches blog post data.
10+
11+
```js
12+
import { module, test } from 'qunit';
13+
import { setup, visit, mockServer } from 'ember-cli-fastboot-testing/test-support';
14+
15+
module('Fastboot | Blog post page', function(hooks) {
16+
setup(hooks);
17+
18+
test('it can render a blog post', async function(assert) {
19+
await mockServer.get('/api/posts/1', {
20+
data: {
21+
type: 'post',
22+
id: '1',
23+
attributes: {
24+
title: 'Hello world!'
25+
}
26+
}
27+
});
28+
29+
await visit('/posts/1');
30+
31+
assert.dom('[data-test-id="title"]').hasText("Hello world!");
32+
});
33+
});
34+
```
35+
36+
The `mockServer#get` method maps a URL to a response for the lifecycle of the test. At the end of each test the `mockServer` is reset.
37+
38+
By default, the `mockServer` will use a status code of 200. However, an optional status code can be passed in as the third parameter.
39+
40+
```js
41+
test('it renders the 404 page when it cannot fetch a blog post', async function(assert) {
42+
await mockServer.get(
43+
'/api/posts/1',
44+
{ error: 'Post not found' },
45+
404
46+
);
47+
48+
await visit('/posts/1');
49+
50+
assert.dom('[data-test-id="page-not-found"]').exists();
51+
});
52+
```
53+
54+
The `mockServer` also exposes `post`, `patch`, `put`, and `delete` mocking methods.

tests/dummy/app/pods/docs/template.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
{{nav.section 'Usage & options'}}
1111
{{nav.item 'Visit helper' 'docs.visit'}}
12+
{{nav.item 'Network mocking' 'docs.network-mocking'}}
1213

1314
{{nav.section 'Tips & tricks'}}
1415
{{nav.item 'Debugging' 'docs.debugging'}}

0 commit comments

Comments
 (0)