Files
notifications-admin/tests/javascripts/updateStatus.test.js
Chris Hill-Scott d452c0081d Add throttling to AJAX calls
The endpoint that count characters should be pretty low-load because it
won’t talk to the database (unless, on the first request, the user and
service aren’t cached in Redis).

The response size is also very small, only one line of text wrapped in a
single `<span>`, so won’t be as CPU-intensive to render as a whole page.

Still, we don’t want to completely hammer the server if a user types
very quickly.

This commit adds some throttling, so that we wait until there’s a
certain amount of delay between keystrokes before firing off the request
to the backend.

I’ve set the delay at 150ms. At normal typing speed this makes the lag
feel fairly imperceptible – it feels like you get an updated count in
response to most keystrokes. It’s only if you really mash the keyboard
that the count won’t update until you take a breath.
2021-01-08 12:49:05 +00:00

165 lines
4.1 KiB
JavaScript

const each = require('jest-each').default;
const jestDateMock = require('jest-date-mock');
const helpers = require('./support/helpers.js');
const serviceNumber = '6658542f-0cad-491f-bec8-ab8457700ead';
const updatesURL = `/services/${serviceNumber}/templates/count-sms-length`;
let responseObj = {};
let jqueryAJAXReturnObj;
beforeAll(() => {
// ensure all timers go through Jest
jest.useFakeTimers();
// mock the bits of jQuery used
jest.spyOn(window.$, 'ajax');
// set up the object returned from $.ajax so it responds with whatever responseObj is set to
jqueryAJAXReturnObj = {
done: callback => {
// For these tests the server responds immediately
callback(responseObj);
return jqueryAJAXReturnObj;
},
fail: () => {}
};
$.ajax.mockImplementation(() => jqueryAJAXReturnObj);
require('../../app/assets/javascripts/updateStatus.js');
});
afterAll(() => {
require('./support/teardown.js');
});
describe('Update content', () => {
beforeEach(() => {
document.body.innerHTML = `
<form>
<input type="hidden" name="csrf_token" value="abc123" />
<label for="template_content" id="template-content-label">Template content<label>
<textarea name="template_content" id="template_content" aria-described-by="template-content-label">Content of message</textarea>
</form>
<div data-module="update-status" data-updates-url="${updatesURL}" data-target="template_content">
Initial content
</div>
`;
});
afterEach(() => {
document.body.innerHTML = '';
// tidy up record of mocked AJAX calls
$.ajax.mockClear();
// ensure any timers set by continually starting the module are cleared
jest.clearAllTimers();
});
test("It should add attributes to the elements", () => {
window.GOVUK.modules.start();
expect(
document.querySelectorAll('[data-module=update-status]')[0].id
).toEqual(
"update-status"
);
expect(
document.getElementById('template_content').getAttribute('aria-described-by')
).toEqual(
"template-content-label update-status"
);
});
test("It should make requests to the URL specified in the data-updates-url attribute", () => {
window.GOVUK.modules.start();
expect($.ajax.mock.calls[0][0]).toEqual(updatesURL);
expect($.ajax.mock.calls[0]).toEqual([
updatesURL,
{
"data": "csrf_token=abc123&template_content=Content%20of%20message",
"method": "post"
}
]);
});
test("It should replace the content of the div with the returned HTML", () => {
responseObj = {'html': 'Updated content'}
expect(
document.querySelectorAll('[data-module=update-status]')[0].textContent.trim()
).toEqual(
"Initial content"
);
window.GOVUK.modules.start();
expect(
document.querySelectorAll('[data-module=update-status]')[0].textContent.trim()
).toEqual(
"Updated content"
);
});
test("It should fire when the content of the textarea changes", () => {
let textarea = document.getElementById('template_content');
// Initial update triggered
window.GOVUK.modules.start();
expect($.ajax.mock.calls.length).toEqual(1);
// 150ms of inactivity
jest.advanceTimersByTime(150);
helpers.triggerEvent(textarea, 'input');
expect($.ajax.mock.calls.length).toEqual(2);
});
test("It should fire only after 150ms of inactivity", () => {
let textarea = document.getElementById('template_content');
// Initial update triggered
window.GOVUK.modules.start();
expect($.ajax.mock.calls.length).toEqual(1);
helpers.triggerEvent(textarea, 'input');
jest.advanceTimersByTime(149);
expect($.ajax.mock.calls.length).toEqual(1);
helpers.triggerEvent(textarea, 'input');
jest.advanceTimersByTime(149);
expect($.ajax.mock.calls.length).toEqual(1);
helpers.triggerEvent(textarea, 'input');
jest.advanceTimersByTime(149);
expect($.ajax.mock.calls.length).toEqual(1);
// > 150ms of inactivity
jest.advanceTimersByTime(1);
expect($.ajax.mock.calls.length).toEqual(2);
});
});