const helpers = require('./support/helpers');
function setFixtures (hierarchy) {
function templatesAndFoldersCheckboxesHTML () {
let result = '';
hierarchy.forEach((node, idx) => {
result += `
`;
});
return result;
};
const foldersCheckboxesHTML = function (filter) {
let count = 0;
// use closure to give all calls access to count
return function (nodes) {
let result = '';
nodes
.filter(node => node.type === 'folder')
.forEach(node => {
result += `
${node.label}
${node.children ? foldersCheckboxesHTML(node.children) : ''}
`;
count++;
});
return ``;
};
}();
function controlsHTML () {
return ``
};
document.body.innerHTML = `
`;
};
beforeAll(() => {
require('../../app/assets/javascripts/templateFolderForm.js');
});
afterAll(() => {
require('./support/teardown.js');
});
describe('TemplateFolderForm', () => {
const hierarchy = [
{
'label': 'Folder 1',
'type': 'folder',
'meta': '1 template, 1 folder',
'children': [
{
'label': 'Template 3',
'type': 'template',
'meta': 'Email template'
},
{
'label': 'Folder 2',
'type': 'folder',
'meta': 'Empty',
'children': []
}
]
},
{
'label': 'Template 1',
'type': 'Email template',
'meta': 'Email template'
},
{
'label': 'Template 2',
'type': 'template',
'meta': 'Email template'
}
];
let templateFolderForm;
let formControls;
let visibleCounter;
let hiddenCounter;
beforeAll(() => {
// stub out calls to sticky JS
GOVUK.stickAtBottomWhenScrolling = {
setMode: jest.fn(),
recalculate: jest.fn()
};
});
afterAll(() => {
GOVUK.stickAtBottomWhenScrolling = undefined;
});
beforeEach(() => {
setFixtures(hierarchy);
templateFolderForm = document.querySelector('form[data-module=template-folder-form]');
});
afterEach(() => {
document.body.innerHTML = '';
});
function getTemplateFolderCheckboxes () {
return templateFolderForm.querySelectorAll('input[type=checkbox]');
};
function getVisibleCounter () {
return formControls.querySelector('.template-list-selected-counter__count');
};
function getHiddenCounter () {
return formControls.querySelector('[role=status]');
};
describe("When the page loads", () => {
beforeEach(() => {
// start module
window.GOVUK.modules.start();
formControls = templateFolderForm.querySelector('#sticky_template_forms');
visibleCounter = getVisibleCounter();
});
test("the default controls and the counter should be showing", () => {
expect(document.querySelector('button[value=add-new-template]')).not.toBeNull();
expect(document.querySelector('button[value=add-new-folder]')).not.toBeNull();
expect(visibleCounter).not.toBeNull();
});
// Our counter needs to be wrapped in an ARIA live region so changes to its content are
// communicated to assistive tech'.
// ARIA live regions need to be in the HTML before JS loads.
// Because of this, we have a counter, in a live region, in the page when it loads, and
// a duplicate, visible, one in the HTML the module adds to the page.
// We hide the one in the live region to avoid duplication of it's content.
describe("Selection counter", () => {
beforeEach(() => {
hiddenCounter = getHiddenCounter();
})
test("the visible counter should be hidden from assistive tech", () => {
expect(visibleCounter.getAttribute('aria-hidden')).toEqual("true");
});
test("the content of both visible and hidden counters should match", () => {
expect(visibleCounter.textContent.trim()).toEqual(hiddenCounter.textContent.trim());
});
});
});
describe("Clicking 'New template'", () => {
beforeEach(() => {
// start module
window.GOVUK.modules.start();
formControls = templateFolderForm.querySelector('#sticky_template_forms');
helpers.triggerEvent(formControls.querySelector('[value=add-new-template]'), 'click');
});
test("should show options for all the types of template", () => {
const options = [
'Email', 'Text message', 'Letter', 'Copy an existing template'
];
const labels = Array.from(formControls.querySelectorAll('label'));
const radios = Array.from(formControls.querySelectorAll('input[type=radio]'));
options.forEach(option => {
let matchingLabels = labels.filter(label => label.textContent.trim() === option);
expect(matchingLabels.length > 0).toBe(true);
let matchingRadio = formControls.querySelector(`#${matchingLabels[0].getAttribute('for')}`)
expect(matchingRadio).not.toBeNull();
});;
});
test("should show a 'Cancel' link", () => {
const cancelLink = formControls.querySelector('.js-cancel');
expect(cancelLink).not.toBeNull();
});
test("should focus the fieldset", () => {
expect(document.activeElement).toBe(formControls.querySelector('fieldset'));
});
describe("When the 'Cancel' link is clicked", () => {
let addNewTemplateButton;
beforeEach(() => {
helpers.triggerEvent(formControls.querySelector('.js-cancel'), 'click');
addNewTemplateButton = formControls.querySelector('[value=add-new-template]');
});
test("the controls should reset", () => {
expect(addNewTemplateButton).not.toBeNull();
});
test("the add new template control should be focused", () => {
expect(document.activeElement).toBe(addNewTemplateButton);
});
});
});
describe("Clicking 'New folder'", () => {
let textbox;
beforeEach(() => {
// start module
window.GOVUK.modules.start();
formControls = templateFolderForm.querySelector('#sticky_template_forms');
helpers.triggerEvent(formControls.querySelector('[value=add-new-folder]'), 'click');
textbox = formControls.querySelector('input[type=text]');
});
test("should show a textbox for the folder name", () => {
expect(textbox).not.toBeNull();
// check textbox has a label
expect(formControls.querySelector(`label[for=${textbox.getAttribute('id')}]`)).not.toBeNull();
});
test("should focus the textbox", () => {
expect(document.activeElement).toBe(textbox);
});
describe("When the 'Cancel' link is clicked", () => {
let addNewFolderButton;
beforeEach(() => {
helpers.triggerEvent(formControls.querySelector('.js-cancel'), 'click');
addNewFolderButton = formControls.querySelector('button[value=add-new-folder]');
});
test("the controls should reset", () => {
expect(addNewFolderButton).not.toBeNull();
});
test("the control for adding a new folder should be focused", () => {
expect(document.activeElement).toBe(addNewFolderButton);
});
});
});
describe("When some templates/folders are selected", () => {
let templateFolderCheckboxes;
beforeEach(() => {
// start module
window.GOVUK.modules.start();
templateFolderCheckboxes = getTemplateFolderCheckboxes();
formControls = templateFolderForm.querySelector('#sticky_template_forms');
helpers.triggerEvent(templateFolderCheckboxes[0], 'click');
helpers.triggerEvent(templateFolderCheckboxes[2], 'click');
});
test("the buttons for moving to a new or existing folder are showing", () => {
expect(formControls.querySelector('button[value=move-to-new-folder]')).not.toBeNull();
expect(formControls.querySelector('button[value=move-to-existing-folder]')).not.toBeNull();
});
describe("Clear link", () => {
let clearLink;
beforeEach(() => {
clearLink = formControls.querySelector('.js-cancel');
});
test("the link has been added with the right text", () => {
expect(clearLink).not.toBeNull();
expect(clearLink.textContent.trim()).toEqual('Clear selection');
});
test("clicking the link clears the selection", () => {
helpers.triggerEvent(clearLink, 'click');
const checkedCheckboxes = Array.from(templateFolderCheckboxes).filter(checkbox => checkbox.checked);
expect(checkedCheckboxes.length === 0).toBe(true);
});
});
describe("Selection counter", () => {
let visibleCounterText;
let hiddenCounterText;
beforeEach(() => {
visibleCounterText = getVisibleCounter().textContent.trim();
hiddenCounterText = getHiddenCounter().textContent.trim();
});
test("the content of both visible and hidden counters should match", () => {
expect(visibleCounterText).toEqual(hiddenCounterText);
});
test("the content of the counter should reflect the selection", () => {
expect(visibleCounterText).toEqual('2 selected');
});
});
describe("Clicking the 'Move' button", () => {
beforeEach(() => {
helpers.triggerEvent(formControls.querySelector('[value=move-to-existing-folder]'), 'click');
});
test("should show radios for all the folders in the hierarchy", () => {
const foldersInHierarchy = [];
function getFolders (nodes) {
nodes.forEach(node => {
if (node.type === 'folder') {
foldersInHierarchy.push(node.label);
if (node.children.length) { getFolders(node.children) }
}
});
};
getFolders(hierarchy);
const folderLabels = Array.from(formControls.querySelectorAll('#move_to label'))
.filter(label => label.textContent.trim() !== 'Templates');
expect(folderLabels.map(label => label.textContent.trim())).toEqual(foldersInHierarchy);
const radiosForLabels = folderLabels
.map(label => formControls.querySelector(`#${label.getAttribute('for')}`))
.filter(radio => radio !== null);
expect(radiosForLabels.length).toEqual(foldersInHierarchy.length);
});
test("focus the 'Choose a folder' fieldset", () => {
expect(document.activeElement).toBe(formControls.querySelector('#move_to'));
});
describe("When the 'Cancel' link is clicked", () => {
let moveToFolderButton;
beforeEach(() => {
helpers.triggerEvent(formControls.querySelector('.js-cancel'), 'click');
moveToFolderButton = formControls.querySelector('button[value=move-to-existing-folder]');
});
test("the controls should reset", () => {
expect(moveToFolderButton).not.toBeNull();
});
test("the control for moving to an existing folder should be focused", () => {
expect(document.activeElement).toBe(moveToFolderButton);
});
});
});
describe("Clicking the 'Add to new folder' button", () => {
let textbox;
beforeEach(() => {
helpers.triggerEvent(formControls.querySelector('[value=move-to-new-folder]'), 'click');
textbox = formControls.querySelector('input[type=text]');
});
test("should show a textbox for the folder name", () => {
expect(textbox).not.toBeNull();
// check textbox has a label
expect(formControls.querySelector(`label[for=${textbox.getAttribute('id')}]`)).not.toBeNull();
});
test("should focus the textbox", () => {
expect(document.activeElement).toBe(textbox);
});
describe("When the 'Cancel' link is clicked", () => {
let moveToNewFolderButton;
beforeEach(() => {
helpers.triggerEvent(formControls.querySelector('.js-cancel'), 'click');
moveToNewFolderButton = formControls.querySelector('button[value=move-to-new-folder]');
});
test("the controls should reset", () => {
expect(moveToNewFolderButton).not.toBeNull();
});
test("the control for adding a new folder should be focused", () => {
expect(document.activeElement).toBe(moveToNewFolderButton);
});
});
});
});
});