2019-07-11 18:59:05 +01:00
const helpers = require ( './support/helpers' );
2020-08-11 15:44:17 +01:00
function setFixtures ( hierarchy , newTemplateDataModules = "" ) {
2019-07-11 18:59:05 +01:00
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 += `<li class="multiple-choice">
<input id="node- ${ count } " name="move_to" type="radio" value="node- ${ count } ">
<label class="block-label" for="node- ${ count } ">
${ node . label }
</label>
${ node . children ? foldersCheckboxesHTML ( node . children ) : '' }
</li>` ;
count ++ ;
});
return `<ul> ${ result } </ul>` ;
};
}();
2020-08-11 15:44:17 +01:00
function controlsHTML ( newTemplateDataModules ) {
2019-07-11 18:59:05 +01:00
return `<div id="sticky_template_forms">
<button type="submit" name="operation" value="unknown" hidden=""></button>
2020-09-22 11:45:55 +01:00
<div id="move_to_folder_radios" class="sticky-template-form" role="region" aria-label="Choose the folder to move selected items to">
2019-07-11 18:59:05 +01:00
<div class="js-will-stick-at-bottom-when-scrolling">
<div class="form-group ">
<fieldset id="move_to">
<legend class="form-label">
Choose a folder
</legend>
<div class="radios-nested">
${ foldersCheckboxesHTML ( hierarchy ) }
</div>
</fieldset>
</div>
</div>
<div class="js-will-stick-at-bottom-when-scrolling">
<div class="page-footer">
2020-02-06 10:50:57 +00:00
<button type="submit" class="govuk-button govuk-button--secondary govuk-!-margin-right-3 govuk-!-margin-bottom-1" name="operation" value="move-to-existing-folder">Move</button>
2019-07-11 18:59:05 +01:00
</div>
</div>
</div>
2020-09-22 11:45:55 +01:00
<div id="move_to_new_folder_form" class="sticky-template-form" role="region" aria-label="Enter name of the new folder to move selected items to">
<div class="js-will-stick-at-bottom-when-scrolling">
2020-08-05 11:57:00 +01:00
<div class="govuk-form-group">
<label class="govuk-label" for="move_to_new_folder_name">
2019-07-11 18:59:05 +01:00
Folder name
</label>
2020-08-05 11:57:00 +01:00
<input class="govuk-input govuk-!-width-full" id="move_to_new_folder_name" name="move_to_new_folder_name" type="text">
2019-07-11 18:59:05 +01:00
</div>
<div class="page-footer">
2020-02-06 10:50:57 +00:00
<button type="submit" class="govuk-button govuk-button--secondary govuk-!-margin-bottom-1" name="operation" value="move-to-new-folder">Add to new folder</button>
2019-07-11 18:59:05 +01:00
</div>
2020-09-22 11:45:55 +01:00
</div>
2019-07-11 18:59:05 +01:00
</div>
2020-09-22 11:45:55 +01:00
<div id="add_new_folder_form" class="sticky-template-form" role="region" aria-label="Enter name of the new folder">
<div class="js-will-stick-at-bottom-when-scrolling">
2020-08-05 11:57:00 +01:00
<div class="govuk-form-group">
<label class="govuk-label" for="add_new_folder_name">
2019-07-11 18:59:05 +01:00
Folder name
</label>
2020-08-05 11:57:00 +01:00
<input class="govuk-input govuk-!-width-full" id="add_new_folder_name" name="add_new_folder_name" type="text">
2019-07-11 18:59:05 +01:00
</div>
<div class="page-footer">
2020-02-06 10:50:57 +00:00
<button type="submit" class="govuk-button page-footer__button" name="operation" value="add-new-folder">Add new folder</button>
2019-07-11 18:59:05 +01:00
</div>
2020-09-22 11:45:55 +01:00
</div>
2019-07-11 18:59:05 +01:00
</div>
2020-09-22 11:45:55 +01:00
<div id="add_new_template_form" class="sticky-template-form" role="region" aria-label="Choose template type" ${ newTemplateDataModules } >
2019-07-11 18:59:05 +01:00
<div class="js-will-stick-at-bottom-when-scrolling">
<div class="form-group ">
<fieldset id="add_template_by_template_type">
<legend class="form-label">
New template
</legend>
<div class="multiple-choice">
<input id="add_template_by_template_type-0" name="add_template_by_template_type" type="radio" value="email">
<label class="block-label" for="add_template_by_template_type-0">
Email
</label>
</div>
<div class="multiple-choice">
<input id="add_template_by_template_type-1" name="add_template_by_template_type" type="radio" value="sms">
<label class="block-label" for="add_template_by_template_type-1">
Text message
</label>
</div>
<div class="multiple-choice">
<input id="add_template_by_template_type-2" name="add_template_by_template_type" type="radio" value="letter">
<label class="block-label" for="add_template_by_template_type-2">
Letter
</label>
</div>
<div class="multiple-choice">
<input id="add_template_by_template_type-3" name="add_template_by_template_type" type="radio" value="copy-existing">
<label class="block-label" for="add_template_by_template_type-3">
Copy an existing template
</label>
</div>
</fieldset>
</div>
</div>
<div class="js-will-stick-at-bottom-when-scrolling">
<div class="page-footer">
2020-02-06 10:50:57 +00:00
<button type="submit" class="govuk-button page-footer__button" name="operation" value="add-new-template">Continue</button>
2019-07-11 18:59:05 +01:00
</div>
</div>
</div>
<div class="selection-counter visuallyhidden" role="status" aria-live="polite">
Nothing selected
</div>
</div>`
};
document . body . innerHTML = `
<form method="post" data-module="template-folder-form">
2019-08-20 16:15:11 +01:00
${ helpers . templatesAndFoldersCheckboxes ( hierarchy ) }
2020-08-11 15:44:17 +01:00
${ controlsHTML ( newTemplateDataModules ) }
2019-07-11 18:59:05 +01:00
</form>` ;
};
2019-07-12 16:00:34 +01:00
function resetStickyMocks () {
GOVUK . stickAtBottomWhenScrolling . recalculate . mockClear ();
GOVUK . stickAtBottomWhenScrolling . setMode . mockClear ();
};
2019-07-11 18:59:05 +01:00
beforeAll (() => {
require ( '../../app/assets/javascripts/templateFolderForm.js' );
2020-09-22 11:45:55 +01:00
// plug JSDOM's lack of support for window.scrollTo
window . scrollTo = () => {};
2019-07-11 18:59:05 +01:00
});
afterAll (() => {
require ( './support/teardown.js' );
2020-09-22 11:45:55 +01:00
// tidy up
delete window . scrollTo ;
2019-07-11 18:59:05 +01:00
});
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]' );
};
2020-09-22 11:45:55 +01:00
describe ( "Before the module starts" , () => {
2020-02-06 10:50:57 +00:00
2019-07-12 16:00:34 +01:00
// 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.
2019-10-01 08:25:25 +01:00
// Because of this, they will need to be marked with classes
2019-07-12 16:00:34 +01:00
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 );
});
2020-02-06 10:50:57 +00:00
2019-07-12 16:00:34 +01:00
});
2020-09-22 11:45:55 +01:00
describe ( "When the module starts" , () => {
2019-07-11 18:59:05 +01:00
beforeEach (() => {
// start module
window . GOVUK . modules . start ();
formControls = templateFolderForm . querySelector ( '#sticky_template_forms' );
visibleCounter = getVisibleCounter ();
});
2019-07-12 16:00:34 +01:00
afterEach (() => resetStickyMocks ());
2019-07-11 18:59:05 +01:00
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 ();
2020-09-20 21:20:51 +01:00
expect ( document . querySelector ( 'button[value=add-new-template]' ). getAttribute ( 'aria-expanded' )). toEqual ( 'false' );
expect ( document . querySelector ( 'button[value=add-new-folder]' ). getAttribute ( 'aria-expanded' )). toEqual ( 'false' );
2019-07-11 18:59:05 +01:00
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 ());
});
});
2019-07-12 16:00:34 +01:00
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' );
});
2019-07-11 18:59:05 +01:00
});
2020-08-11 15:44:17 +01:00
describe ( "Click 'New template' for single channel service" , () => {
test ( "should redirect to new template page" , () => {
setFixtures ( hierarchy , "data-channel='sms' data-service='123'" )
templateFolderForm = document . querySelector ( 'form[data-module=template-folder-form]' );
// start module
window . GOVUK . modules . start ();
formControls = templateFolderForm . querySelector ( '#sticky_template_forms' );
// reset sticky JS mocks called when the module starts
resetStickyMocks ();
// add listener for url change
const descriptor1 = Object . getOwnPropertyDescriptor ( window , 'location' );
delete window . location
const mockCallback = jest . fn ( x => {});
Object . defineProperty ( window , 'location' , {
set : mockCallback
});
// click
helpers . triggerEvent ( formControls . querySelector ( '[value=add-new-template]' ), 'click' );
// expect url to change
expect ( mockCallback ). toHaveBeenCalledWith ( "/services/123/templates/add-sms" )
setFixtures ( hierarchy )
resetStickyMocks ()
});
})
2019-07-11 18:59:05 +01:00
describe ( "Clicking 'New template'" , () => {
beforeEach (() => {
// start module
window . GOVUK . modules . start ();
formControls = templateFolderForm . querySelector ( '#sticky_template_forms' );
2019-07-12 16:00:34 +01:00
// reset sticky JS mocks called when the module starts
resetStickyMocks ();
2019-07-11 18:59:05 +01:00
helpers . triggerEvent ( formControls . querySelector ( '[value=add-new-template]' ), 'click' );
});
2019-07-12 16:00:34 +01:00
afterEach (() => resetStickyMocks ());
2020-09-22 11:45:55 +01:00
describe ( "Should show a region" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
test ( "with options for all the types of template" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
const options = [
'Email' , 'Text message' , 'Letter' , 'Copy an existing template'
];
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
const labels = Array . from ( formControls . querySelectorAll ( 'label' ));
const radios = Array . from ( formControls . querySelectorAll ( 'input[type=radio]' ));
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
options . forEach ( option => {
let matchingLabels = labels . filter ( label => label . textContent . trim () === option );
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
expect ( matchingLabels . length > 0 ). toBe ( true );
let matchingRadio = formControls . querySelector ( `# ${ matchingLabels [ 0 ]. getAttribute ( 'for' ) } ` )
expect ( matchingRadio ). not . toBeNull ();
});
2019-07-11 18:59:05 +01:00
2019-07-12 16:00:34 +01:00
});
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
test ( "with a 'Cancel' link" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
const cancelLink = formControls . querySelector ( '.js-cancel' );
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
expect ( cancelLink ). not . toBeNull ();
expect ( cancelLink . querySelector ( '.govuk-visually-hidden' )). not . toBeNull ();
expect ( cancelLink . querySelector ( '.govuk-visually-hidden' ). textContent . trim ()). toEqual ( 'new template' );
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
});
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
test ( "with an accessible role, name and description" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
let id , description ;
const region = document . querySelector ( '#add_new_template_form' );
expect ( region . hasAttribute ( 'role' )). toBe ( true );
expect ( region . getAttribute ( 'role' )). toEqual ( 'region' );
expect ( region . hasAttribute ( 'aria-label' )). toBe ( true );
expect ( region . getAttribute ( 'aria-label' )). toEqual ( 'Choose template type' );
expect ( region . hasAttribute ( 'aria-describedby' )). toBe ( true );
id = region . getAttribute ( 'aria-describedby' );
description = document . getElementById ( id );
expect ( description ). not . toBeNull ();
expect ( description . textContent . trim ()). toEqual ( 'Press continue to confirm selection or cancel to close' );
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
});
test ( "and focus it" , () => {
expect ( document . activeElement ). toBe ( formControls . querySelector ( '#add_new_template_form' ));
});
2019-07-11 18:59:05 +01:00
});
2019-07-12 16:00:34 +01:00
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' );
});
2019-10-01 14:19:06 +01:00
describe ( "When the 'Cancel' link is clicked after choosing to add a new template" , () => {
2019-07-11 18:59:05 +01:00
let addNewTemplateButton ;
beforeEach (() => {
2019-07-12 16:00:34 +01:00
// reset sticky JS mocks called when the new template state loaded
resetStickyMocks ();
2019-07-11 18:59:05 +01:00
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 ;
2019-07-12 16:00:34 +01:00
afterEach (() => resetStickyMocks ());
2019-07-11 18:59:05 +01:00
beforeEach (() => {
// start module
window . GOVUK . modules . start ();
formControls = templateFolderForm . querySelector ( '#sticky_template_forms' );
2019-07-12 16:00:34 +01:00
// reset sticky JS mocks called when the module starts
resetStickyMocks ();
2019-07-11 18:59:05 +01:00
helpers . triggerEvent ( formControls . querySelector ( '[value=add-new-folder]' ), 'click' );
textbox = formControls . querySelector ( 'input[type=text]' );
});
2020-09-22 11:45:55 +01:00
describe ( "should show a parent region" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
test ( "with an accessible role, name and description" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
let id , description ;
const region = document . querySelector ( '#add_new_folder_form' );
expect ( region . hasAttribute ( 'role' )). toBe ( true );
expect ( region . getAttribute ( 'role' )). toEqual ( 'region' );
expect ( region . hasAttribute ( 'aria-label' )). toBe ( true );
expect ( region . getAttribute ( 'aria-label' )). toEqual ( 'Enter name of the new folder' );
expect ( region . hasAttribute ( 'aria-describedby' )). toBe ( true );
id = region . getAttribute ( 'aria-describedby' );
description = document . getElementById ( id );
expect ( description ). not . toBeNull ();
expect ( description . textContent . trim ()). toEqual ( 'Press add new folder to confirm name or cancel to close' );
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
});
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
test ( "with a textbox for the folder name" , () => {
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
expect ( textbox ). not . toBeNull ();
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
// check textbox has a label
expect ( formControls . querySelector ( `label[for= ${ textbox . getAttribute ( 'id' ) } ]` )). not . toBeNull ();
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
});
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
test ( "with a 'Cancel' link" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
const cancelLink = formControls . querySelector ( '.js-cancel' );
expect ( cancelLink ). not . toBeNull ();
expect ( cancelLink . querySelector ( '.govuk-visually-hidden' )). not . toBeNull ();
expect ( cancelLink . querySelector ( '.govuk-visually-hidden' ). textContent . trim ()). toEqual ( 'new folder' );
});
test ( "and focus it" , () => {
expect ( document . activeElement ). toBe ( formControls . querySelector ( '#add_new_folder_form' ));
});
2019-07-11 18:59:05 +01:00
});
2019-07-12 16:00:34 +01:00
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' );
});
2019-10-01 14:19:06 +01:00
describe ( "When the 'Cancel' link is clicked after choosing to add a new folder" , () => {
2019-07-11 18:59:05 +01:00
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' );
2019-07-12 16:00:34 +01:00
// reset sticky JS mocks called when the module starts
resetStickyMocks ();
2019-07-11 18:59:05 +01:00
helpers . triggerEvent ( templateFolderCheckboxes [ 0 ], 'click' );
helpers . triggerEvent ( templateFolderCheckboxes [ 2 ], 'click' );
});
2019-07-12 16:00:34 +01:00
afterEach (() => resetStickyMocks ());
2019-07-11 18:59:05 +01:00
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 ();
2020-09-20 21:20:51 +01:00
expect ( formControls . querySelector ( 'button[value=move-to-new-folder]' ). getAttribute ( 'aria-expanded' )). toEqual ( 'false' );
expect ( formControls . querySelector ( 'button[value=move-to-existing-folder]' ). getAttribute ( 'aria-expanded' )). toEqual ( 'false' );
2019-07-11 18:59:05 +01:00
});
2019-07-12 16:00:34 +01:00
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' );
});
2019-10-01 14:10:22 +01:00
describe ( "'Clear selection' link" , () => {
2019-07-11 18:59:05 +01:00
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" , () => {
2020-02-06 10:50:57 +00:00
2020-09-18 11:51:16 +01:00
expect ( visibleCounterText ). toEqual ( '1 template, 1 folder selected' );
2020-02-06 10:50:57 +00:00
2019-07-11 18:59:05 +01:00
});
2020-02-06 10:50:57 +00:00
2019-07-11 18:59:05 +01:00
});
describe ( "Clicking the 'Move' button" , () => {
beforeEach (() => {
2019-07-12 16:00:34 +01:00
// reset sticky JS mocks called when a selection was made
resetStickyMocks ();
2019-07-11 18:59:05 +01:00
helpers . triggerEvent ( formControls . querySelector ( '[value=move-to-existing-folder]' ), 'click' );
});
2020-09-22 11:45:55 +01:00
describe ( "Should show a region" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
test ( "with an accessible role, name and description" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
let id , description ;
const region = document . querySelector ( '#move_to_folder_radios' );
expect ( region . hasAttribute ( 'role' )). toBe ( true );
expect ( region . getAttribute ( 'role' )). toEqual ( 'region' );
expect ( region . hasAttribute ( 'aria-label' )). toBe ( true );
expect ( region . getAttribute ( 'aria-label' )). toEqual ( 'Choose the folder to move selected items to' );
expect ( region . hasAttribute ( 'aria-describedby' )). toBe ( true );
id = region . getAttribute ( 'aria-describedby' );
description = document . getElementById ( id );
expect ( description ). not . toBeNull ();
expect ( description . textContent . trim ()). toEqual ( 'Press move to confirm or cancel to close' );
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
});
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
test ( "with radios for all the folders in the hierarchy" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
const foldersInHierarchy = [];
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
function getFolders ( nodes ) {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
nodes . forEach ( node => {
if ( node . type === 'folder' ) {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
foldersInHierarchy . push ( node . label );
if ( node . children . length ) { getFolders ( node . children ) }
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
}
});
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
};
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
getFolders ( hierarchy );
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
const folderLabels = Array . from ( formControls . querySelectorAll ( '#move_to label' ))
. filter ( label => label . textContent . trim () !== 'Templates' );
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
expect ( folderLabels . map ( label => label . textContent . trim ())). toEqual ( foldersInHierarchy );
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
const radiosForLabels = folderLabels
. map ( label => formControls . querySelector ( `# ${ label . getAttribute ( 'for' ) } ` ))
. filter ( radio => radio !== null );
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
expect ( radiosForLabels . length ). toEqual ( foldersInHierarchy . length );
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
});
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
test ( "with a 'Cancel' link" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
const cancelLink = formControls . querySelector ( '.js-cancel' );
expect ( cancelLink ). not . toBeNull ();
expect ( cancelLink . querySelector ( '.govuk-visually-hidden' )). not . toBeNull ();
expect ( cancelLink . querySelector ( '.govuk-visually-hidden' ). textContent . trim ()). toEqual ( 'move to folder' );
});
test ( "and focus it" , () => {
expect ( document . activeElement ). toBe ( formControls . querySelector ( '#move_to_folder_radios' ));
});
2019-07-11 18:59:05 +01:00
});
2019-07-12 16:00:34 +01:00
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' );
});
2019-10-01 14:19:06 +01:00
describe ( "When the 'Cancel' link is clicked after choosing to move a template or folder" , () => {
2019-07-11 18:59:05 +01:00
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 (() => {
2019-07-12 16:00:34 +01:00
// reset sticky JS mocks called when a selection was made
resetStickyMocks ();
2019-07-11 18:59:05 +01:00
helpers . triggerEvent ( formControls . querySelector ( '[value=move-to-new-folder]' ), 'click' );
textbox = formControls . querySelector ( 'input[type=text]' );
});
2020-09-22 11:45:55 +01:00
describe ( "Should show a region" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
test ( "with an accessible role, name and description" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
let id , description ;
const region = document . querySelector ( '#move_to_new_folder_form' );
expect ( region . hasAttribute ( 'role' )). toBe ( true );
expect ( region . getAttribute ( 'role' )). toEqual ( 'region' );
expect ( region . hasAttribute ( 'aria-label' )). toBe ( true );
expect ( region . getAttribute ( 'aria-label' )). toEqual ( 'Enter name of the new folder to move selected items to' );
expect ( region . hasAttribute ( 'aria-describedby' )). toBe ( true );
id = region . getAttribute ( 'aria-describedby' );
description = document . getElementById ( id );
expect ( description ). not . toBeNull ();
expect ( description . textContent . trim ()). toEqual ( 'Press add to new folder to confirm name or cancel to close' );
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
});
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
test ( "with a textbox for the folder name" , () => {
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
expect ( textbox ). not . toBeNull ();
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
// check textbox has a label
expect ( formControls . querySelector ( `label[for= ${ textbox . getAttribute ( 'id' ) } ]` )). not . toBeNull ();
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
});
2020-09-21 20:27:47 +01:00
2020-09-22 11:45:55 +01:00
test ( "with a 'Cancel' link" , () => {
2019-07-11 18:59:05 +01:00
2020-09-22 11:45:55 +01:00
const cancelLink = formControls . querySelector ( '.js-cancel' );
expect ( cancelLink ). not . toBeNull ();
expect ( cancelLink . querySelector ( '.govuk-visually-hidden' )). not . toBeNull ();
expect ( cancelLink . querySelector ( '.govuk-visually-hidden' ). textContent . trim ()). toEqual ( 'move to new folder' );
});
test ( "and focus it" , () => {
expect ( document . activeElement ). toBe ( formControls . querySelector ( '#move_to_new_folder_form' ));
});
2019-07-11 18:59:05 +01:00
});
2019-07-12 16:00:34 +01:00
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' );
});
2019-10-01 14:19:06 +01:00
describe ( "When the 'Cancel' link is clicked after choosing to add a template or folder to a new folder" , () => {
2019-07-11 18:59:05 +01:00
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 );
});
});
});
});
2020-09-18 11:51:16 +01:00
describe ( "Additional selection counter scenarios" , () => {
let templateFolderCheckboxes ;
beforeEach (() => {
// start module
window . GOVUK . modules . start ();
templateFolderCheckboxes = getTemplateFolderCheckboxes ();
visibleCounterText = getVisibleCounter (). textContent . trim ();
hiddenCounterText = getHiddenCounter (). textContent . trim ();
formControls = templateFolderForm . querySelector ( '#sticky_template_forms' );
// reset sticky JS mocks called when the module starts
resetStickyMocks ();
});
afterEach (() => resetStickyMocks ());
describe ( "When just templates are selected" , () => {
test ( "the content of both visible and hidden counters should match" , () => {
helpers . triggerEvent ( templateFolderCheckboxes [ 1 ], 'click' );
helpers . triggerEvent ( templateFolderCheckboxes [ 2 ], 'click' );
expect ( visibleCounterText ). toEqual ( hiddenCounterText );
});
test ( "the content of the counter should reflect the selection" , () => {
helpers . triggerEvent ( templateFolderCheckboxes [ 1 ], 'click' );
helpers . triggerEvent ( templateFolderCheckboxes [ 2 ], 'click' );
expect ( visibleCounterText ). toEqual ( '2 templates selected' );
});
});
describe ( "When just folders are selected" , () => {
test ( "the content of both visible and hidden counters should match" , () => {
helpers . triggerEvent ( templateFolderCheckboxes [ 0 ], 'click' );
expect ( visibleCounterText ). toEqual ( hiddenCounterText );
});
test ( "the content of the counter should reflect the selection" , () => {
helpers . triggerEvent ( templateFolderCheckboxes [ 0 ], 'click' );
expect ( visibleCounterText ). toEqual ( '1 folder selected' );
});
});
});
2019-07-11 18:59:05 +01:00
});