Sebastian Häni 39266654e6 fix(http): queue jsonp <script> tag onLoad event handler in microtask (#39512)
Before this change, when trying to load a JSONP script that calls the JSONP callback inside a
microtask, it will fail in Internet Explorer 11 and EdgeHTML. This commit changes the onLoad cleanup
to be queued after the loaded endpoint executed any potential microtask itself. This ensures that
the aforementioned browsers will first evaluate the loaded script calling the JSONP callback and
only then run the cleanup inside onLoad.

Fixes #39496

PR Close #39512
2020-11-17 13:09:08 -08:00

86 lines
3.0 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.sonpCallbackContext
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {JSONP_ERR_NO_CALLBACK, JSONP_ERR_WRONG_METHOD, JSONP_ERR_WRONG_RESPONSE_TYPE, JsonpClientBackend} from '@angular/common/http/src/jsonp';
import {HttpRequest} from '@angular/common/http/src/request';
import {HttpErrorResponse, HttpEventType} from '@angular/common/http/src/response';
import {ddescribe, describe, it} from '@angular/core/testing/src/testing_internal';
import {toArray} from 'rxjs/operators';
import {MockDocument} from './jsonp_mock';
function runOnlyCallback(home: any, data: Object) {
const keys = Object.keys(home);
expect(keys.length).toBe(1);
const callback = home[keys[0]];
delete home[keys[0]];
callback(data);
}
const SAMPLE_REQ = new HttpRequest<never>('JSONP', '/test');
{
describe('JsonpClientBackend', () => {
let home = {};
let document: MockDocument;
let backend: JsonpClientBackend;
beforeEach(() => {
home = {};
document = new MockDocument();
backend = new JsonpClientBackend(home, document);
});
it('handles a basic request', done => {
backend.handle(SAMPLE_REQ).pipe(toArray()).subscribe(events => {
expect(events.map(event => event.type)).toEqual([
HttpEventType.Sent,
HttpEventType.Response,
]);
done();
});
runOnlyCallback(home, {data: 'This is a test'});
document.mockLoad();
});
// Issue #39496
it('handles a request with callback call wrapped in promise', done => {
backend.handle(SAMPLE_REQ).subscribe(() => {
done();
});
Promise.resolve().then(() => {
runOnlyCallback(home, {data: 'This is a test'});
});
document.mockLoad();
});
it('handles an error response properly', done => {
const error = new Error('This is a test error');
backend.handle(SAMPLE_REQ).pipe(toArray()).subscribe(undefined, (err: HttpErrorResponse) => {
expect(err.status).toBe(0);
expect(err.error).toBe(error);
done();
});
document.mockError(error);
});
describe('throws an error', () => {
it('when request method is not JSONP',
() => expect(() => backend.handle(SAMPLE_REQ.clone<never>({method: 'GET'})))
.toThrowError(JSONP_ERR_WRONG_METHOD));
it('when response type is not json',
() => expect(() => backend.handle(SAMPLE_REQ.clone<never>({responseType: 'text'})))
.toThrowError(JSONP_ERR_WRONG_RESPONSE_TYPE));
it('when callback is never called', done => {
backend.handle(SAMPLE_REQ).subscribe(undefined, (err: HttpErrorResponse) => {
expect(err.status).toBe(0);
expect(err.error instanceof Error).toEqual(true);
expect(err.error.message).toEqual(JSONP_ERR_NO_CALLBACK);
done();
});
document.mockLoad();
});
});
});
}