Skip to content

Commit 76a9abb

Browse files
Brooooooklynjayphelps
authored andcommitted
fix(Observable/Ajax): mount properties to origin readystatechange fn (#2025)
1 parent f94ceb9 commit 76a9abb

File tree

3 files changed

+188
-16
lines changed

3 files changed

+188
-16
lines changed

spec/helpers/ajax-helper.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ export class MockXMLHttpRequest {
135135
requestHeaders: any = {};
136136
withCredentials: boolean = false;
137137

138+
onreadystatechange: (e: ProgressEvent) => any;
139+
onerror: (e: ErrorEvent) => any;
140+
onprogress: (e: ProgressEvent) => any;
141+
ontimeout: (e: ProgressEvent) => any;
142+
upload: XMLHttpRequestUpload;
143+
138144
constructor() {
139145
this.previousRequest = MockXMLHttpRequest.recentRequest;
140146
MockXMLHttpRequest.recentRequest = this;

spec/observables/dom/ajax-spec.ts

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,4 +598,166 @@ describe('Observable.ajax', () => {
598598
});
599599

600600
});
601+
602+
it('should work fine when XMLHttpRequest onreadystatechange property is monkey patched', function() {
603+
Object.defineProperty(root.XMLHttpRequest.prototype, 'onreadystatechange', {
604+
set: function (fn: (e: ProgressEvent) => any) {
605+
const wrapFn = (ev: ProgressEvent) => {
606+
const result = fn.call(this, ev);
607+
if (result === false) {
608+
ev.preventDefault();
609+
}
610+
};
611+
this['_onreadystatechange'] = wrapFn;
612+
},
613+
get() {
614+
return this['_onreadystatechange'];
615+
},
616+
configurable: true
617+
});
618+
619+
Rx.Observable.ajax({
620+
url: '/flibbertyJibbet'
621+
})
622+
.subscribe();
623+
624+
const request = MockXMLHttpRequest.mostRecent;
625+
expect(() => {
626+
request.onreadystatechange((<any>'onreadystatechange'));
627+
}).not.throw();
628+
629+
delete root.XMLHttpRequest.prototype.onreadystatechange;
630+
});
631+
632+
it('should work fine when XMLHttpRequest ontimeout property is monkey patched', function() {
633+
Object.defineProperty(root.XMLHttpRequest.prototype, 'ontimeout', {
634+
set: function (fn: (e: ProgressEvent) => any) {
635+
const wrapFn = (ev: ProgressEvent) => {
636+
const result = fn.call(this, ev);
637+
if (result === false) {
638+
ev.preventDefault();
639+
}
640+
};
641+
this['_ontimeout'] = wrapFn;
642+
},
643+
get() {
644+
return this['_ontimeout'];
645+
},
646+
configurable: true
647+
});
648+
649+
const ajaxRequest = {
650+
url: '/flibbertyJibbet'
651+
};
652+
653+
Rx.Observable.ajax(ajaxRequest)
654+
.subscribe();
655+
656+
const request = MockXMLHttpRequest.mostRecent;
657+
try {
658+
request.ontimeout((<any>'ontimeout'));
659+
} catch (e) {
660+
expect(e.message).to.equal(new Rx.AjaxTimeoutError((<any>request), ajaxRequest).message);
661+
}
662+
delete root.XMLHttpRequest.prototype.ontimeout;
663+
});
664+
665+
it('should work fine when XMLHttpRequest onprogress property is monkey patched', function() {
666+
Object.defineProperty(root.XMLHttpRequest.prototype, 'onprogress', {
667+
set: function (fn: (e: ProgressEvent) => any) {
668+
const wrapFn = (ev: ProgressEvent) => {
669+
const result = fn.call(this, ev);
670+
if (result === false) {
671+
ev.preventDefault();
672+
}
673+
};
674+
this['_onprogress'] = wrapFn;
675+
},
676+
get() {
677+
return this['_onprogress'];
678+
},
679+
configurable: true
680+
});
681+
682+
Object.defineProperty(root.XMLHttpRequest.prototype, 'upload', {
683+
get() {
684+
return true;
685+
},
686+
configurable: true
687+
});
688+
689+
// mock for onprogress
690+
root.XDomainRequest = true;
691+
692+
Rx.Observable.ajax({
693+
url: '/flibbertyJibbet',
694+
progressSubscriber: (<any>{
695+
next: () => {
696+
// noop
697+
},
698+
error: () => {
699+
// noop
700+
},
701+
complete: () => {
702+
// noop
703+
}
704+
})
705+
})
706+
.subscribe();
707+
708+
const request = MockXMLHttpRequest.mostRecent;
709+
710+
expect(() => {
711+
request.onprogress((<any>'onprogress'));
712+
}).not.throw();
713+
714+
delete root.XMLHttpRequest.prototype.onprogress;
715+
delete root.XMLHttpRequest.prototype.upload;
716+
delete root.XDomainRequest;
717+
});
718+
719+
it('should work fine when XMLHttpRequest onerror property is monkey patched', function() {
720+
Object.defineProperty(root.XMLHttpRequest.prototype, 'onerror', {
721+
set: function (fn: (e: ProgressEvent) => any) {
722+
const wrapFn = (ev: ProgressEvent) => {
723+
const result = fn.call(this, ev);
724+
if (result === false) {
725+
ev.preventDefault();
726+
}
727+
};
728+
this['_onerror'] = wrapFn;
729+
},
730+
get() {
731+
return this['_onerror'];
732+
},
733+
configurable: true
734+
});
735+
736+
Object.defineProperty(root.XMLHttpRequest.prototype, 'upload', {
737+
get() {
738+
return true;
739+
},
740+
configurable: true
741+
});
742+
743+
// mock for onprogress
744+
root.XDomainRequest = true;
745+
746+
Rx.Observable.ajax({
747+
url: '/flibbertyJibbet'
748+
})
749+
.subscribe();
750+
751+
const request = MockXMLHttpRequest.mostRecent;
752+
753+
try {
754+
request.onerror((<any>'onerror'));
755+
} catch (e) {
756+
expect(e.message).to.equal('ajax error');
757+
}
758+
759+
delete root.XMLHttpRequest.prototype.onerror;
760+
delete root.XMLHttpRequest.prototype.upload;
761+
delete root.XDomainRequest;
762+
});
601763
});

src/observable/dom/AjaxObservable.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -293,39 +293,42 @@ export class AjaxSubscriber<T> extends Subscriber<Event> {
293293
private setupEvents(xhr: XMLHttpRequest, request: AjaxRequest) {
294294
const progressSubscriber = request.progressSubscriber;
295295

296-
xhr.ontimeout = function xhrTimeout(e) {
296+
function xhrTimeout(e: ProgressEvent) {
297297
const {subscriber, progressSubscriber, request } = (<any>xhrTimeout);
298298
if (progressSubscriber) {
299299
progressSubscriber.error(e);
300300
}
301301
subscriber.error(new AjaxTimeoutError(this, request)); //TODO: Make betterer.
302302
};
303-
(<any>xhr.ontimeout).request = request;
304-
(<any>xhr.ontimeout).subscriber = this;
305-
(<any>xhr.ontimeout).progressSubscriber = progressSubscriber;
306-
303+
xhr.ontimeout = xhrTimeout;
304+
(<any>xhrTimeout).request = request;
305+
(<any>xhrTimeout).subscriber = this;
306+
(<any>xhrTimeout).progressSubscriber = progressSubscriber;
307307
if (xhr.upload && 'withCredentials' in xhr && root.XDomainRequest) {
308308
if (progressSubscriber) {
309-
xhr.onprogress = function xhrProgress(e) {
309+
let xhrProgress: (e: ProgressEvent) => void;
310+
xhrProgress = function(e: ProgressEvent) {
310311
const { progressSubscriber } = (<any>xhrProgress);
311312
progressSubscriber.next(e);
312313
};
313-
(<any>xhr.onprogress).progressSubscriber = progressSubscriber;
314+
xhr.onprogress = xhrProgress;
315+
(<any>xhrProgress).progressSubscriber = progressSubscriber;
314316
}
315-
316-
xhr.onerror = function xhrError(e) {
317+
let xhrError: (e: ErrorEvent) => void;
318+
xhrError = function(e: ErrorEvent) {
317319
const { progressSubscriber, subscriber, request } = (<any>xhrError);
318320
if (progressSubscriber) {
319321
progressSubscriber.error(e);
320322
}
321323
subscriber.error(new AjaxError('ajax error', this, request));
322324
};
323-
(<any>xhr.onerror).request = request;
324-
(<any>xhr.onerror).subscriber = this;
325-
(<any>xhr.onerror).progressSubscriber = progressSubscriber;
325+
xhr.onerror = xhrError;
326+
(<any>xhrError).request = request;
327+
(<any>xhrError).subscriber = this;
328+
(<any>xhrError).progressSubscriber = progressSubscriber;
326329
}
327330

328-
xhr.onreadystatechange = function xhrReadyStateChange(e) {
331+
function xhrReadyStateChange(e: ProgressEvent) {
329332
const { subscriber, progressSubscriber, request } = (<any>xhrReadyStateChange);
330333
if (this.readyState === 4) {
331334
// normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
@@ -354,9 +357,10 @@ export class AjaxSubscriber<T> extends Subscriber<Event> {
354357
}
355358
}
356359
};
357-
(<any>xhr.onreadystatechange).subscriber = this;
358-
(<any>xhr.onreadystatechange).progressSubscriber = progressSubscriber;
359-
(<any>xhr.onreadystatechange).request = request;
360+
xhr.onreadystatechange = xhrReadyStateChange;
361+
(<any>xhrReadyStateChange).subscriber = this;
362+
(<any>xhrReadyStateChange).progressSubscriber = progressSubscriber;
363+
(<any>xhrReadyStateChange).request = request;
360364
}
361365

362366
unsubscribe() {

0 commit comments

Comments
 (0)