DefinitelyTyped/types/jasmine-ajax/jasmine-ajax-tests.ts
Julian Gonggrijp 1c92cef26e Add missing RequestStub methods to jasmine-ajax (#37804)
* Add a test to jasmine-ajax for RequestStub.andReturn

* Add tests to jasmine-ajax for the missing RequestStub methods

* Add the missing RequestStub methods to jasmine-ajax

* Add myself to the list of jasmine-ajax contributors

* Fix a linter issue in jasmine-ajax

* Address remarks by @sheetalkamat (#37804)

* Add missing parameter to JasmineAjaxRequest.responseError (#37804)
2019-09-03 06:14:03 -07:00

1854 lines
56 KiB
TypeScript

/**
* Tests adapted from: https://github.com/jasmine/jasmine-ajax
* By Louis Grignon
*/
/// <reference types="jasmine" />
declare function getJasmineRequireObj();
describe('StubTracker', () => {
beforeEach(() => {
const Constructor = getJasmineRequireObj().AjaxStubTracker();
this.tracker = new Constructor();
});
it('finds nothing if no stubs are added', () => {
expect(this.tracker.findStub()).toBeUndefined();
});
it('finds an added stub', () => {
const stub = { matches: () => true };
this.tracker.addStub(stub);
expect(this.tracker.findStub()).toBe(stub);
});
it('skips an added stub that does not match', () => {
const stub = { matches: () => false };
this.tracker.addStub(stub);
expect(this.tracker.findStub()).toBeUndefined();
});
it('passes url, data, and method to the stub', () => {
const stub = { matches: jasmine.createSpy('matches') };
this.tracker.addStub(stub);
this.tracker.findStub('url', 'data', 'method');
expect(stub.matches).toHaveBeenCalledWith('url', 'data', 'method');
});
it('can clear out all stubs', () => {
const stub = { matches: jasmine.createSpy('matches') };
this.tracker.addStub(stub);
this.tracker.findStub();
expect(stub.matches).toHaveBeenCalled();
this.tracker.reset();
stub.matches.calls.reset();
this.tracker.findStub();
expect(stub.matches).not.toHaveBeenCalled();
});
it('uses the most recently added stub that matches', () => {
const stub1 = { matches: () => true };
const stub2 = { matches: () => false };
const stub3 = { matches: () => false };
this.tracker.addStub(stub1);
this.tracker.addStub(stub2);
this.tracker.addStub(stub3);
expect(this.tracker.findStub()).toBe(stub2);
});
});
describe('FakeRequest', () => {
beforeEach(() => {
this.requestTracker = { track: jasmine.createSpy('trackRequest') };
this.stubTracker = { findStub() { } };
const parserInstance = this.parserInstance = jasmine.createSpy('parse');
this.paramParser = { findParser: () => ({ parse: parserInstance }) };
const eventBus = this.fakeEventBus = {
addEventListener: jasmine.createSpy('addEventListener'),
trigger: jasmine.createSpy('trigger'),
removeEventListener: jasmine.createSpy('removeEventListener')
};
this.eventBusFactory = () => {
return eventBus;
};
this.fakeGlobal = {
XMLHttpRequest: () => {
this.extraAttribute = 'my cool attribute';
},
DOMParser: window['DOMParser'],
ActiveXObject: window['ActiveXObject']
};
this.FakeRequest = getJasmineRequireObj().AjaxFakeRequest(this.eventBusFactory)(this.fakeGlobal, this.requestTracker, this.stubTracker, this.paramParser);
});
it('extends from the global XMLHttpRequest', () => {
const request = new this.FakeRequest();
expect(request.extraAttribute).toEqual('my cool attribute');
});
it('skips XMLHttpRequest attributes that IE does not want copied', () => {
// use real window here so it will correctly go red on IE if it breaks
const FakeRequest = getJasmineRequireObj().AjaxFakeRequest(this.eventBusFactory)(window, this.requestTracker, this.stubTracker, this.paramParser);
const request = new FakeRequest();
expect(request.responseBody).toBeUndefined();
expect(request.responseXML).toBeUndefined();
expect(request.statusText).toBeUndefined();
});
it('tracks the request', () => {
const request = new this.FakeRequest();
expect(this.requestTracker.track).toHaveBeenCalledWith(request);
});
it('has default request headers and override mime type', () => {
const request = new this.FakeRequest();
expect(request.requestHeaders).toEqual({});
expect(request.overriddenMimeType).toBeNull();
});
it('saves request information when opened', () => {
const request = new this.FakeRequest();
request.open('METHOD', 'URL', 'ignore_async', 'USERNAME', 'PASSWORD');
expect(request.method).toEqual('METHOD');
expect(request.url).toEqual('URL');
expect(request.username).toEqual('USERNAME');
expect(request.password).toEqual('PASSWORD');
});
it('saves an override mime type', () => {
const request = new this.FakeRequest();
request.overrideMimeType('application/text; charset: utf-8');
expect(request.overriddenMimeType).toBe('application/text; charset: utf-8');
});
it('saves request headers', () => {
const request = new this.FakeRequest();
request.setRequestHeader('X-Header-1', 'value1');
request.setRequestHeader('X-Header-2', 'value2');
expect(request.requestHeaders).toEqual({
'X-Header-1': 'value1',
'X-Header-2': 'value2'
});
});
it('combines request headers with the same header name', () => {
const request = new this.FakeRequest();
request.setRequestHeader('X-Header', 'value1');
request.setRequestHeader('X-Header', 'value2');
expect(request.requestHeaders['X-Header']).toEqual('value1, value2');
});
it('finds the content-type request header', () => {
const request = new this.FakeRequest();
request.setRequestHeader('ContEnt-tYPe', 'application/text+xml');
expect(request.contentType()).toEqual('application/text+xml');
});
describe('managing readyState', () => {
beforeEach(() => {
this.request = new this.FakeRequest();
});
it('has an initial ready state of 0 (uninitialized)', () => {
expect(this.request.readyState).toBe(0);
expect(this.fakeEventBus.trigger).not.toHaveBeenCalled();
});
it('has a ready state of 1 (open) when opened', () => {
this.request.open();
expect(this.request.readyState).toBe(1);
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange');
});
it('has a ready state of 0 (uninitialized) when aborted', () => {
this.request.open();
this.fakeEventBus.trigger.calls.reset();
this.request.abort();
expect(this.request.readyState).toBe(0);
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange');
});
it('has a ready state of 1 (sent) when sent', () => {
this.request.open();
this.fakeEventBus.trigger.calls.reset();
this.request.send();
expect(this.request.readyState).toBe(1);
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('loadstart');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('readystatechange');
});
it('has a ready state of 4 (loaded) when timed out', () => {
this.request.open();
this.request.send();
this.fakeEventBus.trigger.calls.reset();
jasmine.clock().install();
this.request.responseTimeout();
jasmine.clock().uninstall();
expect(this.request.readyState).toBe(4);
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange', 'timeout');
});
it('has a ready state of 4 (loaded) when network erroring', () => {
this.request.open();
this.request.send();
this.fakeEventBus.trigger.calls.reset();
this.request.responseError();
expect(this.request.readyState).toBe(4);
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange');
});
it('has a ready state of 4 (loaded) when responding', () => {
this.request.open();
this.request.send();
this.fakeEventBus.trigger.calls.reset();
this.request.respondWith({});
expect(this.request.readyState).toBe(4);
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange');
});
it('has a ready state of 2, then 4 (loaded) when responding', () => {
this.request.open();
this.request.send();
this.fakeEventBus.trigger.calls.reset();
const request = this.request;
const events = [];
const headers = [
{ name: 'X-Header', value: 'foo' }
];
this.fakeEventBus.trigger.and.callFake(event => {
if (event === 'readystatechange') {
events.push({
readyState: request.readyState,
status: request.status,
statusText: request.statusText,
responseHeaders: request.responseHeaders
});
}
});
this.request.respondWith({
status: 200,
statusText: 'OK',
responseHeaders: headers
});
expect(this.request.readyState).toBe(4);
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange');
expect(events.length).toBe(2);
expect(events).toEqual([
{ readyState: 2, status: 200, statusText: 'OK', responseHeaders: headers },
{ readyState: 4, status: 200, statusText: 'OK', responseHeaders: headers }
]);
});
it('throws an error when timing out a request that has completed', () => {
this.request.open();
this.request.send();
this.request.respondWith({});
const request = this.request;
expect(() => {
request.responseTimeout();
}).toThrowError('FakeXMLHttpRequest already completed');
});
it('throws an error when responding to a request that has completed', () => {
this.request.open();
this.request.send();
this.request.respondWith({});
const request = this.request;
expect(() => {
request.respondWith({});
}).toThrowError('FakeXMLHttpRequest already completed');
});
it('throws an error when erroring a request that has completed', () => {
this.request.open();
this.request.send();
this.request.respondWith({});
const request = this.request;
expect(() => {
request.responseError({});
}).toThrowError('FakeXMLHttpRequest already completed');
});
});
it('registers on-style callback with the event bus', () => {
this.request = new this.FakeRequest();
expect(this.fakeEventBus.addEventListener).toHaveBeenCalledWith('readystatechange', jasmine.any(Function));
expect(this.fakeEventBus.addEventListener).toHaveBeenCalledWith('loadstart', jasmine.any(Function));
expect(this.fakeEventBus.addEventListener).toHaveBeenCalledWith('progress', jasmine.any(Function));
expect(this.fakeEventBus.addEventListener).toHaveBeenCalledWith('abort', jasmine.any(Function));
expect(this.fakeEventBus.addEventListener).toHaveBeenCalledWith('error', jasmine.any(Function));
expect(this.fakeEventBus.addEventListener).toHaveBeenCalledWith('load', jasmine.any(Function));
expect(this.fakeEventBus.addEventListener).toHaveBeenCalledWith('timeout', jasmine.any(Function));
expect(this.fakeEventBus.addEventListener).toHaveBeenCalledWith('loadend', jasmine.any(Function));
this.request.onreadystatechange = jasmine.createSpy('readystatechange');
this.request.onloadstart = jasmine.createSpy('loadstart');
this.request.onprogress = jasmine.createSpy('progress');
this.request.onabort = jasmine.createSpy('abort');
this.request.onerror = jasmine.createSpy('error');
this.request.onload = jasmine.createSpy('load');
this.request.ontimeout = jasmine.createSpy('timeout');
this.request.onloadend = jasmine.createSpy('loadend');
const args = this.fakeEventBus.addEventListener.calls.allArgs();
for (const [eventName, busCallback] of args) {
busCallback();
expect(this.request['on' + eventName]).toHaveBeenCalled();
}
});
it('delegates addEventListener to the eventBus', () => {
this.request = new this.FakeRequest();
this.request.addEventListener('foo', 'bar');
expect(this.fakeEventBus.addEventListener).toHaveBeenCalledWith('foo', 'bar');
});
it('delegates removeEventListener to the eventBus', () => {
this.request = new this.FakeRequest();
this.request.removeEventListener('foo', 'bar');
expect(this.fakeEventBus.removeEventListener).toHaveBeenCalledWith('foo', 'bar');
});
describe('triggering progress events', () => {
beforeEach(() => {
this.request = new this.FakeRequest();
});
it('should not trigger any events to start', () => {
this.request.open();
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange');
});
it('should trigger loadstart when sent', () => {
this.request.open();
this.fakeEventBus.trigger.calls.reset();
this.request.send();
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('loadstart');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('readystatechange');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('progress');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('abort');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('error');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('load');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('timeout');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('loadend');
});
it('should trigger abort, progress, loadend when aborted', () => {
this.request.open();
this.request.send();
this.fakeEventBus.trigger.calls.reset();
this.request.abort();
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('loadstart');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('progress');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('abort');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('error');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('load');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('timeout');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('loadend');
});
it('should trigger error, progress, loadend when network error', () => {
this.request.open();
this.request.send();
this.fakeEventBus.trigger.calls.reset();
this.request.responseError();
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('loadstart');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('progress');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('abort');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('error');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('load');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('timeout');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('loadend');
});
it('should trigger timeout, progress, loadend when timing out', () => {
this.request.open();
this.request.send();
this.fakeEventBus.trigger.calls.reset();
jasmine.clock().install();
this.request.responseTimeout();
jasmine.clock().uninstall();
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('loadstart');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange', 'timeout');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('progress');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('abort');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('error');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('load');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('timeout');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('loadend');
});
it('should trigger load, progress, loadend when responding', () => {
this.request.open();
this.request.send();
this.fakeEventBus.trigger.calls.reset();
this.request.respondWith({ status: 200 });
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('loadstart');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('readystatechange');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('progress');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('abort');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('error');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('load');
expect(this.fakeEventBus.trigger).not.toHaveBeenCalledWith('timeout');
expect(this.fakeEventBus.trigger).toHaveBeenCalledWith('loadend');
});
});
it('ticks the jasmine clock on timeout', () => {
const clock = { tick: jasmine.createSpy('tick') };
spyOn(jasmine, 'clock').and.returnValue(clock as any);
const request = new this.FakeRequest();
request.open();
request.send();
request.responseTimeout();
expect(clock.tick).toHaveBeenCalledWith(30000);
});
it('has an initial status of null', () => {
const request = new this.FakeRequest();
expect(request.status).toBeNull();
});
it('has an aborted status', () => {
const request = new this.FakeRequest();
request.abort();
expect(request.status).toBe(0);
expect(request.statusText).toBe('abort');
});
it('has a status from the response', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200 });
expect(request.status).toBe(200);
expect(request.statusText).toBe('');
});
it('has a statusText from the response', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200, statusText: 'OK' });
expect(request.status).toBe(200);
expect(request.statusText).toBe('OK');
});
it('saves off any data sent to the server', () => {
const request = new this.FakeRequest();
request.open();
request.send('foo=bar&baz=quux');
expect(request.params).toBe('foo=bar&baz=quux');
});
it('parses data sent to the server', () => {
const request = new this.FakeRequest();
request.open();
request.send('foo=bar&baz=quux');
this.parserInstance.and.returnValue({data: 'parsed'});
expect(request.data()).toEqual({data: 'parsed'});
});
it('skips parsing if no data was sent', () => {
const request = new this.FakeRequest();
request.open();
request.send();
expect(request.data()).toEqual({});
expect(this.parserInstance).not.toHaveBeenCalled();
});
it('saves responseText', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200, responseText: 'foobar' });
expect(request.responseText).toBe('foobar');
});
it('defaults responseText if none is given', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200 });
expect(request.responseText).toBe('');
});
it('retrieves individual response headers', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({
status: 200,
responseHeaders: {
'X-Header': 'foo'
}
});
expect(request.getResponseHeader('X-Header')).toBe('foo');
});
it('retrieves individual response headers case-insensitively', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({
status: 200,
responseHeaders: {
'X-Header': 'foo'
}
});
expect(request.getResponseHeader('x-header')).toBe('foo');
});
it('retrieves a combined response header', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({
status: 200,
responseHeaders: [
{ name: 'X-Header', value: 'foo' },
{ name: 'X-Header', value: 'bar' }
]
});
expect(request.getResponseHeader('x-header')).toBe('foo, bar');
});
it("doesn't pollute the response headers of other XHRs", () => {
const request1 = new this.FakeRequest();
request1.open();
request1.send();
const request2 = new this.FakeRequest();
request2.open();
request2.send();
request1.respondWith({ status: 200, responseHeaders: { 'X-Foo': 'bar' } });
request2.respondWith({ status: 200, responseHeaders: { 'X-Baz': 'quux' } });
expect(request1.getAllResponseHeaders()).toBe("X-Foo: bar\r\n");
expect(request2.getAllResponseHeaders()).toBe("X-Baz: quux\r\n");
});
it('retrieves all response headers', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({
status: 200,
responseHeaders: [
{ name: 'X-Header-1', value: 'foo' },
{ name: 'X-Header-2', value: 'bar' },
{ name: 'X-Header-1', value: 'baz' }
]
});
expect(request.getAllResponseHeaders()).toBe("X-Header-1: foo\r\nX-Header-2: bar\r\nX-Header-1: baz\r\n");
});
it('sets the content-type header to the specified contentType when no other headers are supplied', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200, contentType: 'text/plain' });
expect(request.getResponseHeader('content-type')).toBe('text/plain');
expect(request.getAllResponseHeaders()).toBe("Content-Type: text/plain\r\n");
});
it('sets a default content-type header if no contentType and headers are supplied', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200 });
expect(request.getResponseHeader('content-type')).toBe('application/json');
expect(request.getAllResponseHeaders()).toBe("Content-Type: application/json\r\n");
});
it('has no responseXML by default', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200 });
expect(request.responseXML).toBeNull();
});
it('parses a text/xml document into responseXML', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200, contentType: 'text/xml', responseText: '<dom><stuff/></dom>' });
if (typeof window['Document'] !== 'undefined') {
expect(request.responseXML instanceof window['Document']).toBe(true);
expect(request.response instanceof window['Document']).toBe(true);
} else {
// IE 8
expect(request.responseXML instanceof window['ActiveXObject']).toBe(true);
expect(request.response instanceof window['ActiveXObject']).toBe(true);
}
});
it('parses an application/xml document into responseXML', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200, contentType: 'application/xml', responseText: '<dom><stuff/></dom>' });
if (typeof window['Document'] !== 'undefined') {
expect(request.responseXML instanceof window['Document']).toBe(true);
expect(request.response instanceof window['Document']).toBe(true);
} else {
// IE 8
expect(request.responseXML instanceof window['ActiveXObject']).toBe(true);
expect(request.response instanceof window['ActiveXObject']).toBe(true);
}
});
it('parses a custom blah+xml document into responseXML', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200, contentType: 'application/text+xml', responseText: '<dom><stuff/></dom>' });
if (typeof window['Document'] !== 'undefined') {
expect(request.responseXML instanceof window['Document']).toBe(true);
expect(request.response instanceof window['Document']).toBe(true);
} else {
// IE 8
expect(request.responseXML instanceof window['ActiveXObject']).toBe(true);
expect(request.response instanceof window['ActiveXObject']).toBe(true);
}
});
it('defaults the response attribute to the responseText', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200, responseText: 'foo' });
expect(request.response).toEqual('foo');
});
it('has a text response when the responseType is blank', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200, responseText: 'foo', responseType: '' });
expect(request.response).toEqual('foo');
});
it('has a text response when the responseType is text', () => {
const request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200, responseText: 'foo', responseType: 'text' });
expect(request.response).toEqual('foo');
});
});
describe("Jasmine Mock Ajax (for toplevel)", () => {
// TODO: these are all `any`!
let request, anotherRequest, response; // tslint:disable-line one-variable-per-declaration
let success, error, complete; // tslint:disable-line one-variable-per-declaration
let client, onreadystatechange; // tslint:disable-line one-variable-per-declaration
const sharedContext: any = {};
let fakeGlobal, mockAjax; // tslint:disable-line one-variable-per-declaration
beforeEach(() => {
const fakeXMLHttpRequest = jasmine.createSpy('realFakeXMLHttpRequest');
fakeGlobal = {
XMLHttpRequest: fakeXMLHttpRequest,
DOMParser: window['DOMParser'],
ActiveXObject: window['ActiveXObject']
};
mockAjax = new MockAjax(fakeGlobal);
mockAjax.install();
success = jasmine.createSpy("onSuccess");
error = jasmine.createSpy("onFailure");
complete = jasmine.createSpy("onComplete");
onreadystatechange = function() {
if (this.readyState === (this.DONE || 4)) { // IE 8 doesn't support DONE
if (this.status === 200) {
success(this.responseText, this.textStatus, this);
} else {
error(this, this.textStatus, '');
}
complete(this, this.textStatus);
}
};
});
describe("when making a request", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com/someApi");
client.send();
request = mockAjax.requests.mostRecent();
});
it("should store URL and transport", () => {
expect(request.url).toEqual("example.com/someApi");
});
it("should queue the request", () => {
expect(mockAjax.requests.count()).toEqual(1);
});
it("should allow access to the queued request", () => {
expect(mockAjax.requests.first()).toEqual(request);
});
it("should allow access to the queued request via index", () => {
expect(mockAjax.requests.at(0)).toEqual(request);
});
describe("and then another request", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com/someApi");
client.send();
anotherRequest = mockAjax.requests.mostRecent();
});
it("should queue the next request", () => {
expect(mockAjax.requests.count()).toEqual(2);
});
it("should allow access to the other queued request", () => {
expect(mockAjax.requests.first()).toEqual(request);
expect(mockAjax.requests.mostRecent()).toEqual(anotherRequest);
});
});
describe("mockAjax.requests.mostRecent()", () => {
describe("when there is one request queued", () => {
it("should return the request", () => {
expect(mockAjax.requests.mostRecent()).toEqual(request);
});
});
describe("when there is more than one request", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com/someApi");
client.send();
anotherRequest = mockAjax.requests.mostRecent();
});
it("should return the most recent request", () => {
expect(mockAjax.requests.mostRecent()).toEqual(anotherRequest);
});
});
describe("when there are no requests", () => {
beforeEach(() => {
mockAjax.requests.reset();
});
it("should return null", () => {
expect(mockAjax.requests.mostRecent()).toBeUndefined();
});
});
});
describe("clearAjaxRequests()", () => {
beforeEach(() => {
mockAjax.requests.reset();
});
it("should remove all requests", () => {
expect(mockAjax.requests.count()).toEqual(0);
expect(mockAjax.requests.mostRecent()).toBeUndefined();
});
});
});
describe("when simulating a response with request.response", () => {
describe("and the response is Success", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com/someApi");
client.setRequestHeader("Content-Type", "text/plain");
client.send();
request = mockAjax.requests.mostRecent();
response = { status: 200, statusText: "OK", contentType: "text/html", responseText: "OK!", responseType: "json" };
request.respondWith(response);
sharedContext.responseCallback = success;
sharedContext.status = response.status;
sharedContext.statusText = response.statusText;
sharedContext.contentType = response.contentType;
sharedContext.responseText = response.responseText;
sharedContext.responseType = response.responseType;
});
it("should call the success handler", () => {
expect(success).toHaveBeenCalled();
});
it("should not call the failure handler", () => {
expect(error).not.toHaveBeenCalled();
});
it("should call the complete handler", () => {
expect(complete).toHaveBeenCalled();
});
sharedAjaxResponseBehaviorForZepto_Success(sharedContext);
});
describe("and the response is Success, but with JSON", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com/someApi");
client.setRequestHeader("Content-Type", "application/json");
client.send();
request = mockAjax.requests.mostRecent();
const responseObject = { status: 200, statusText: "OK", contentType: "application/json", responseText: '{"foo":"bar"}', responseType: "json" };
request.respondWith(responseObject);
sharedContext.responseCallback = success;
sharedContext.status = responseObject.status;
sharedContext.statusText = responseObject.statusText;
sharedContext.contentType = responseObject.contentType;
sharedContext.responseText = responseObject.responseText;
sharedContext.responseType = responseObject.responseType;
response = success.calls.mostRecent().args[2];
});
it("should call the success handler", () => {
expect(success).toHaveBeenCalled();
});
it("should not call the failure handler", () => {
expect(error).not.toHaveBeenCalled();
});
it("should call the complete handler", () => {
expect(complete).toHaveBeenCalled();
});
it("should return a JavaScript object for XHR2 response", () => {
const responseText = sharedContext.responseText;
expect(success.calls.mostRecent().args[0]).toEqual(responseText);
expect(response.responseText).toEqual(responseText);
expect(response.response).toEqual({ foo: "bar" });
});
sharedAjaxResponseBehaviorForZepto_Success(sharedContext);
});
describe("and the response is Success, and response is overriden", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com/someApi");
client.setRequestHeader("Content-Type", "application/json");
client.send();
request = mockAjax.requests.mostRecent();
const responseObject = { status: 200, statusText: "OK", contentType: "application/json", responseText: '{"foo":"bar"}', responseType: 'json' };
request.respondWith(responseObject);
sharedContext.responseCallback = success;
sharedContext.status = responseObject.status;
sharedContext.statusText = responseObject.statusText;
sharedContext.contentType = responseObject.contentType;
sharedContext.responseText = responseObject.responseText;
sharedContext.responseType = responseObject.responseType;
response = success.calls.mostRecent().args[2];
});
it("should return the provided override for the XHR2 response", () => {
const responseText = sharedContext.responseText;
expect(response.responseText).toEqual(responseText);
expect(response.response).toEqual({ foo: "bar" });
});
sharedAjaxResponseBehaviorForZepto_Success(sharedContext);
});
describe("response with unique header names using an object", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com");
client.send();
request = mockAjax.requests.mostRecent();
const responseObject = {
status: 200, statusText: "OK", responseText: '["foo"]', responseHeaders: {
'X-Header1': 'header 1 value',
'X-Header2': 'header 2 value',
'X-Header3': 'header 3 value'
}
};
request.respondWith(responseObject);
response = success.calls.mostRecent().args[2];
});
it("getResponseHeader should return the each value", () => {
expect(response.getResponseHeader('X-Header1')).toBe('header 1 value');
expect(response.getResponseHeader('X-Header2')).toBe('header 2 value');
expect(response.getResponseHeader('X-Header3')).toBe('header 3 value');
});
it("getAllResponseHeaders should return all values", () => {
expect(response.getAllResponseHeaders()).toBe([
"X-Header1: header 1 value",
"X-Header2: header 2 value",
"X-Header3: header 3 value"
].join("\r\n") + "\r\n");
});
});
describe("response with multiple headers of the same name using an array of objects", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com");
client.send();
request = mockAjax.requests.mostRecent();
const responseObject = {
status: 200, statusText: "OK", responseText: '["foo"]', responseHeaders: [
{ name: 'X-Header', value: 'header value 1' },
{ name: 'X-Header', value: 'header value 2' }
]
};
request.respondWith(responseObject);
response = success.calls.mostRecent().args[2];
});
it("getResponseHeader should return all values comma separated", () => {
expect(response.getResponseHeader('X-Header')).toBe('header value 1, header value 2');
});
it("getAllResponseHeaders should return all values", () => {
expect(response.getAllResponseHeaders()).toBe([
"X-Header: header value 1",
"X-Header: header value 2"
].join("\r\n") + "\r\n");
});
});
describe("the content type defaults to application/json", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com/someApi");
client.setRequestHeader("Content-Type", "application/json");
client.send();
request = mockAjax.requests.mostRecent();
response = { status: 200, statusText: "OK", responseText: '{"foo": "valid JSON, dammit."}', responseType: 'json' };
request.respondWith(response);
sharedContext.responseCallback = success;
sharedContext.status = response.status;
sharedContext.statusText = response.statusText;
sharedContext.contentType = "application/json";
sharedContext.responseType = response.responseType;
sharedContext.responseText = response.responseText;
});
it("should call the success handler", () => {
expect(success).toHaveBeenCalled();
});
it("should not call the failure handler", () => {
expect(error).not.toHaveBeenCalled();
});
it("should call the complete handler", () => {
expect(complete).toHaveBeenCalled();
});
sharedAjaxResponseBehaviorForZepto_Success(sharedContext);
});
describe("and the status/response code is 0", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com/someApi");
client.setRequestHeader("Content-Type", "text/plain");
client.send();
request = mockAjax.requests.mostRecent();
response = { status: 0, statusText: "ABORT", responseText: '{"foo": "whoops!"}', responseType: "json"};
request.respondWith(response);
sharedContext.responseCallback = error;
sharedContext.status = 0;
sharedContext.statusText = response.statusText;
sharedContext.contentType = 'application/json';
sharedContext.responseText = response.responseText;
sharedContext.responseType = response.responseType;
});
it("should call the success handler", () => {
expect(success).not.toHaveBeenCalled();
});
it("should not call the failure handler", () => {
expect(error).toHaveBeenCalled();
});
it("should call the complete handler", () => {
expect(complete).toHaveBeenCalled();
});
sharedAjaxResponseBehaviorForZepto_Failure(sharedContext);
});
});
describe("and the response is error", () => {
beforeEach(() => {
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com/someApi");
client.setRequestHeader("Content-Type", "text/plain");
client.send();
request = mockAjax.requests.mostRecent();
response = { status: 500, statusText: "SERVER ERROR", contentType: "text/html", responseText: "(._){", responseType: "json"};
request.respondWith(response);
sharedContext.responseCallback = error;
sharedContext.status = response.status;
sharedContext.statusText = response.statusText;
sharedContext.contentType = response.contentType;
sharedContext.responseText = response.responseText;
sharedContext.responseType = response.responseType;
});
it("should not call the success handler", () => {
expect(success).not.toHaveBeenCalled();
});
it("should call the failure handler", () => {
expect(error).toHaveBeenCalled();
});
it("should call the complete handler", () => {
expect(complete).toHaveBeenCalled();
});
sharedAjaxResponseBehaviorForZepto_Failure(sharedContext);
});
describe('when simulating a response with request.responseTimeout', () => {
beforeEach(() => {
jasmine.clock().install();
client = new fakeGlobal.XMLHttpRequest();
client.onreadystatechange = onreadystatechange;
client.open("GET", "example.com/someApi");
client.setRequestHeader("Content-Type", "text/plain");
client.send();
request = mockAjax.requests.mostRecent();
response = { contentType: "text/html", response: "(._){response", responseText: "(._){", responseType: "text", status: 200, statusText: 'OK' };
request.responseTimeout(response);
sharedContext.responseCallback = error;
sharedContext.status = response.status;
sharedContext.statusText = response.statusText;
sharedContext.contentType = response.contentType;
sharedContext.responseText = response.responseText;
sharedContext.responseType = response.responseType;
});
afterEach(() => {
jasmine.clock().uninstall();
});
it("should not call the success handler", () => {
expect(success).not.toHaveBeenCalled();
});
it("should call the failure handler", () => {
expect(error).toHaveBeenCalled();
});
it("should call the complete handler", () => {
expect(complete).toHaveBeenCalled();
});
});
});
function sharedAjaxResponseBehaviorForZepto_Success(context) {
describe("the success response", () => {
let xhr;
beforeEach(() => {
xhr = context.responseCallback.calls.mostRecent().args[2];
});
it("should have the expected status code", () => {
expect(xhr.status).toEqual(context.status);
});
it("should have the expected content type", () => {
expect(xhr.getResponseHeader('Content-Type')).toEqual(context.contentType);
});
it("should have the expected xhr2 response", () => {
const expected = context.response || context.responseType === 'json' ? JSON.parse(context.responseText) : context.responseText;
expect(xhr.response).toEqual(expected);
});
it("should have the expected response text", () => {
expect(xhr.responseText).toEqual(context.responseText);
});
it("should have the expected status text", () => {
expect(xhr.statusText).toEqual(context.statusText);
});
});
}
function sharedAjaxResponseBehaviorForZepto_Failure(context) {
describe("the failure response", () => {
let xhr;
beforeEach(() => {
xhr = context.responseCallback.calls.mostRecent().args[0];
});
it("should have the expected status code", () => {
expect(xhr.status).toEqual(context.status);
});
it("should have the expected content type", () => {
expect(xhr.getResponseHeader('Content-Type')).toEqual(context.contentType);
});
it("should have the expected xhr2 response", () => {
const expected = context.response || xhr.responseType === 'json' ? JSON.parse(xhr.responseText) : xhr.responseText;
expect(xhr.response).toEqual(expected);
});
it("should have the expected response text", () => {
expect(xhr.responseText).toEqual(context.responseText);
});
it("should have the expected status text", () => {
expect(xhr.statusText).toEqual(context.statusText);
});
});
}
describe('ParamParser', () => {
beforeEach(() => {
const Constructor = getJasmineRequireObj().AjaxParamParser();
expect(Constructor).toEqual(jasmine.any(Function));
this.parser = new Constructor();
});
it('has a default parser', () => {
const parser = this.parser.findParser({ contentType: () => { } });
const parsed = parser.parse('3+stooges=shemp&3+stooges=larry%20%26%20moe%20%26%20curly&some%3Dthing=else+entirely');
expect(parsed).toEqual({
'3 stooges': ['shemp', 'larry & moe & curly'],
'some=thing': ['else entirely']
});
});
it('should detect and parse json', () => {
const data = {
foo: 'bar',
baz: ['q', 'u', 'u', 'x'],
nested: {
object: {
containing: 'stuff'
}
}
};
const parser = this.parser.findParser({ contentType: () => 'application/json' });
const parsed = parser.parse(JSON.stringify(data));
expect(parsed).toEqual(data);
});
it('should parse json with further qualifiers on content-type', () => {
const data = {
foo: 'bar',
baz: ['q', 'u', 'u', 'x'],
nested: {
object: {
containing: 'stuff'
}
}
};
const parser = this.parser.findParser({ contentType: () => 'application/json; charset=utf-8' });
const parsed = parser.parse(JSON.stringify(data));
expect(parsed).toEqual(data);
});
it('should have custom parsers take precedence', () => {
const custom = {
test: jasmine.createSpy('test').and.returnValue(true),
parse: jasmine.createSpy('parse').and.returnValue('parsedFormat')
};
this.parser.add(custom);
const parser = this.parser.findParser({ contentType: () => { } });
const parsed = parser.parse('custom_format');
expect(parsed).toEqual('parsedFormat');
expect(custom.test).toHaveBeenCalled();
expect(custom.parse).toHaveBeenCalledWith('custom_format');
});
it('should skip custom parsers that do not match', () => {
const custom = {
test: jasmine.createSpy('test').and.returnValue(false),
parse: jasmine.createSpy('parse').and.returnValue('parsedFormat')
};
this.parser.add(custom);
const parser = this.parser.findParser({ contentType: () => { } });
const parsed = parser.parse('custom_format');
expect(parsed).toEqual({ custom_format: ['undefined'] });
expect(custom.test).toHaveBeenCalled();
expect(custom.parse).not.toHaveBeenCalled();
});
it('removes custom parsers when reset', () => {
const custom = {
test: jasmine.createSpy('test').and.returnValue(true),
parse: jasmine.createSpy('parse').and.returnValue('parsedFormat')
};
this.parser.add(custom);
let parser = this.parser.findParser({ contentType: () => { } });
let parsed = parser.parse('custom_format');
expect(parsed).toEqual('parsedFormat');
custom.test['calls'].reset();
custom.parse['calls'].reset();
this.parser.reset();
parser = this.parser.findParser({ contentType: () => { } });
parsed = parser.parse('custom_format');
expect(parsed).toEqual({ custom_format: ['undefined'] });
expect(custom.test).not.toHaveBeenCalled();
expect(custom.parse).not.toHaveBeenCalled();
});
});
describe('RequestStub', () => {
beforeEach(() => {
this.RequestStub = getJasmineRequireObj().AjaxRequestStub();
jasmine.addMatchers({
toMatchRequest(a, b) {
return {
compare(actual): jasmine.CustomMatcherResult {
return {
message: '',
pass: actual.matches.apply(actual, Array.prototype.slice.call(arguments, 1))
};
}
};
}
});
});
it('matches just by exact url', () => {
const stub = new this.RequestStub('www.example.com/foo');
expect(stub)['toMatchRequest']('www.example.com/foo');
});
it('does not match if the url differs', () => {
const stub = new this.RequestStub('www.example.com/foo');
expect(stub).not['toMatchRequest']('www.example.com/bar');
});
it('matches unordered query params', () => {
const stub = new this.RequestStub('www.example.com?foo=bar&baz=quux');
expect(stub)['toMatchRequest']('www.example.com?baz=quux&foo=bar');
});
it('requires all specified query params to be there', () => {
const stub = new this.RequestStub('www.example.com?foo=bar&baz=quux');
expect(stub).not['toMatchRequest']('www.example.com?foo=bar');
});
it('can match the url with a RegExp', () => {
const stub = new this.RequestStub(/ba[rz]/);
expect(stub)['toMatchRequest']('bar');
expect(stub)['toMatchRequest']('baz');
expect(stub).not['toMatchRequest']('foo');
});
it('requires the method to match if supplied', () => {
const stub = new this.RequestStub('www.example.com/foo', null, 'POST');
expect(stub).not['toMatchRequest']('www.example.com/foo');
expect(stub).not['toMatchRequest']('www.example.com/foo', null, 'GET');
expect(stub)['toMatchRequest']('www.example.com/foo', null, 'POST');
});
it('requires the data submitted to match if supplied', () => {
const stub = new this.RequestStub('/foo', 'foo=bar&baz=quux');
expect(stub)['toMatchRequest']('/foo', 'baz=quux&foo=bar');
expect(stub).not['toMatchRequest']('/foo', 'foo=bar');
});
it('has methods', () => {
jasmine.Ajax.stubRequest('/foo').andReturn({
status: 200,
contentType: 'application/json',
responseText: '{"success": true}',
responseHeaders: { 'X-Example': 'a value' },
});
jasmine.Ajax.stubRequest('/bar').andReturn({});
jasmine.Ajax.stubRequest('/baz').andError({
status: 400,
statusText: 'Invalid',
});
jasmine.Ajax.stubRequest('/foobar').andError({});
jasmine.Ajax.stubRequest('/foobaz').andTimeout();
jasmine.Ajax.stubRequest('/barbaz').andCallFunction((xhr) => {
xhr.url === '/barbaz';
xhr.method === 'POST';
xhr.params === {};
xhr.username === 'jane_coder';
xhr.password === '12345';
xhr.requestHeaders === {Accept: 'application/json'};
xhr.data() === {query: 'bananas'};
xhr.respondWith({
status: 200,
contentType: 'application/json',
responseText: '{"success": true}',
responseHeaders: { 'X-Example': 'a value' },
});
xhr.responseTimeout();
xhr.responseError({
status: 400,
statusText: 'Invalid',
});
});
});
});
describe('RequestTracker', () => {
beforeEach(() => {
const Constructor = getJasmineRequireObj().AjaxRequestTracker();
this.tracker = new Constructor();
});
it('tracks the number of times ajax requests are made', () => {
expect(this.tracker.count()).toBe(0);
this.tracker.track();
expect(this.tracker.count()).toBe(1);
});
it('simplifies access to the last (most recent) request', () => {
this.tracker.track();
this.tracker.track('request');
expect(this.tracker.mostRecent()).toEqual('request');
});
it('returns a useful falsy value when there is no last (most recent) request', () => {
expect(this.tracker.mostRecent()).toBeFalsy();
});
it('simplifies access to the first (oldest) request', () => {
this.tracker.track('request');
this.tracker.track();
expect(this.tracker.first()).toEqual('request');
});
it('returns a useful falsy value when there is no first (oldest) request', () => {
expect(this.tracker.first()).toBeFalsy();
});
it('allows the requests list to be reset', () => {
this.tracker.track();
this.tracker.track();
expect(this.tracker.count()).toBe(2);
this.tracker.reset();
expect(this.tracker.count()).toBe(0);
});
it('allows retrieval of an arbitrary request by index', () => {
this.tracker.track('1');
this.tracker.track('2');
this.tracker.track('3');
expect(this.tracker.at(1)).toEqual('2');
});
it('allows retrieval of all requests that are for a given url', () => {
this.tracker.track({ url: 'foo' });
this.tracker.track({ url: 'bar' });
expect(this.tracker.filter('bar')).toEqual([{ url: 'bar' }]);
});
it('allows retrieval of all requests that match a given RegExp', () => {
this.tracker.track({ url: 'foo' });
this.tracker.track({ url: 'bar' });
this.tracker.track({ url: 'baz' });
expect(this.tracker.filter(/ba[rz]/)).toEqual([{ url: 'bar' }, { url: 'baz' }]);
});
it('allows retrieval of all requests that match based on a function', () => {
this.tracker.track({ url: 'foo' });
this.tracker.track({ url: 'bar' });
this.tracker.track({ url: 'baz' });
const func = request => request.url === 'bar';
expect(this.tracker.filter(func)).toEqual([{ url: 'bar' }]);
});
it('filters to nothing if no requests have been tracked', () => {
expect(this.tracker.filter('foo')).toEqual([]);
});
});
describe('EventBus', () => {
beforeEach(() => {
this.bus = getJasmineRequireObj().AjaxEventBus()();
});
it('calls an event listener', () => {
const callback = jasmine.createSpy('callback');
this.bus.addEventListener('foo', callback);
this.bus.trigger('foo');
expect(callback).toHaveBeenCalled();
});
it('calls an event listener with additional arguments', () => {
const callback = jasmine.createSpy('callback');
this.bus.addEventListener('foo', callback);
this.bus.trigger('foo', 'bar');
expect(callback).toHaveBeenCalledWith('bar');
});
it('only triggers callbacks for the specified event', () => {
const fooCallback = jasmine.createSpy('foo');
const barCallback = jasmine.createSpy('bar');
this.bus.addEventListener('foo', fooCallback);
this.bus.addEventListener('bar', barCallback);
this.bus.trigger('foo');
expect(fooCallback).toHaveBeenCalled();
expect(barCallback).not.toHaveBeenCalled();
});
it('calls all the callbacks for the specified event', () => {
const callback1 = jasmine.createSpy('callback');
const callback2 = jasmine.createSpy('otherCallback');
this.bus.addEventListener('foo', callback1);
this.bus.addEventListener('foo', callback2);
this.bus.trigger('foo');
expect(callback1).toHaveBeenCalled();
expect(callback2).toHaveBeenCalled();
});
it('works if there are no callbacks for the event', () => {
const bus = this.bus;
expect(() => {
bus.trigger('notActuallyThere');
}).not.toThrow();
});
it('does not call listeners that have been removed', () => {
const callback = jasmine.createSpy('callback');
this.bus.addEventListener('foo', callback);
this.bus.removeEventListener('foo', callback);
this.bus.trigger('foo');
expect(callback).not.toHaveBeenCalled();
});
it('only removes the specified callback', () => {
const callback1 = jasmine.createSpy('callback');
const callback2 = jasmine.createSpy('otherCallback');
this.bus.addEventListener('foo', callback1);
this.bus.addEventListener('foo', callback2);
this.bus.removeEventListener('foo', callback2);
this.bus.trigger('foo');
expect(callback1).toHaveBeenCalled();
expect(callback2).not.toHaveBeenCalled();
});
});
describe("Webmock style mocking", () => {
let successSpy, response, fakeGlobal, mockAjax; // tslint:disable-line one-variable-per-declaration
const sendRequest = function(fakeGlobal, url?, method?) {
url = url || "http://example.com/someApi";
method = method || 'GET';
const xhr = new fakeGlobal.XMLHttpRequest();
xhr.onreadystatechange = args => {
if (this.readyState === (this.DONE || 4)) { // IE 8 doesn't support DONE
response = this;
successSpy();
}
};
xhr.open(method, url);
xhr.send();
};
beforeEach(() => {
successSpy = jasmine.createSpy('success');
fakeGlobal = { XMLHttpRequest: jasmine.createSpy('realXMLHttpRequest') };
mockAjax = new MockAjax(fakeGlobal);
mockAjax.install();
mockAjax.stubRequest("http://example.com/someApi").andReturn({ responseText: "hi!" });
});
it("allows a url to be setup as a stub", () => {
sendRequest(fakeGlobal);
expect(successSpy).toHaveBeenCalled();
});
it("should allow you to clear all the ajax stubs", () => {
mockAjax.stubs.reset();
sendRequest(fakeGlobal);
expect(successSpy).not.toHaveBeenCalled();
});
it("should set the contentType", () => {
sendRequest(fakeGlobal);
expect(response.getResponseHeader('Content-Type')).toEqual('application/json');
});
it("should set the responseText", () => {
sendRequest(fakeGlobal);
expect(response.responseText).toEqual('hi!');
});
it("should default the status to 200", () => {
sendRequest(fakeGlobal);
expect(response.status).toEqual(200);
});
it("should set the responseHeaders", () => {
mockAjax.stubRequest("http://example.com/someApi").andReturn({
responseText: "hi!",
responseHeaders: [{ name: "X-Custom", value: "header value" }]
});
sendRequest(fakeGlobal);
expect(response.getResponseHeader('X-Custom')).toEqual('header value');
});
describe("with another stub for the same url", () => {
beforeEach(() => {
mockAjax.stubRequest("http://example.com/someApi").andReturn({ responseText: "no", status: 403 });
sendRequest(fakeGlobal);
});
it("should set the status", () => {
expect(response.status).toEqual(403);
});
it("should allow the latest stub to win", () => {
expect(response.responseText).toEqual('no');
});
});
});
describe("withMock", () => {
const sendRequest = fakeGlobal => {
const xhr = new fakeGlobal.XMLHttpRequest();
xhr.open("GET", "http://example.com/someApi");
xhr.send();
};
it("installs the mock for passed in function, and uninstalls when complete", () => {
const xmlHttpRequest = jasmine.createSpyObj('XMLHttpRequest', ['open', 'send']);
const xmlHttpRequestCtor = spyOn(window as any, 'XMLHttpRequest').and.returnValue(xmlHttpRequest);
const fakeGlobal = { XMLHttpRequest: xmlHttpRequestCtor };
const mockAjax = new MockAjax(fakeGlobal);
mockAjax.withMock(() => {
sendRequest(fakeGlobal);
expect(xmlHttpRequest.open).not.toHaveBeenCalled();
});
sendRequest(fakeGlobal);
expect(xmlHttpRequest.open).toHaveBeenCalled();
});
it("properly uninstalls when the passed in function throws", () => {
const xmlHttpRequest = jasmine.createSpyObj('XMLHttpRequest', ['open', 'send']);
const xmlHttpRequestCtor = spyOn(window as any, 'XMLHttpRequest').and.returnValue(xmlHttpRequest);
const fakeGlobal = { XMLHttpRequest: xmlHttpRequestCtor };
const mockAjax = new MockAjax(fakeGlobal);
expect(() => {
mockAjax.withMock(() => {
throw "error"; // tslint:disable-line:no-string-throw
});
}).toThrow("error");
sendRequest(fakeGlobal);
expect(xmlHttpRequest.open).toHaveBeenCalled();
});
});
describe("mockAjax", () => {
it("throws an error if installed multiple times", () => {
const fakeXmlHttpRequest = jasmine.createSpy('fakeXmlHttpRequest');
const fakeGlobal = { XMLHttpRequest: fakeXmlHttpRequest };
const mockAjax = new MockAjax(fakeGlobal);
function doubleInstall() {
mockAjax.install();
mockAjax.install();
}
expect(doubleInstall).toThrow();
});
it("does not throw an error if uninstalled between installs", () => {
const fakeXmlHttpRequest = jasmine.createSpy('fakeXmlHttpRequest');
const fakeGlobal = { XMLHttpRequest: fakeXmlHttpRequest };
const mockAjax = new MockAjax(fakeGlobal);
function sequentialInstalls() {
mockAjax.install();
mockAjax.uninstall();
mockAjax.install();
}
expect(sequentialInstalls).not.toThrow();
});
it("does not replace XMLHttpRequest until it is installed", () => {
const fakeXmlHttpRequest = jasmine.createSpy('fakeXmlHttpRequest');
const fakeGlobal = { XMLHttpRequest: fakeXmlHttpRequest };
const mockAjax = new MockAjax(fakeGlobal);
fakeGlobal.XMLHttpRequest('foo');
expect(fakeXmlHttpRequest).toHaveBeenCalledWith('foo');
fakeXmlHttpRequest.calls.reset();
mockAjax.install();
fakeGlobal.XMLHttpRequest('foo');
expect(fakeXmlHttpRequest).not.toHaveBeenCalled();
});
it("replaces the global XMLHttpRequest on uninstall", () => {
const fakeXmlHttpRequest = jasmine.createSpy('fakeXmlHttpRequest');
const fakeGlobal = { XMLHttpRequest: fakeXmlHttpRequest };
const mockAjax = new MockAjax(fakeGlobal);
mockAjax.install();
mockAjax.uninstall();
fakeGlobal.XMLHttpRequest('foo');
expect(fakeXmlHttpRequest).toHaveBeenCalledWith('foo');
});
it("clears requests and stubs upon uninstall", () => {
const fakeXmlHttpRequest = jasmine.createSpy('fakeXmlHttpRequest');
const fakeGlobal = { XMLHttpRequest: fakeXmlHttpRequest };
const mockAjax = new MockAjax(fakeGlobal);
mockAjax.install();
// mockAjax.requests.track(<any> { url: '/testurl' });
mockAjax.stubRequest('/bobcat');
expect(mockAjax.requests.count()).toEqual(1);
expect(mockAjax.stubs.findStub('/bobcat')).toBeDefined();
mockAjax.uninstall();
expect(mockAjax.requests.count()).toEqual(0);
expect(mockAjax.stubs.findStub('/bobcat')).not.toBeDefined();
});
it("allows the httpRequest to be retrieved", () => {
const fakeXmlHttpRequest = jasmine.createSpy('fakeXmlHttpRequest');
const fakeGlobal = { XMLHttpRequest: fakeXmlHttpRequest };
const mockAjax = new MockAjax(fakeGlobal);
mockAjax.install();
const request = new (fakeGlobal.XMLHttpRequest as any)();
expect(mockAjax.requests.count()).toBe(1);
expect(mockAjax.requests.mostRecent()).toBe(request);
});
it("allows the httpRequests to be cleared", () => {
const fakeXmlHttpRequest = jasmine.createSpy('fakeXmlHttpRequest');
const fakeGlobal = { XMLHttpRequest: fakeXmlHttpRequest };
const mockAjax = new MockAjax(fakeGlobal);
mockAjax.install();
const request = new (fakeGlobal.XMLHttpRequest as any)();
expect(mockAjax.requests.mostRecent()).toBe(request);
mockAjax.requests.reset();
expect(mockAjax.requests.count()).toBe(0);
});
});