From 60389d7087f852bf8baec349366519efd75563e9 Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Thu, 11 Jul 2019 18:59:05 +0100 Subject: [PATCH 1/7] Add tests for templateFolderForm JS interactions --- tests/javascripts/templateFolderForm.test.js | 645 +++++++++++++++++++ 1 file changed, 645 insertions(+) create mode 100644 tests/javascripts/templateFolderForm.test.js diff --git a/tests/javascripts/templateFolderForm.test.js b/tests/javascripts/templateFolderForm.test.js new file mode 100644 index 000000000..d04be551c --- /dev/null +++ b/tests/javascripts/templateFolderForm.test.js @@ -0,0 +1,645 @@ +const helpers = require('./support/helpers'); + +function setFixtures (hierarchy) { + + function templatesAndFoldersCheckboxesHTML () { + let result = ''; + + hierarchy.forEach((node, idx) => { + + result += ` +
+
+ + +
+

+ + ${node.label} + +

+ ${node.meta} +
`; + + }); + + 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.children ? foldersCheckboxesHTML(node.children) : ''} +
  • `; + count++; + }); + + return ``; + }; + + }(); + + function controlsHTML () { + + return `
    + +
    +
    +
    +
    + + Choose a folder + +
    + ${foldersCheckboxesHTML(hierarchy)} +
    +
    +
    +
    +
    + +
    +
    +
    +
    + Add to new folder +
    + + +
    + +
    +
    +
    +
    + Add new folder +
    + + +
    + +
    +
    +
    +
    +
    +
    + + New template + +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    +
    + Nothing selected +
    +
    ` + }; + + document.body.innerHTML = ` +
    + ${templatesAndFoldersCheckboxesHTML()} + ${controlsHTML()} +
    `; + +}; + +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); + + }); + + }); + + }); + + }); + +}); From d0b6f844cee0fcd07fb4ea026bf79613ff328771 Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Thu, 11 Jul 2019 14:28:13 +0100 Subject: [PATCH 2/7] Move focus of legends to their fieldsets When testing with the JAWS screenreader, we found a bug around getting it to announce the name of a fieldset when we ask the user to select from it. Bug on pivotal: https://www.pivotaltracker.com/story/show/165565088 The flows for adding a new template and moving a template/folder both need the user to select an option from a radio group. When we add the radio group to the UI, we need to move focus to it so the user is in the right place to choose an option. The expectation of the original code was that focusing the field set's legend would work like focusing the heading of a section of content and announce the label of it. This didn't happen with JAWS. This tries to achieve the same by focusing the whole fieldset instead. When doing this we also hide the focus style, to follow the convention for this across www.gov.uk. --- app/assets/javascripts/templateFolderForm.js | 4 ++-- .../stylesheets/components/stick-at-top-when-scrolling.scss | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/templateFolderForm.js b/app/assets/javascripts/templateFolderForm.js index 6aa8f6f74..b85ab7ead 100644 --- a/app/assets/javascripts/templateFolderForm.js +++ b/app/assets/javascripts/templateFolderForm.js @@ -19,10 +19,10 @@ this.states = [ {key: 'nothing-selected-buttons', $el: this.$form.find('#nothing_selected'), cancellable: false}, {key: 'items-selected-buttons', $el: this.$form.find('#items_selected'), cancellable: false}, - {key: 'move-to-existing-folder', $el: this.$form.find('#move_to_folder_radios'), cancellable: true, setFocus: this.getFocusRoutine('#move_to_folder_radios legend', true)}, + {key: 'move-to-existing-folder', $el: this.$form.find('#move_to_folder_radios'), cancellable: true, setFocus: this.getFocusRoutine('#move_to_folder_radios fieldset', true)}, {key: 'move-to-new-folder', $el: this.$form.find('#move_to_new_folder_form'), cancellable: true, setFocus: this.getFocusRoutine('#move_to_new_folder_name', false)}, {key: 'add-new-folder', $el: this.$form.find('#add_new_folder_form'), cancellable: true, setFocus: this.getFocusRoutine('#add_new_folder_name', false)}, - {key: 'add-new-template', $el: this.$form.find('#add_new_template_form'), cancellable: true, setFocus: this.getFocusRoutine('#add_new_template_form legend', true)} + {key: 'add-new-template', $el: this.$form.find('#add_new_template_form'), cancellable: true, setFocus: this.getFocusRoutine('#add_new_template_form fieldset', true)} ]; // cancel/clear buttons only relevant if JS enabled, so diff --git a/app/assets/stylesheets/components/stick-at-top-when-scrolling.scss b/app/assets/stylesheets/components/stick-at-top-when-scrolling.scss index c4d39a2e3..3e5f7eb11 100644 --- a/app/assets/stylesheets/components/stick-at-top-when-scrolling.scss +++ b/app/assets/stylesheets/components/stick-at-top-when-scrolling.scss @@ -48,6 +48,10 @@ $sticky-padding: $gutter * 2 / 3; margin-top: ($sticky-padding * 2) * -1; } + fieldset:focus { + outline: none; + } + .page-footer { margin-bottom: 0; min-height: 50px; From f495288034b138d3d33af6cb80ddd82d7c4c0c79 Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Fri, 12 Jul 2019 16:00:34 +0100 Subject: [PATCH 3/7] Add tests for integration with sticky JS --- tests/javascripts/templateFolderForm.test.js | 135 ++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/tests/javascripts/templateFolderForm.test.js b/tests/javascripts/templateFolderForm.test.js index d04be551c..6717f2b11 100644 --- a/tests/javascripts/templateFolderForm.test.js +++ b/tests/javascripts/templateFolderForm.test.js @@ -157,6 +157,13 @@ function setFixtures (hierarchy) { }; +function resetStickyMocks () { + + GOVUK.stickAtBottomWhenScrolling.recalculate.mockClear(); + GOVUK.stickAtBottomWhenScrolling.setMode.mockClear(); + +}; + beforeAll(() => { require('../../app/assets/javascripts/templateFolderForm.js'); }); @@ -245,6 +252,22 @@ describe('TemplateFolderForm', () => { return formControls.querySelector('[role=status]'); }; + describe("Before the page loads", () => { + + // We need parts of the module to be made sticky, but by the module code, + // not the sticky JS code that operates on the HTML at page load. + // Because of this, they wll need to be marked with classes + test("the HTML for the module should contain placeholder classes on each part that needs to be sticky", () => { + + expect(templateFolderForm.querySelectorAll('#move_to_folder_radios > .js-will-stick-at-bottom-when-scrolling').length).toEqual(2); + expect(templateFolderForm.querySelector('#move_to_new_folder_form > .js-will-stick-at-bottom-when-scrolling')).not.toBeNull(); + expect(templateFolderForm.querySelector('#add_new_folder_form > .js-will-stick-at-bottom-when-scrolling')).not.toBeNull(); + expect(templateFolderForm.querySelectorAll('#add_new_template_form > .js-will-stick-at-bottom-when-scrolling').length).toEqual(2); + + }); + + }); + describe("When the page loads", () => { beforeEach(() => { @@ -257,6 +280,8 @@ describe('TemplateFolderForm', () => { }); + afterEach(() => resetStickyMocks()); + test("the default controls and the counter should be showing", () => { expect(document.querySelector('button[value=add-new-template]')).not.toBeNull(); @@ -293,6 +318,20 @@ describe('TemplateFolderForm', () => { }); + test("should make the current controls sticky", () => { + + // the class the sticky JS hooks into should be present + expect(formControls.querySelector('#nothing_selected .js-stick-at-bottom-when-scrolling')).not.toBeNull(); + + // .recalculate should have been called so the sticky JS picks up the controls + expect(GOVUK.stickAtBottomWhenScrolling.recalculate.mock.calls.length).toEqual(1); + + // mode should have been set to 'default' as the controls only have one part + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls.length).toEqual(1); + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls[0][0]).toEqual('default'); + + }); + }); describe("Clicking 'New template'", () => { @@ -304,10 +343,15 @@ describe('TemplateFolderForm', () => { formControls = templateFolderForm.querySelector('#sticky_template_forms'); + // reset sticky JS mocks called when the module starts + resetStickyMocks(); + helpers.triggerEvent(formControls.querySelector('[value=add-new-template]'), 'click'); }); + afterEach(() => resetStickyMocks()); + test("should show options for all the types of template", () => { const options = [ @@ -325,7 +369,7 @@ describe('TemplateFolderForm', () => { let matchingRadio = formControls.querySelector(`#${matchingLabels[0].getAttribute('for')}`) expect(matchingRadio).not.toBeNull(); - });; + }); }); @@ -343,12 +387,29 @@ describe('TemplateFolderForm', () => { }); + test("should make the current controls sticky", () => { + + // the classes the sticky JS hooks into should be present for both parts + expect(formControls.querySelectorAll('#add_new_template_form .js-stick-at-bottom-when-scrolling').length).toEqual(2); + + // .recalculate should have been called so the sticky JS picks up the controls + expect(GOVUK.stickAtBottomWhenScrolling.recalculate.mock.calls.length).toEqual(1); + + // the mode should be set to 'dialog' so both parts can be sticky + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls.length).toEqual(1); + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls[0][0]).toEqual('dialog'); + + }); + describe("When the 'Cancel' link is clicked", () => { let addNewTemplateButton; beforeEach(() => { + // reset sticky JS mocks called when the new template state loaded + resetStickyMocks(); + helpers.triggerEvent(formControls.querySelector('.js-cancel'), 'click'); addNewTemplateButton = formControls.querySelector('[value=add-new-template]'); @@ -375,6 +436,8 @@ describe('TemplateFolderForm', () => { let textbox; + afterEach(() => resetStickyMocks()); + beforeEach(() => { // start module @@ -382,6 +445,9 @@ describe('TemplateFolderForm', () => { formControls = templateFolderForm.querySelector('#sticky_template_forms'); + // reset sticky JS mocks called when the module starts + resetStickyMocks(); + helpers.triggerEvent(formControls.querySelector('[value=add-new-folder]'), 'click'); textbox = formControls.querySelector('input[type=text]'); @@ -403,6 +469,20 @@ describe('TemplateFolderForm', () => { }); + test("should make the current controls sticky", () => { + + // the class the sticky JS hooks into should be present + expect(formControls.querySelector('#add_new_folder_form .js-stick-at-bottom-when-scrolling')).not.toBeNull(); + + // .recalculate should have been called so the sticky JS picks up the controls + expect(GOVUK.stickAtBottomWhenScrolling.recalculate.mock.calls.length).toEqual(1); + + // mode should have been set to 'default' as the controls only have one part + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls.length).toEqual(1); + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls[0][0]).toEqual('default'); + + }); + describe("When the 'Cancel' link is clicked", () => { let addNewFolderButton; @@ -444,11 +524,16 @@ describe('TemplateFolderForm', () => { formControls = templateFolderForm.querySelector('#sticky_template_forms'); + // reset sticky JS mocks called when the module starts + resetStickyMocks(); + helpers.triggerEvent(templateFolderCheckboxes[0], 'click'); helpers.triggerEvent(templateFolderCheckboxes[2], 'click'); }); + afterEach(() => resetStickyMocks()); + test("the buttons for moving to a new or existing folder are showing", () => { expect(formControls.querySelector('button[value=move-to-new-folder]')).not.toBeNull(); @@ -456,6 +541,20 @@ describe('TemplateFolderForm', () => { }); + test("should make the current controls sticky", () => { + + // the class the sticky JS hooks into should be present + expect(formControls.querySelector('#items_selected .js-stick-at-bottom-when-scrolling')).not.toBeNull(); + + // .recalculate should have been called so the sticky JS picks up the controls + expect(GOVUK.stickAtBottomWhenScrolling.recalculate.mock.calls.length).toEqual(1); + + // mode should have been set to 'default' as the controls only have one part + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls.length).toEqual(1); + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls[0][0]).toEqual('default'); + + }); + describe("Clear link", () => { let clearLink; @@ -515,6 +614,9 @@ describe('TemplateFolderForm', () => { beforeEach(() => { + // reset sticky JS mocks called when a selection was made + resetStickyMocks(); + helpers.triggerEvent(formControls.querySelector('[value=move-to-existing-folder]'), 'click'); }); @@ -557,6 +659,20 @@ describe('TemplateFolderForm', () => { }); + test("should make the current controls sticky", () => { + + // the classes the sticky JS hooks into should be present for both parts + expect(formControls.querySelectorAll('#move_to_folder_radios .js-stick-at-bottom-when-scrolling').length).toEqual(2); + + // .recalculate should have been called so the sticky JS picks up the controls + expect(GOVUK.stickAtBottomWhenScrolling.recalculate.mock.calls.length).toEqual(1); + + // the mode should be set to 'dialog' so both parts can be sticky + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls.length).toEqual(1); + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls[0][0]).toEqual('dialog'); + + }); + describe("When the 'Cancel' link is clicked", () => { let moveToFolderButton; @@ -591,6 +707,9 @@ describe('TemplateFolderForm', () => { beforeEach(() => { + // reset sticky JS mocks called when a selection was made + resetStickyMocks(); + helpers.triggerEvent(formControls.querySelector('[value=move-to-new-folder]'), 'click'); textbox = formControls.querySelector('input[type=text]'); @@ -612,6 +731,20 @@ describe('TemplateFolderForm', () => { }); + test("should make the current controls sticky", () => { + + // the class the sticky JS hooks into should be present + expect(formControls.querySelector('#move_to_new_folder_form .js-stick-at-bottom-when-scrolling')).not.toBeNull(); + + // .recalculate should have been called so the sticky JS picks up the controls + expect(GOVUK.stickAtBottomWhenScrolling.recalculate.mock.calls.length).toEqual(1); + + // mode should have been set to 'default' as the controls only have one part + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls.length).toEqual(1); + expect(GOVUK.stickAtBottomWhenScrolling.setMode.mock.calls[0][0]).toEqual('default'); + + }); + describe("When the 'Cancel' link is clicked", () => { let moveToNewFolderButton; From 5bd1e4bb7e33b68d25e09d21e5b4508bbfe65ffa Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Tue, 20 Aug 2019 16:15:11 +0100 Subject: [PATCH 4/7] Move template list fixture function into helpers This will be needed in other tests so should be made into a helper. --- tests/javascripts/support/helpers.js | 1 + tests/javascripts/support/helpers/html.js | 26 +++++++++++++++++++ tests/javascripts/templateFolderForm.test.js | 27 +------------------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/tests/javascripts/support/helpers.js b/tests/javascripts/support/helpers.js index 65c7f6a0d..fe5adf08e 100644 --- a/tests/javascripts/support/helpers.js +++ b/tests/javascripts/support/helpers.js @@ -13,6 +13,7 @@ exports.RangeMock = domInterfaces.RangeMock; exports.SelectionMock = domInterfaces.SelectionMock; exports.getRadioGroup = html.getRadioGroup; exports.getRadios = html.getRadios; +exports.templatesAndFoldersCheckboxes = html.templatesAndFoldersCheckboxes; exports.element = elements.element; exports.WindowMock = rendering.WindowMock; exports.ScreenMock = rendering.ScreenMock; diff --git a/tests/javascripts/support/helpers/html.js b/tests/javascripts/support/helpers/html.js index 06dfa758d..7b3d78eb4 100644 --- a/tests/javascripts/support/helpers/html.js +++ b/tests/javascripts/support/helpers/html.js @@ -33,5 +33,31 @@ function getRadioGroup (data) { return radioGroup; }; +function templatesAndFoldersCheckboxes (hierarchy) { + let result = ''; + + hierarchy.forEach((node, idx) => { + + result += ` +
    +
    + + +
    +

    + + ${node.label} + +

    + ${node.meta} +
    `; + + }); + + return result; + +}; + exports.getRadios = getRadios; exports.getRadioGroup = getRadioGroup; +exports.templatesAndFoldersCheckboxes = templatesAndFoldersCheckboxes; diff --git a/tests/javascripts/templateFolderForm.test.js b/tests/javascripts/templateFolderForm.test.js index 6717f2b11..00dd8164a 100644 --- a/tests/javascripts/templateFolderForm.test.js +++ b/tests/javascripts/templateFolderForm.test.js @@ -2,31 +2,6 @@ const helpers = require('./support/helpers'); function setFixtures (hierarchy) { - function templatesAndFoldersCheckboxesHTML () { - let result = ''; - - hierarchy.forEach((node, idx) => { - - result += ` -
    -
    - - -
    -

    - - ${node.label} - -

    - ${node.meta} -
    `; - - }); - - return result; - - }; - const foldersCheckboxesHTML = function (filter) { let count = 0; @@ -151,7 +126,7 @@ function setFixtures (hierarchy) { document.body.innerHTML = `
    - ${templatesAndFoldersCheckboxesHTML()} + ${helpers.templatesAndFoldersCheckboxes(hierarchy)} ${controlsHTML()}
    `; From 8b9c8f2f9585d2948d6a33e55312b0ca86ef2a59 Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Tue, 1 Oct 2019 08:25:25 +0100 Subject: [PATCH 5/7] Fix typo in comment --- tests/javascripts/templateFolderForm.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/javascripts/templateFolderForm.test.js b/tests/javascripts/templateFolderForm.test.js index 00dd8164a..1e3c45d20 100644 --- a/tests/javascripts/templateFolderForm.test.js +++ b/tests/javascripts/templateFolderForm.test.js @@ -231,7 +231,7 @@ describe('TemplateFolderForm', () => { // We need parts of the module to be made sticky, but by the module code, // not the sticky JS code that operates on the HTML at page load. - // Because of this, they wll need to be marked with classes + // Because of this, they will need to be marked with classes test("the HTML for the module should contain placeholder classes on each part that needs to be sticky", () => { expect(templateFolderForm.querySelectorAll('#move_to_folder_radios > .js-will-stick-at-bottom-when-scrolling').length).toEqual(2); From b6b1e7682bd6e88cefcc22905e43ce97a76a76f2 Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Tue, 1 Oct 2019 14:10:22 +0100 Subject: [PATCH 6/7] Rename the 'clear selection' link In response to the sensible comments here: https://github.com/alphagov/notifications-admin/pull/3053#discussion_r329957545 --- tests/javascripts/templateFolderForm.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/javascripts/templateFolderForm.test.js b/tests/javascripts/templateFolderForm.test.js index 1e3c45d20..964771632 100644 --- a/tests/javascripts/templateFolderForm.test.js +++ b/tests/javascripts/templateFolderForm.test.js @@ -530,7 +530,7 @@ describe('TemplateFolderForm', () => { }); - describe("Clear link", () => { + describe("'Clear selection' link", () => { let clearLink; From 0543dca23d314d38b329047d77b9efe8ff6ef293 Mon Sep 17 00:00:00 2001 From: Tom Byers Date: Tue, 1 Oct 2019 14:19:06 +0100 Subject: [PATCH 7/7] Improve description of cancel link tests --- tests/javascripts/templateFolderForm.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/javascripts/templateFolderForm.test.js b/tests/javascripts/templateFolderForm.test.js index 964771632..4439d45c5 100644 --- a/tests/javascripts/templateFolderForm.test.js +++ b/tests/javascripts/templateFolderForm.test.js @@ -376,7 +376,7 @@ describe('TemplateFolderForm', () => { }); - describe("When the 'Cancel' link is clicked", () => { + describe("When the 'Cancel' link is clicked after choosing to add a new template", () => { let addNewTemplateButton; @@ -458,7 +458,7 @@ describe('TemplateFolderForm', () => { }); - describe("When the 'Cancel' link is clicked", () => { + describe("When the 'Cancel' link is clicked after choosing to add a new folder", () => { let addNewFolderButton; @@ -648,7 +648,7 @@ describe('TemplateFolderForm', () => { }); - describe("When the 'Cancel' link is clicked", () => { + describe("When the 'Cancel' link is clicked after choosing to move a template or folder", () => { let moveToFolderButton; @@ -720,7 +720,7 @@ describe('TemplateFolderForm', () => { }); - describe("When the 'Cancel' link is clicked", () => { + describe("When the 'Cancel' link is clicked after choosing to add a template or folder to a new folder", () => { let moveToNewFolderButton;