Commit e0e7bb49 authored by Paul Slaughter's avatar Paul Slaughter

Move rest of lib/utils specs to Jest

- csrf_token_spec
- naviation_utility_spec
- sticky_spec
- poll_spec

Also fixed setHTMLFixtures to use innerHTML
parent ac669daf
...@@ -23,11 +23,12 @@ Did you run bin/rake frontend:fixtures?`, ...@@ -23,11 +23,12 @@ Did you run bin/rake frontend:fixtures?`,
export const getJSONFixture = relativePath => JSON.parse(getFixture(relativePath)); export const getJSONFixture = relativePath => JSON.parse(getFixture(relativePath));
export const resetHTMLFixture = () => { export const resetHTMLFixture = () => {
document.body.textContent = ''; document.head.innerHTML = '';
document.body.innerHTML = '';
}; };
export const setHTMLFixture = (htmlContent, resetHook = afterEach) => { export const setHTMLFixture = (htmlContent, resetHook = afterEach) => {
document.body.outerHTML = htmlContent; document.body.innerHTML = htmlContent;
resetHook(resetHTMLFixture); resetHook(resetHTMLFixture);
}; };
......
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import { setHTMLFixture } from 'helpers/fixtures';
describe('csrf', () => {
let testContext;
beforeEach(() => {
testContext = {};
});
describe('csrf', function() {
beforeEach(() => { beforeEach(() => {
this.tokenKey = 'X-CSRF-Token'; testContext.tokenKey = 'X-CSRF-Token';
this.token = testContext.token =
'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ=='; 'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ==';
}); });
it('returns the correct headerKey', () => { it('returns the correct headerKey', () => {
expect(csrf.headerKey).toBe(this.tokenKey); expect(csrf.headerKey).toBe(testContext.tokenKey);
}); });
describe('when csrf token is in the DOM', () => { describe('when csrf token is in the DOM', () => {
beforeEach(() => { beforeEach(() => {
setFixtures(` setHTMLFixture(`
<meta name="csrf-token" content="${this.token}"> <meta name="csrf-token" content="${testContext.token}">
`); `);
csrf.init(); csrf.init();
}); });
it('returns the csrf token', () => { it('returns the csrf token', () => {
expect(csrf.token).toBe(this.token); expect(csrf.token).toBe(testContext.token);
}); });
it('returns the csrf headers object', () => { it('returns the csrf headers object', () => {
expect(csrf.headers[this.tokenKey]).toBe(this.token); expect(csrf.headers[testContext.tokenKey]).toBe(testContext.token);
}); });
}); });
describe('when csrf token is not in the DOM', () => { describe('when csrf token is not in the DOM', () => {
beforeEach(() => { beforeEach(() => {
setFixtures(` setHTMLFixture(`
<meta name="some-other-token"> <meta name="some-other-token">
`); `);
......
import findAndFollowLink from '~/lib/utils/navigation_utility'; import findAndFollowLink from '~/lib/utils/navigation_utility';
import { visitUrl } from '~/lib/utils/url_utility';
jest.mock('~/lib/utils/url_utility');
describe('findAndFollowLink', () => { describe('findAndFollowLink', () => {
it('visits a link when the selector exists', () => { it('visits a link when the selector exists', () => {
const href = '/some/path'; const href = '/some/path';
const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl');
setFixtures(`<a class="my-shortcut" href="${href}">link</a>`); setFixtures(`<a class="my-shortcut" href="${href}">link</a>`);
...@@ -13,8 +15,6 @@ describe('findAndFollowLink', () => { ...@@ -13,8 +15,6 @@ describe('findAndFollowLink', () => {
}); });
it('does not throw an exception when the selector does not exist', () => { it('does not throw an exception when the selector does not exist', () => {
const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl');
// this should not throw an exception // this should not throw an exception
findAndFollowLink('.this-selector-does-not-exist'); findAndFollowLink('.this-selector-does-not-exist');
......
/* eslint-disable jasmine/no-unsafe-spy */
import Poll from '~/lib/utils/poll'; import Poll from '~/lib/utils/poll';
import { successCodes } from '~/lib/utils/http_status'; import { successCodes } from '~/lib/utils/http_status';
import waitForPromises from 'helpers/wait_for_promises';
const waitForAllCallsToFinish = (service, waitForCount, successCallback) => {
const timer = () => {
setTimeout(() => {
if (service.fetch.calls.count() === waitForCount) {
successCallback();
} else {
timer();
}
}, 0);
};
timer();
};
function mockServiceCall(service, response, shouldFail = false) {
const action = shouldFail ? Promise.reject : Promise.resolve;
const responseObject = response;
if (!responseObject.headers) responseObject.headers = {};
service.fetch.and.callFake(action.bind(Promise, responseObject));
}
describe('Poll', () => { describe('Poll', () => {
const service = jasmine.createSpyObj('service', ['fetch']); let callbacks;
const callbacks = jasmine.createSpyObj('callbacks', ['success', 'error', 'notification']); let service;
function setup() { function setup() {
return new Poll({ return new Poll({
...@@ -40,18 +16,45 @@ describe('Poll', () => { ...@@ -40,18 +16,45 @@ describe('Poll', () => {
}).makeRequest(); }).makeRequest();
} }
afterEach(() => { const mockServiceCall = (response, shouldFail = false) => {
callbacks.success.calls.reset(); const value = {
callbacks.error.calls.reset(); ...response,
callbacks.notification.calls.reset(); header: response.header || {},
service.fetch.calls.reset(); };
if (shouldFail) {
service.fetch.mockRejectedValue(value);
} else {
service.fetch.mockResolvedValue(value);
}
};
const waitForAllCallsToFinish = (waitForCount, successCallback) => {
if (!waitForCount) {
return Promise.resolve().then(successCallback());
}
jest.runOnlyPendingTimers();
return waitForPromises().then(() => waitForAllCallsToFinish(waitForCount - 1, successCallback));
};
beforeEach(() => {
service = {
fetch: jest.fn(),
};
callbacks = {
success: jest.fn(),
error: jest.fn(),
notification: jest.fn(),
};
}); });
it('calls the success callback when no header for interval is provided', done => { it('calls the success callback when no header for interval is provided', done => {
mockServiceCall(service, { status: 200 }); mockServiceCall({ status: 200 });
setup(); setup();
waitForAllCallsToFinish(service, 1, () => { waitForAllCallsToFinish(1, () => {
expect(callbacks.success).toHaveBeenCalled(); expect(callbacks.success).toHaveBeenCalled();
expect(callbacks.error).not.toHaveBeenCalled(); expect(callbacks.error).not.toHaveBeenCalled();
...@@ -60,10 +63,10 @@ describe('Poll', () => { ...@@ -60,10 +63,10 @@ describe('Poll', () => {
}); });
it('calls the error callback when the http request returns an error', done => { it('calls the error callback when the http request returns an error', done => {
mockServiceCall(service, { status: 500 }, true); mockServiceCall({ status: 500 }, true);
setup(); setup();
waitForAllCallsToFinish(service, 1, () => { waitForAllCallsToFinish(1, () => {
expect(callbacks.success).not.toHaveBeenCalled(); expect(callbacks.success).not.toHaveBeenCalled();
expect(callbacks.error).toHaveBeenCalled(); expect(callbacks.error).toHaveBeenCalled();
...@@ -72,10 +75,10 @@ describe('Poll', () => { ...@@ -72,10 +75,10 @@ describe('Poll', () => {
}); });
it('skips the error callback when request is aborted', done => { it('skips the error callback when request is aborted', done => {
mockServiceCall(service, { status: 0 }, true); mockServiceCall({ status: 0 }, true);
setup(); setup();
waitForAllCallsToFinish(service, 1, () => { waitForAllCallsToFinish(1, () => {
expect(callbacks.success).not.toHaveBeenCalled(); expect(callbacks.success).not.toHaveBeenCalled();
expect(callbacks.error).not.toHaveBeenCalled(); expect(callbacks.error).not.toHaveBeenCalled();
expect(callbacks.notification).toHaveBeenCalled(); expect(callbacks.notification).toHaveBeenCalled();
...@@ -85,7 +88,7 @@ describe('Poll', () => { ...@@ -85,7 +88,7 @@ describe('Poll', () => {
}); });
it('should call the success callback when the interval header is -1', done => { it('should call the success callback when the interval header is -1', done => {
mockServiceCall(service, { status: 200, headers: { 'poll-interval': -1 } }); mockServiceCall({ status: 200, headers: { 'poll-interval': -1 } });
setup() setup()
.then(() => { .then(() => {
expect(callbacks.success).toHaveBeenCalled(); expect(callbacks.success).toHaveBeenCalled();
...@@ -99,7 +102,7 @@ describe('Poll', () => { ...@@ -99,7 +102,7 @@ describe('Poll', () => {
describe('for 2xx status code', () => { describe('for 2xx status code', () => {
successCodes.forEach(httpCode => { successCodes.forEach(httpCode => {
it(`starts polling when http status is ${httpCode} and interval header is provided`, done => { it(`starts polling when http status is ${httpCode} and interval header is provided`, done => {
mockServiceCall(service, { status: httpCode, headers: { 'poll-interval': 1 } }); mockServiceCall({ status: httpCode, headers: { 'poll-interval': 1 } });
const Polling = new Poll({ const Polling = new Poll({
resource: service, resource: service,
...@@ -111,10 +114,10 @@ describe('Poll', () => { ...@@ -111,10 +114,10 @@ describe('Poll', () => {
Polling.makeRequest(); Polling.makeRequest();
waitForAllCallsToFinish(service, 2, () => { waitForAllCallsToFinish(2, () => {
Polling.stop(); Polling.stop();
expect(service.fetch.calls.count()).toEqual(2); expect(service.fetch.mock.calls).toHaveLength(2);
expect(service.fetch).toHaveBeenCalledWith({ page: 1 }); expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
expect(callbacks.success).toHaveBeenCalled(); expect(callbacks.success).toHaveBeenCalled();
expect(callbacks.error).not.toHaveBeenCalled(); expect(callbacks.error).not.toHaveBeenCalled();
...@@ -127,7 +130,7 @@ describe('Poll', () => { ...@@ -127,7 +130,7 @@ describe('Poll', () => {
describe('stop', () => { describe('stop', () => {
it('stops polling when method is called', done => { it('stops polling when method is called', done => {
mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } }); mockServiceCall({ status: 200, headers: { 'poll-interval': 1 } });
const Polling = new Poll({ const Polling = new Poll({
resource: service, resource: service,
...@@ -139,12 +142,12 @@ describe('Poll', () => { ...@@ -139,12 +142,12 @@ describe('Poll', () => {
errorCallback: callbacks.error, errorCallback: callbacks.error,
}); });
spyOn(Polling, 'stop').and.callThrough(); jest.spyOn(Polling, 'stop');
Polling.makeRequest(); Polling.makeRequest();
waitForAllCallsToFinish(service, 1, () => { waitForAllCallsToFinish(1, () => {
expect(service.fetch.calls.count()).toEqual(1); expect(service.fetch.mock.calls).toHaveLength(1);
expect(service.fetch).toHaveBeenCalledWith({ page: 1 }); expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
expect(Polling.stop).toHaveBeenCalled(); expect(Polling.stop).toHaveBeenCalled();
...@@ -155,8 +158,7 @@ describe('Poll', () => { ...@@ -155,8 +158,7 @@ describe('Poll', () => {
describe('enable', () => { describe('enable', () => {
it('should enable polling upon a response', done => { it('should enable polling upon a response', done => {
jasmine.clock().install(); mockServiceCall({ status: 200 });
const Polling = new Poll({ const Polling = new Poll({
resource: service, resource: service,
method: 'fetch', method: 'fetch',
...@@ -169,13 +171,10 @@ describe('Poll', () => { ...@@ -169,13 +171,10 @@ describe('Poll', () => {
response: { status: 200, headers: { 'poll-interval': 1 } }, response: { status: 200, headers: { 'poll-interval': 1 } },
}); });
jasmine.clock().tick(1); waitForAllCallsToFinish(1, () => {
jasmine.clock().uninstall();
waitForAllCallsToFinish(service, 1, () => {
Polling.stop(); Polling.stop();
expect(service.fetch.calls.count()).toEqual(1); expect(service.fetch.mock.calls).toHaveLength(1);
expect(service.fetch).toHaveBeenCalledWith({ page: 4 }); expect(service.fetch).toHaveBeenCalledWith({ page: 4 });
expect(Polling.options.data).toEqual({ page: 4 }); expect(Polling.options.data).toEqual({ page: 4 });
done(); done();
...@@ -185,7 +184,7 @@ describe('Poll', () => { ...@@ -185,7 +184,7 @@ describe('Poll', () => {
describe('restart', () => { describe('restart', () => {
it('should restart polling when its called', done => { it('should restart polling when its called', done => {
mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } }); mockServiceCall({ status: 200, headers: { 'poll-interval': 1 } });
const Polling = new Poll({ const Polling = new Poll({
resource: service, resource: service,
...@@ -193,23 +192,27 @@ describe('Poll', () => { ...@@ -193,23 +192,27 @@ describe('Poll', () => {
data: { page: 1 }, data: { page: 1 },
successCallback: () => { successCallback: () => {
Polling.stop(); Polling.stop();
// Let's pretend that we asynchronously restart this.
// setTimeout is mocked but this will actually get triggered
// in waitForAllCalssToFinish.
setTimeout(() => { setTimeout(() => {
Polling.restart({ data: { page: 4 } }); Polling.restart({ data: { page: 4 } });
}, 0); }, 1);
}, },
errorCallback: callbacks.error, errorCallback: callbacks.error,
}); });
spyOn(Polling, 'stop').and.callThrough(); jest.spyOn(Polling, 'stop');
spyOn(Polling, 'enable').and.callThrough(); jest.spyOn(Polling, 'enable');
spyOn(Polling, 'restart').and.callThrough(); jest.spyOn(Polling, 'restart');
Polling.makeRequest(); Polling.makeRequest();
waitForAllCallsToFinish(service, 2, () => { waitForAllCallsToFinish(2, () => {
Polling.stop(); Polling.stop();
expect(service.fetch.calls.count()).toEqual(2); expect(service.fetch.mock.calls).toHaveLength(2);
expect(service.fetch).toHaveBeenCalledWith({ page: 4 }); expect(service.fetch).toHaveBeenCalledWith({ page: 4 });
expect(Polling.stop).toHaveBeenCalled(); expect(Polling.stop).toHaveBeenCalled();
expect(Polling.enable).toHaveBeenCalled(); expect(Polling.enable).toHaveBeenCalled();
......
import { isSticky } from '~/lib/utils/sticky'; import { isSticky } from '~/lib/utils/sticky';
import { setHTMLFixture } from 'helpers/fixtures';
const TEST_OFFSET_TOP = 500;
describe('sticky', () => { describe('sticky', () => {
let el; let el;
let offsetTop;
beforeEach(() => { beforeEach(() => {
document.body.innerHTML += ` setHTMLFixture(
`
<div class="parent"> <div class="parent">
<div id="js-sticky"></div> <div id="js-sticky"></div>
</div> </div>
`; `,
);
offsetTop = TEST_OFFSET_TOP;
el = document.getElementById('js-sticky'); el = document.getElementById('js-sticky');
Object.defineProperty(el, 'offsetTop', {
get() {
return offsetTop;
},
});
}); });
afterEach(() => { afterEach(() => {
el.parentNode.remove(); el = null;
}); });
describe('when stuck', () => { describe('when stuck', () => {
...@@ -40,14 +52,13 @@ describe('sticky', () => { ...@@ -40,14 +52,13 @@ describe('sticky', () => {
describe('when not stuck', () => { describe('when not stuck', () => {
it('removes is-stuck class', () => { it('removes is-stuck class', () => {
spyOn(el.classList, 'remove').and.callThrough(); jest.spyOn(el.classList, 'remove');
isSticky(el, 0, el.offsetTop); isSticky(el, 0, el.offsetTop);
isSticky(el, 0, 0); isSticky(el, 0, 0);
expect(el.classList.remove).toHaveBeenCalledWith('is-stuck'); expect(el.classList.remove).toHaveBeenCalledWith('is-stuck');
expect(el.classList.contains('is-stuck')).toBe(false);
expect(el.classList.contains('is-stuck')).toBeFalsy();
}); });
it('does not add is-stuck class', () => { it('does not add is-stuck class', () => {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment