pretenderjs / pretender

A mock server library with a nice routing DSL
MIT License
1.26k stars 157 forks source link

node.js support #192

Open taras opened 7 years ago

taras commented 7 years ago

ember-ajax team is looking for a way to test the addon on node.js. We're using Pretender to stub XMLHttpRequest in the browser. In node.js, there is no XMLHttpRequest. nock stubs http.ClientRequest to provide behaviour similar to what Pretender offers in the browser.

If it was possible to use Pretender on node, then we could use Pretender to test app in Fastboot mode.

What are your thoughts about node.js support for this library?

trek commented 7 years ago

I'm down. I'd personally design this in an "adapter" style, with the default adapter being "Browser" or maybe "XMLHttpRequest"

taras commented 7 years ago

Great. We'll start experimenting with it. I'll create a WIP PR and we'll go from there.

stefanpenner commented 7 years ago

@taras did you make any progress?

I believe we use for fastboot: https://github.com/najaxjs/najax/blob/master/lib/najax.js

taras commented 7 years ago

@stefanpenner We didn't end up getting round to it. I got busy and we didn't pick it up.

stefanpenner commented 7 years ago

Ok.

stefanpenner commented 7 years ago

i wonder if we can just provide our own XMLHTTPRequest global in fastboot, and then most of this stuff would "just work".

taras commented 7 years ago

That's an interesting idea. We might be able to use this https://www.npmjs.com/package/xmlhttprequest

stefanpenner commented 7 years ago

some trickier will be required to deal with najax or ember datas stuff.

izelnakri commented 6 years ago

Hi @stefanpenner, I'm able to simulate and run pretender in node by using the packages below:

require('jsdom-global')();
window.FakeXMLHttpRequest = require('fake-xml-http-request');
window.RouteRecognizer = require('route-recognizer');
window.$ = require("jquery");

require('pretender');

var PHOTOS = {
  "10": {
    id: 10,
    src: 'http://media.giphy.com/media/UdqUo8xvEcvgA/giphy.gif'
  },
  "42": {
    id: 42,
    src: 'http://media0.giphy.com/media/Ko2pyD26RdYRi/giphy.gif'
  }
};

var server = new window.Pretender(function(){
  this.get('/photos', function(request){
    var all =  JSON.stringify(Object.keys(PHOTOS).map((k) => PHOTOS[k]));
    return [200, {"Content-Type": "application/json"}, all];
  });

  this.get('/photos/:id', function(request){
    return [200, {"Content-Type": "application/json"}, JSON.stringify(PHOTOS[request.params.id])];
  });
});

server.handledRequest = function(verb, path, request) {
  console.log("a request was responded to", verb, path, request);
}

server.unhandledRequest = function(verb, path, request) {
  console.log("what is this I don't even...");
}

window.$.getJSON('/photos/12', (a) => {
  console.log(a);
}).done(function() {
  console.log('second success');
}).fail(function() {
  console.log('error');
});

Pretender is able to intercept the requests, however the request.params are not parsed correctly, therefore server responds with empty body/error. I believe there is a problem with route-recognizer addon.

This is an important issue because, I was trying to run ember-fastboot with mirage and I get 404s during the broccoli/node environment, while the browser environment of the mirage server runs correctly. Considering the fact that most serious ember projects use mirage during development, enabling pretender in node.js should open new possibilities for ember fastboot.

izelnakri commented 6 years ago

I've found a problem with the parseURL() function:

parseURL('/photos/12').fullpath returns '/' for the node environment above..

Also the parseURL function seems to be very hard to read/maintain and probably quirky.

izelnakri commented 6 years ago

I found a hacky way to fix this, however the whole thing was an ordeal:

const JSDOM = require('jsdom').JSDOM;
const dom = new JSDOM(`<p>Hello</p>`, { url: 'http://localhost' });

global.window = dom.window;
global.document = window.document;

window.FakeXMLHttpRequest = require('fake-xml-http-request');
window.RouteRecognizer = require('route-recognizer');
window.$ = require("jquery");

window.self = window;
global.self = window.self;

require('pretender');

let PHOTOS = {
  '10': {
    id: 10,
    src: 'http://media.giphy.com/media/UdqUo8xvEcvgA/giphy.gif'
  },
  '42': {
    id: 42,
    src: 'http://media0.giphy.com/media/Ko2pyD26RdYRi/giphy.gif'
  }
};

let server = new window.Pretender(function(){
  this.get('/photos', (request) => {
    var all =  JSON.stringify(Object.keys(PHOTOS).map((k) => PHOTOS[k]));
    return [200, {'Content-Type': 'application/json'}, all];
  });

  this.get('/photos/:id', (request) => {
    return [200, {'Content-Type': 'application/json'}, JSON.stringify(PHOTOS[request.params.id])];
  });
});

server.handledRequest = function(verb, path, request) {
  console.log('a request was responded to', verb, path);
}

server.unhandledRequest = function(verb, path, request) {
  console.log('[UNHANDLED REQUEST]', verb, path);
  console.log('REQUEST:');
  console.log(request);
}

window.$.getJSON('/photos/10', (a) => {
  console.log(a);
}).done(function() {
  console.log('second success');
}).fail(function() {
  console.log('error');
});

So the actual fix was this line:

const dom = new JSDOM(`<p>Hello</p>`, { url: 'http://localhost' }); 

when JSDOM receives no url, then window.location.host is '' and the parseURL() function breaks.