Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Expose nock via comlink #61

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions addon-test-support/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { fetch } from 'whatwg-fetch';
import { setupContext, teardownContext } from '@ember/test-helpers';
import { mockServer } from './-private/mock-server';
import param from 'jquery-param';
import * as Comlink from 'comlink';

export function setup(hooks) {
hooks.beforeEach(async function() {
Expand Down Expand Up @@ -39,6 +40,35 @@ export async function visit(url, options = {}) {

export { mockServer };

export const nock = Comlink.wrapChain({
listeners: [],

async postMessage(message) {
let response = await fetch('/__nock-proxy', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(message),
});

let result = await response.json();

for (let listener of this.listeners) {
listener({ data: result });
}
},

addEventListener(type, listener) {
this.listeners.push(listener);
},

removeEventListener(type, listener) {
let index = this.listeners.indexOf(listener);
this.listeners.splice(index, 1);
},
});

// private

let fetchFromEmberCli = async function(url, headers) {
Expand All @@ -50,17 +80,19 @@ let fetchFromEmberCli = async function(url, headers) {
response = await fetch(endpoint);
} catch (e) {
if (e.message && e.message.match(/^Mirage:/)) {
error = `Ember CLI FastBoot Testing: It looks like Mirage is intercepting ember-cli-fastboot-testing's attempt to render ${url}. Please disable Mirage when running FastBoot tests.`;
error = `It looks like Mirage is intercepting ember-cli-fastboot-testing's attempt to render ${url}. Please disable Mirage when running FastBoot tests.`;
} else {
error = `Ember CLI FastBoot Testing: We were unable to render ${url}. Is your test suite blocking or intercepting HTTP requests? Error: ${e.message ? e.message : e}.`
error = `We were unable to render ${url}. Is your test suite blocking or intercepting HTTP requests? Error: ${e.message ? e.message : e}.`
}
}

if (response && response.headers && response.headers.get && response.headers.get('x-fastboot-testing') !== 'true') {
error = `Ember CLI FastBoot Testing: We were unable to render ${url}. Is your test suite blocking or intercepting HTTP requests?`;
error = `We were unable to render ${url}. Is your test suite blocking or intercepting HTTP requests?`;
}

if (error) {
error = `Ember CLI FastBoot Testing: ${error}`;

// eslint-disable-next-line no-console
console.error(error);
throw new Error(error);
Expand Down
51 changes: 50 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,46 @@ let FastBoot = require('fastboot');
let url = require('url');
let resolve = require('resolve');
let nock = require('nock');
let bodyParser = require('body-parser')
let bodyParser = require('body-parser');
let Comlink = require('comlink');

let nockInterface = {
listeners: [],
lastMessage: null,

dispatchEvent(message) {
let listener = this.listeners[0];
return listener({ data: message });
},

postMessage(message) {
this.lastMessage = message;
},

addEventListener(type, listener) {
this.listeners.push(listener);
},

removeEventListener(type, listener) {
let index = this.listeners.indexOf(listener);
this.listeners.splice(index, 1);
},
}

Comlink.expose(nock, nockInterface);

let getCircularReplacer = () => {
let seen = new WeakSet();
return (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
}

module.exports = {
name: 'ember-cli-fastboot-testing',
Expand Down Expand Up @@ -41,6 +80,16 @@ module.exports = {
},

_fastbootRenderingMiddleware(app) {
app.post('/__nock-proxy', bodyParser.json({ limit: '50mb' }), (req, res) => {
nockInterface.dispatchEvent(req.body).then(() => {
let body = JSON.stringify(
nockInterface.lastMessage,
getCircularReplacer()
);

res.send(body);
});
});

app.post('/__mock-request', bodyParser.json({ limit: '50mb' }), (req, res) => {
let mock = nock(req.headers.origin)
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
},
"scripts": {
"build": "ember build",
"install": "cd node_modules/comlink && npm install && npm run build",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does comlink need to be compiled? Does this have any negatives or gotchas?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the compile step is necessary when using the git repo source. Comlink’s written in TypeScript, and gets compiled to JS when published to npm, so here we need to do it on our own.

It’s just a temporary solution for sharing this work-in-progress. I wouldn’t even try publishing ECFT with it. 😅

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah cool, good to know

"lint:js": "eslint .",
"start": "ember serve",
"test": "ember test",
"test:all": "ember try:each"
},
"dependencies": {
"body-parser": "^1.18.3",
"comlink": "CvX/comlink#wrap-chain",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you added some stuff here to get the chaning working. Should we wait for them to merge it or depend on this branch?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continuing from the previous comment: I wouldn’t use this setup. I’d either wait for a comlink release or, in case that gets stuck, I’d push a fork of comlink with chain method calls to npm.

"ember-auto-import": "^1.2.15",
"ember-cli-babel": "^6.6.0",
"fastboot": "^1.2.1",
Expand Down
4 changes: 2 additions & 2 deletions tests/fastboot/network-mocking-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ module('Fastboot | network mocking', function(hooks) {

test('it can mock a get request', async function(assert) {
await mockServer.get('/api/notes', [
{ id: 1, title: 'get note'},
{ id: 1, title: 'get note' },
]);

await visit('/examples/network/other/get-request');
Expand All @@ -79,7 +79,7 @@ module('Fastboot | network mocking', function(hooks) {

test('it can mock a post request', async function(assert) {
await mockServer.post('/api/notes', [
{ id: 1, title: 'post note'},
{ id: 1, title: 'post note' },
]);

await visit('/examples/network/other/post-request');
Expand Down
95 changes: 95 additions & 0 deletions tests/fastboot/nock-proxy-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { module, test } from 'qunit';
import { setup, visit, nock } from 'ember-cli-fastboot-testing/test-support';

module('Fastboot | nock proxy', function(hooks) {
setup(hooks);

test('it will not change an endpoint that already exists', async function(assert) {
await visit('/examples/network/other/echo?message=hello%20world');
assert.dom('[data-test-id="echo"]').hasText("hello world");
});

test('it can mock an array of models', async function(assert) {
await nock('http://localhost:7357')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to auto detect the local hostname and not require it here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could set the hostname in the application adapter, and use that here. It wouldn’t then change when, for example, using a different testing port.

.intercept('/api/notes', 'GET')
.reply(200, {
data: [
{
type: 'note',
id: '1',
attributes: {
title: 'test note'
}
},
{
type: 'note',
id: '2',
attributes: {
title: 'test 2'
}
}
]
});

await visit('/examples/network/notes');

assert.dom('[data-test-id="title-1"]').hasText("test note")
assert.dom('[data-test-id="title-2"]').hasText("test 2")
});

test('it can mock a single model', async function(assert) {
await nock('http://localhost:7357')
.intercept('/api/notes/1', 'GET')
.reply(200, {
data: {
type: "notes",
id: "1",
attributes: {
title: 'test note'
}
}
});

await visit('/examples/network/notes/1');

assert.dom('[data-test-id="title"]').hasText("test note");
});

test('it can mock 404s', async function(assert) {
await nock('http://localhost:7357')
.intercept('/api/notes/1', 'GET')
.reply(404, {
errors: [
{ title: "Not found" }
]
});

await visit('/examples/network/notes/1');

assert.dom().includesText('Ember Data Request GET /api/notes/1 returned a 404');
});

test('it can mock a get request', async function(assert) {
await nock('http://localhost:7357')
.intercept('/api/notes', 'GET')
.reply(200, [
{ id: 1, title: 'get note' },
]);

await visit('/examples/network/other/get-request');

assert.dom('[data-test-id="title-1"]').hasText("get note")
});

test('it can mock a post request', async function(assert) {
await nock('http://localhost:7357')
.intercept('/api/notes', 'POST')
.reply(200, [
{ id: 1, title: 'post note' },
]);

await visit('/examples/network/other/post-request');

assert.dom('[data-test-id="title-1"]').hasText("post note")
});
});
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3663,6 +3663,10 @@ combined-stream@~0.0.4:
dependencies:
delayed-stream "0.0.5"

comlink@CvX/comlink#wrap-chain:
version "4.0.1"
resolved "https://codeload.github.com/CvX/comlink/tar.gz/f7913e3a02905389eeee4fda441356f3dbb19688"

[email protected]:
version "2.12.2"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555"
Expand Down