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

Integration with ember-cli-mirage #53

Closed
allthesignals opened this issue May 25, 2018 · 7 comments
Closed

Integration with ember-cli-mirage #53

allthesignals opened this issue May 25, 2018 · 7 comments

Comments

@allthesignals
Copy link
Contributor

allthesignals commented May 25, 2018

I would like to setup ember-cli-mirage such that all XHR requests emitted from core mapbox-gl work with the Mirage passthrough API.

Here's what I've found so far:

  1. MapboxGL's request for the style JSON object throws an "AJAXError (200)" through PretenderJS and one of its dependencies, FakeXMLHttpRequest.
  2. Addressing (but maybe not solving) 1 gets us past the XHR request, but lands us into another issue: style JSON appears to be requested twice, throwing a Mapbox error: "There is already a source with this ID." (Full stack trace below).

image

This is a pretty open-ended issue because I'm not sure where to look next. I don't understand why MapboxGL would be request the style JSON twice. Is this because of how Pretender's FakeXHR object is structured? Or is something happening in Ember Mirage?

You can take a peek at how I landed with this at this branch/commit here: allthesignals/ember-mirage-mapbox-example@ce98473. yarn and ember s and errors will be right there in console.

Thanks!

Full stack trace

server.js:22 Passthrough request: GET https://api.mapbox.com/styles/v1/mapbox/dark-v9?access_token=pk.eyJ1IjoibWF0dGdhcmRuZXJueWMiLCJhIjoiY2o4NTk2dTlzMGFjZDMydWRtY3NsYnFjZSJ9.lA_A4IHM2EGToFiPKFzSug
server.js:22 Passthrough request: GET https://api.mapbox.com/v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7.json?secure&access_token=pk.eyJ1IjoibWF0dGdhcmRuZXJueWMiLCJhIjoiY2o4NTk2dTlzMGFjZDMydWRtY3NsYnFjZSJ9.lA_A4IHM2EGToFiPKFzSug
server.js:22 Passthrough request: GET https://api.mapbox.com/styles/v1/mapbox/dark-v9/[email protected]?access_token=pk.eyJ1IjoibWF0dGdhcmRuZXJueWMiLCJhIjoiY2o4NTk2dTlzMGFjZDMydWRtY3NsYnFjZSJ9.lA_A4IHM2EGToFiPKFzSug
server.js:22 Passthrough request: GET https://api.mapbox.com/styles/v1/mapbox/dark-v9/[email protected]?access_token=pk.eyJ1IjoibWF0dGdhcmRuZXJueWMiLCJhIjoiY2o4NTk2dTlzMGFjZDMydWRtY3NsYnFjZSJ9.lA_A4IHM2EGToFiPKFzSug
mapbox-gl-dev.js:27626 Uncaught Error: There is already a source with this ID
at Style.addSource (mapbox-gl-dev.js:27626)
at Style._load (mapbox-gl-dev.js:27361)
at mapbox-gl-dev.js:27332
at FakeRequest.xhr.onload (mapbox-gl-dev.js:39703)
at dispatchEvent (pretender.js:223)
at XMLHttpRequest.xhr.(:4200/anonymous function) (http://localhost:4200/assets/vendor.js:108440:9)
addSource @ mapbox-gl-dev.js:27626
_load @ mapbox-gl-dev.js:27361
(anonymous) @ mapbox-gl-dev.js:27332
xhr.onload @ mapbox-gl-dev.js:39703
dispatchEvent @ pretender.js:223
xhr.(anonymous function) @ pretender.js:231
load (async)
createHandler @ pretender.js:229
createPassthrough @ pretender.js:247
send @ pretender.js:171
exports.getJSON @ mapbox-gl-dev.js:39708
loadURL @ mapbox-gl-dev.js:27328
setStyle @ mapbox-gl-dev.js:37965
Map @ mapbox-gl-dev.js:37369
_setup @ mapbox-gl.js:49
invoke @ backburner.js:205
flush @ backburner.js:125
flush @ backburner.js:278
end @ backburner.js:410
run @ backburner.js:760
join @ backburner.js:736
join @ backburner.js:477
run.join @ ember-metal.js:4366
(anonymous) @ ember-metal.js:4441
mightThrow @ jquery.js:3534
process @ jquery.js:3602
setTimeout (async)
(anonymous) @ jquery.js:3640
fire @ jquery.js:3268
fireWith @ jquery.js:3398
fire @ jquery.js:3406
fire @ jquery.js:3268
fireWith @ jquery.js:3398
ready @ jquery.js:3878
completed @ jquery.js:3888
mapbox-gl-dev.js:2349 Uncaught AssertionError {name: "AssertionError", actual: false, expected: true, operator: "==", message: "false == true", …}actual: falseexpected: truegeneratedMessage: truemessage: "false == true"name: "AssertionError"operator: "=="stack: "AssertionError: false == true↵ at ImageManager.addImage (http://localhost:4200/assets/vendor.js:78787:7)↵ at http://localhost:4200/assets/vendor.js:91040:45↵ at maybeComplete (http://localhost:4200/assets/vendor.js:89998:13)↵ at http://localhost:4200/assets/vendor.js:89974:13↵ at Image.img.onload (http://localhost:4200/assets/vendor.js:103425:17)"__proto
: Error
fail @ mapbox-gl-dev.js:2349
ok @ mapbox-gl-dev.js:2369
addImage @ mapbox-gl-dev.js:15117
(anonymous) @ mapbox-gl-dev.js:27370
maybeComplete @ mapbox-gl-dev.js:26328
(anonymous) @ mapbox-gl-dev.js:26304
img.onload @ mapbox-gl-dev.js:39755
load (async)
(anonymous) @ mapbox-gl-dev.js:39754
xhr.onload @ mapbox-gl-dev.js:39724
dispatchEvent @ pretender.js:223
xhr.(anonymous function) @ pretender.js:231
load (async)
createHandler @ pretender.js:229
createPassthrough @ pretender.js:247
send @ pretender.js:171
exports.getArrayBuffer @ mapbox-gl-dev.js:39733
exports.getImage @ mapbox-gl-dev.js:39748
module.exports @ mapbox-gl-dev.js:26300
_load @ mapbox-gl-dev.js:27365
(anonymous) @ mapbox-gl-dev.js:27332
xhr.onload @ mapbox-gl-dev.js:39703
(anonymous) @ fake_xml_http_request.js:137
dispatchEvent @ fake_xml_http_request.js:181
dispatchEvent @ pretender.js:221
xhr.(anonymous function) @ pretender.js:231
load (async)
createHandler @ pretender.js:229
createPassthrough @ pretender.js:247
send @ pretender.js:171
exports.getJSON @ mapbox-gl-dev.js:39708
loadURL @ mapbox-gl-dev.js:27328
setStyle @ mapbox-gl-dev.js:37965
Map @ mapbox-gl-dev.js:37369
_setup @ mapbox-gl.js:49
invoke @ backburner.js:205
flush @ backburner.js:125
flush @ backburner.js:278
end @ backburner.js:410
_run @ backburner.js:760
_join @ backburner.js:736
join @ backburner.js:477
run.join @ ember-metal.js:4366
(anonymous) @ ember-metal.js:4441
mightThrow @ jquery.js:3534
process @ jquery.js:3602
setTimeout (async)
(anonymous) @ jquery.js:3640
fire @ jquery.js:3268
fireWith @ jquery.js:3398
fire @ jquery.js:3406
fire @ jquery.js:3268
fireWith @ jquery.js:3398
ready @ jquery.js:3878
completed @ jquery.js:3888
server.js:22 Passthrough request: GET https://api.mapbox.com/fonts/v1/mapbox/DIN Offc Pro Medium,Arial Unicode MS Regular/0-255.pbf?access_token=pk.eyJ1IjoibWF0dGdhcmRuZXJueWMiLCJhIjoiY2o4NTk2dTlzMGFjZDMydWRtY3NsYnFjZSJ9.lA_A4IHM2EGToFiPKFzSug
server.js:22 Passthrough request: GET https://api.mapbox.com/fonts/v1/mapbox/DIN Offc Pro Regular,Arial Unicode MS Regular/0-255.pbf?access_token=pk.eyJ1IjoibWF0dGdhcmRuZXJueWMiLCJhIjoiY2o4NTk2dTlzMGFjZDMydWRtY3NsYnFjZSJ9.lA_A4IHM2EGToFiPKFzSug
mapbox-gl-dev.js:14878 Uncaught TypeError: Cannot read property 'stack' of undefined
at mapbox-gl-dev.js:14878
at mapbox-gl-dev.js:42151
at mapbox-gl-dev.js:14866
at mapbox-gl-dev.js:14856
at mapbox-gl-dev.js:26267
at XMLHttpRequest.xhr.onload (mapbox-gl-dev.js:39724)
at FakeRequest. (fake_xml_http_request.js:137)
at FakeRequest.dispatchEvent (fake_xml_http_request.js:181)
at dispatchEvent (pretender.js:221)
at XMLHttpRequest.xhr.(:4200/anonymous function) (http://localhost:4200/assets/vendor.js:108440:9)
(anonymous) @ mapbox-gl-dev.js:14878
(anonymous) @ mapbox-gl-dev.js:42151
(anonymous) @ mapbox-gl-dev.js:14866
(anonymous) @ mapbox-gl-dev.js:14856
(anonymous) @ mapbox-gl-dev.js:26267
xhr.onload @ mapbox-gl-dev.js:39724
(anonymous) @ fake_xml_http_request.js:137
dispatchEvent @ fake_xml_http_request.js:181
dispatchEvent @ pretender.js:221
xhr.(anonymous function) @ pretender.js:231
load (async)
createHandler @ pretender.js:229
createPassthrough @ pretender.js:247
send @ pretender.js:171
exports.getArrayBuffer @ mapbox-gl-dev.js:39733
module.exports @ mapbox-gl-dev.js:26255
(anonymous) @ mapbox-gl-dev.js:14846
(anonymous) @ mapbox-gl-dev.js:42148
exports.asyncAll @ mapbox-gl-dev.js:42147
getGlyphs @ mapbox-gl-dev.js:14813
getGlyphs @ mapbox-gl-dev.js:28243
receive @ mapbox-gl-dev.js:39605

@allthesignals
Copy link
Contributor Author

When I set breakpoints in the Style.addSource prototype, the first time it's called, ID is "composite", second time, "composite".

@allthesignals
Copy link
Contributor Author

allthesignals commented May 25, 2018

In mapbox-gl, this gets called twice with the same data:

        __chunk1_js.getJSON(request, function (error, json) {
            if (error) {
                this$1.fire(new __chunk1_js.ErrorEvent(error));
            } else if (json) {
                this$1._load((json     ), validate);
            }
        });

So, I think somehow this callback isn't working because PretenderJS is doing something else? Not sure.

@kturney
Copy link
Owner

kturney commented May 25, 2018

if you put a breakpoint here https://github.com/pretenderjs/FakeXMLHttpRequest/blob/master/src/fake-xml-http-request.js#L173, are there multiple listeners? I'm wondering if somehow the callback is getting doubled up

@allthesignals
Copy link
Contributor Author

Hmm I checked - the length of the listeners array is always either 0 or 1

@allthesignals
Copy link
Contributor Author

allthesignals commented Jun 6, 2018

This seems to work:

EDIT:
Here's where I actually landed, still very sloppy, but it does not break tests:

Cache the native XMLHttpRequest here:
https://github.com/NYCPlanning/labs-applicantmaps/blob/develop/app/app.js#L6

Before a request is made, swap in the FakeXMLHttpRequest:
https://github.com/NYCPlanning/labs-applicantmaps/blob/develop/app/adapters/application.js#L6-L8

Use the mapbox transformRequest hook to swap in the native XMLHttpRequest:
https://github.com/NYCPlanning/labs-applicantmaps/blob/develop/app/components/mapbox-gl.js#L15-L20

Create an initializer to cache the XMLHttpRequest:
https://github.com/NYCPlanning/labs-applicantmaps/blob/31e4cfc5ceaf63e6d12ed3a22de83870c9246a54/app/initializers/fake-xml-http-req.js#L2-L4

@kturney
Copy link
Owner

kturney commented Jun 14, 2018

in my app I've ended up with this monkeypatch that I call as the first line in my mirage config function. Seems to be working.

// this can be removed if https://github.com/pretenderjs/FakeXMLHttpRequest/pull/32 is ever merged
// and maybe also if a double calling of load event onload is fixed?

export default () => {
  // Note: the below XMLHttpRequest has already been converted to a FakeXMLHttpRequest by pretender

  const origSend = window.XMLHttpRequest.prototype.send;
  window.XMLHttpRequest.prototype.send = function send() {
    origSend.apply(this, arguments);

    const fakeXhr = this; // eslint-disable-line consistent-this
    const realXhr = this._passthroughRequest;
    if (realXhr) {
      realXhr.onload = function(event) {
        if (fakeXhr.responseType !== 'arraybuffer') {
          fakeXhr.response = realXhr.response;
        }

        // dispatch event instead of calling the original to prevent a double call bug
        fakeXhr.dispatchEvent(event);
      };
    }
  };
};

@allthesignals
Copy link
Contributor Author

@kturney muuuuch smarter! Target the send method, of course. Nice. I'll give this a try.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants