Setup Pretender for tests
jorgelainfiesta opened this issue · 22 comments
We need to be able to run Pretender in Glimmer tests but I cannot figure out how to import the package in Typescript. I ran out of ideas on how to address this issue, my problem is reported here: pretenderjs/pretender#229
This issue has become a big blocker because I cannot write the tests for all the other features without this setup. Any ideas on alternatives or any work around that could work?
@Turbo87: can you spend some time with @jorgelainfiesta on this this week and try and figure this out?
I am unsure why we would use Pretender at all. Pretender only works with XMLHttpRequest AFAIK, and I would assume that for such a futuristic project we would use native fetch()
instead?!
That's indeed a good point. I somehow assumed Pretender supported fetch
. There is https://github.com/sstur/fetch-pretender but we should probably check what other alternatives there are. If there's not we could of course force a Polyfill when testing which would allow us to use Pretender again…
Orbit uses fetch
indeed. However, you have to manually bind it to the window's fetch
(orbitjs/orbit#452). I think we could bind it to the polyfill for testing.
@jorgelainfiesta: did you check whether there is anything like Pretender that supports fetch
though?
I hadn't but I'm researching now. From early results it seems like fetch-mock is quite popular. It basically allows you to provide a response per matcher, quite similar to Pretender.
Another alternative, popular in Jest, is to completely override the fetch
function with a mock function. I assume this is more popular in unit tests because it'd be quite troublesome to test a feature that calls different endpoints with this approach.
fetch-mock
LGTM on first glance. I'd like to avoid mocking fetch
by overriding it as that'll be more effort to maintain and likely also more brittle.
Awesome! I'll retake the tests tasks now.
Update: fetch-mock
internally uses a babel plugin called transform-runtime
for polyfills. However, this kind of configuration clashes with glimmer's use of rollup
. The error I get literally says It looks like your Babel configuration specifies a module transformer. Please disable it.
.
fetch
?
I'm evaluating jest-fetch-mock. It doesn't seem to have a jest specific dependency. However, it's also a mock of the fetch function with some helpers. No matchers are used here.
Update: I also had build problems with jest-fetch-mock
. However, I found some configurations that are possible on the build process that might enable us to use this addon or maybe even Pretender.
I'm experimenting with this configurations. So far I only get a cryptic error when adding this options to the rollup configuration. It simply says parse is not a function
.
I'm transcribing some hints I got from Toran Billups so they don't get lost in Ember's community slack.
I've had success wrapping fetch w/ a simple es2015 module that I can monkey patch for tests
so in my app code I doimport fetch from 'my/fetch';
in that module^ I do something like this
import fetch from 'fetch';
const _private = {
_fetch(url) {
return fetch(url);
}
}
export default function(url) {
return _private._fetch(url).then((response) => {
if (response.ok) {
return response;
}
throw response;
});
}
export { _private };
then in your tests you can write a simple helper to tap into that _fetch and return whatev you want
import { _private } from 'my/flash';
const patchFetch = function patchFetch(callback) {
const origFunc = _private._fetch;
_private._fetch = function() {
try {
callback(arguments[0]);
} finally {
_private._fetch = origFunc;
}
return origFunc.apply(this, arguments);
};
};
the funky
_private
object you see above^ is required here because anything exported from an es2015 module isreadonly
meaning you can't alter it like you see inside thepatchFetch
function
that little helper^ would allow for simple fetch stubbing from component integration/ or unit tests
here is a quick look at how I'm using the patch method today (just simple url path verification mostly but could be used to return a promise w/ json if needed)
patchFetch((url) => {
assert.equal(url, '/foobar');
});
Update: the most promising solution seemed to mock Orbit's fetch
in the test. Each component that loads data has to be given a store instance by the entry component. So we could also mock the Store in the test.
While implementing this solution, we came across a huge limitation in Glimmer's testing story: you cannot bind context variables when rendering a component in a test (glimmerjs/glimmer.js#14). We'l have to skip tests for now.
As a reference for the future, below is a draft of how the test could've looked if it wasn't because of this problem:
import hbs from '@glimmer/inline-precompile';
import { setupRenderingTest } from '@glimmer/test-helpers';
import Orbit from '@orbit/data';
import setupStore from '../../../utils/data/setup-store';
const { module, test } = QUnit;
const locations = [
{
id: 1,
type: 'location',
attributes: {
'city': 'København',
'coordinates': '55.676098, 12.568337',
'country': 'Denmark',
'last-updated': '2017-03-06'
}
},
{
id: 2,
type: 'location',
attributes: {
'city': 'Salzburg',
'country': 'Austria',
'last-updated': '2017-03-07',
'coordinates': '47.811195, 13.033229'
}
}
];
module('Component: Home', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
Orbit.fetch = () => {
return new Promise((resolve, reject) => {
resolve({
data: locations
});
});
};
this.store = setupStore();
await this.render(hbs`<Home store={{store}} />`);
let location1 = this.containerElement.querySelector('.test__location-1');
assert.ok(!!location1, 'Location 1 loaded');
let location2 = this.containerElement.querySelector('.test__location-2');
assert.ok(!!location2, 'Location 2 loaded');
});
});
have you tested using this.set('store', setupStore())
instead?
this.set
doesn't exist anymore in Glimmer.
yeah, but the testing context is not aware of Glimmer afaik
although I just noticed that you're importing from @glimmer/test-helpers
, so maybe I'm wrong... no idea how that package works
I did try but I get an error saying that this.set
is not a function. You can actually see that this feature is not yet implemented in glimmer testing: glimmerjs/glimmer.js#14
Since Glimmer doesn't even allow proper testing at this stage and has quite a few other issues I'm wondering if this is the right solution at this point. Given blog posts like https://madhatted.com/2017/6/16/building-a-progressive-web-app-with-ember it might be a lot easier to build the PWA with Ember.js instead
I think building this app with Ember would be way easier. But I think the intention of using Glimmer and Orbit etc, is to show that we can handle the edgy technologies?
Yes, the idea is to use the latest and (to-be) greatest - basically showing off lightest/fastest etc. possible. So for now Ember is not an option here.