2019-08-16 18:32:16 +01:00
const helpers = require ( './support/helpers.js' ) ;
beforeAll ( ( ) => {
2019-10-16 15:20:05 +01:00
require ( '../../app/assets/javascripts/enhancedTextbox.js' ) ;
2019-08-16 18:32:16 +01:00
} ) ;
afterAll ( ( ) => {
require ( './support/teardown.js' ) ;
} ) ;
2019-10-16 15:20:05 +01:00
describe ( 'Enhanced textbox' , ( ) => {
2019-08-16 18:32:16 +01:00
let input ;
let textarea ;
let backgroundEl ;
2022-10-27 11:12:39 -04:00
const stylesheet = document . createElement ( 'style' ) ;
2019-08-16 18:32:16 +01:00
beforeAll ( ( ) => {
// set some default styling
stylesheet . innerHTML = ".textbox-highlight-textbox { padding: 2px; width: 576px; border-width: 1px; }" ;
stylesheet . innerHTML += "textarea.textbox-highlight-textbox { height: 224px; }" ;
document . getElementsByTagName ( 'head' ) [ 0 ] . appendChild ( stylesheet ) ;
} ) ;
afterAll ( ( ) => {
stylesheet . parentNode . removeChild ( stylesheet ) ;
} ) ;
beforeEach ( ( ) => {
// set up DOM
document . body . innerHTML = `
< div class = "form-group" >
< label for = "subject" > Subject < / l a b e l >
2019-10-16 15:20:05 +01:00
< input class = "form-control textbox-highlight-textbox" data - module = "enhanced-textbox" type = "text" name = "subject" id = "subject" / >
2019-08-16 18:32:16 +01:00
< / d i v >
< div class = "form-group" >
< label for = "template_content" > Message < / l a b e l >
2019-10-16 15:20:05 +01:00
< textarea class = "form-control form-control-1-1 textbox-highlight-textbox" data - module = "enhanced-textbox" id = "template_content" name = "template_content" rows = "8" >
2019-08-16 18:32:16 +01:00
< / t e x t a r e a >
< / d i v > ` ;
input = document . querySelector ( 'input' ) ;
textarea = document . querySelector ( 'textarea' ) ;
} ) ;
afterEach ( ( ) => {
document . body . innerHTML = '' ;
} ) ;
describe ( "When the page loads" , ( ) => {
describe ( "An element should be added as a layer below the textbox to hold the highlights" , ( ) => {
beforeEach ( ( ) => {
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
} ) ;
test ( "If the textbox is a <textarea>" , ( ) => {
backgroundEl = textarea . nextElementSibling ;
// both the textbox and the element behind need a wrapping element
expect ( textarea . parentNode . classList . contains ( 'textbox-highlight-wrapper' ) ) . toBe ( true ) ;
expect ( backgroundEl ) . not . toBeNull ( ) ;
expect ( backgroundEl . classList . contains ( 'textbox-highlight-background' ) ) . toBe ( true ) ;
} ) ;
test ( "If the textbox is an <input>" , ( ) => {
backgroundEl = input . nextElementSibling ;
// both the textbox and the element behind need a wrapping element
expect ( input . parentNode . classList . contains ( 'textbox-highlight-wrapper' ) ) . toBe ( true ) ;
expect ( backgroundEl ) . not . toBeNull ( ) ;
expect ( backgroundEl . classList . contains ( 'textbox-highlight-background' ) ) . toBe ( true ) ;
} ) ;
} ) ;
describe ( "The element's dimensions and border-width should match those of the textbox" , ( ) => {
beforeEach ( ( ) => {
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
} ) ;
test ( "If the textbox is an <textarea>" , ( ) => {
backgroundEl = textarea . nextElementSibling ;
2020-03-05 15:44:31 +00:00
expect ( backgroundEl . style . width ) . toEqual ( '576px' ) ;
2019-08-16 18:32:16 +01:00
expect ( backgroundEl . style . borderWidth ) . toEqual ( '1px' ) ;
} ) ;
test ( "If the textbox is an <input>" , ( ) => {
backgroundEl = input . nextElementSibling ;
2020-03-05 15:44:31 +00:00
expect ( backgroundEl . style . width ) . toEqual ( '576px' ) ;
2019-08-16 18:32:16 +01:00
expect ( backgroundEl . style . borderWidth ) . toEqual ( '1px' ) ;
} ) ;
} ) ;
2019-10-16 14:35:18 +01:00
describe ( "The element's width should match even when the textbox is initially hidden" , ( ) => {
beforeEach ( ( ) => {
let setDisplayPropertyOfFormGroups = function ( property ) {
Array . prototype . forEach . call (
document . getElementsByClassName ( 'form-group' ) ,
element => element . style . display = property
) ;
} ;
setDisplayPropertyOfFormGroups ( 'none' ) ;
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-10-16 14:35:18 +01:00
setDisplayPropertyOfFormGroups ( 'block' ) ;
} ) ;
test ( "If the textbox is an <textarea>" , ( ) => {
backgroundEl = textarea . nextElementSibling ;
2020-03-05 15:44:31 +00:00
expect ( backgroundEl . style . width ) . toEqual ( '576px' ) ;
2019-10-16 14:35:18 +01:00
} ) ;
} ) ;
2019-08-16 18:32:16 +01:00
test ( "The element should be hidden from assistive technologies" , ( ) => {
expect ( backgroundEl . getAttribute ( 'aria-hidden' ) ) . toEqual ( 'true' ) ;
} ) ;
describe ( "If there is a variable in the content, its matching text in the element below should be wrapped in a highlight tag" , ( ) => {
test ( "If the textbox is a <textarea>" , ( ) => {
textarea . textContent = "Dear ((title)) ((name))" ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = textarea . nextElementSibling ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder' ) ;
expect ( highlightTags . length ) . toEqual ( 2 ) ;
expect ( highlightTags [ 0 ] . textContent ) . toEqual ( '((title))' ) ;
expect ( highlightTags [ 1 ] . textContent ) . toEqual ( '((name))' ) ;
} ) ;
test ( "If the textbox is a <input>" , ( ) => {
input . value = "Dear ((title)) ((name))" ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = input . nextElementSibling ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder' ) ;
expect ( highlightTags . length ) . toEqual ( 2 ) ;
expect ( highlightTags [ 0 ] . textContent ) . toEqual ( '((title))' ) ;
expect ( highlightTags [ 1 ] . textContent ) . toEqual ( '((name))' ) ;
} ) ;
2019-10-16 14:35:18 +01:00
test ( "Unless a data attribute is set to turn this feature off" , ( ) => {
textarea . textContent = "Dear ((title)) ((name))" ;
textarea . setAttribute ( 'data-highlight-placeholders' , 'false' )
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-10-16 14:35:18 +01:00
backgroundEl = textarea . nextElementSibling ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder' ) ;
expect ( highlightTags . length ) . toEqual ( 0 ) ;
} ) ;
2019-08-16 18:32:16 +01:00
} ) ;
describe ( "If there is optional text in the content, its matching text in the element below should be wrapped in a highlight tag" , ( ) => {
test ( "If the textbox is a <textarea>" , ( ) => {
textarea . textContent = "When you arrive, please go to the ((weekday??main entrance))((weekend??side entrance))" ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = textarea . nextElementSibling ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder-conditional' ) ;
expect ( highlightTags . length ) . toEqual ( 2 ) ;
expect ( highlightTags [ 0 ] . textContent ) . toEqual ( '((weekday??' ) ;
expect ( highlightTags [ 1 ] . textContent ) . toEqual ( '((weekend??' ) ;
} ) ;
test ( "If the textbox is an <input>" , ( ) => {
input . value = "When you arrive, please go to the ((weekday??main entrance))((weekend??side entrance))" ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = input . nextElementSibling ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder-conditional' ) ;
expect ( highlightTags . length ) . toEqual ( 2 ) ;
expect ( highlightTags [ 0 ] . textContent ) . toEqual ( '((weekday??' ) ;
expect ( highlightTags [ 1 ] . textContent ) . toEqual ( '((weekend??' ) ;
} ) ;
} ) ;
} ) ;
describe ( "When the content of the textbox is updated" , ( ) => {
// doesn't apply to inputs as they have a fixed height
2020-03-05 15:44:31 +00:00
test ( "If new input changes the textarea's height, the height of the element below should still match" , ( ) => {
2019-08-16 18:32:16 +01:00
// set 10 lines of content
textarea . textContent = `
Ref : ( ( reference ) )
Date : ( ( date ) )
NHS number : ( ( nhs _number ) )
Dear ( ( name ) )
Thank you for attending the appointment on ( ( appointment _date ) ) .
We will now pass on the results to your GP , ( ( doctor ) ) , who will be in contact with you soon to arrange a follow up appointment .
` ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = textarea . nextElementSibling ;
// add another line of text to the content
textarea . textContent += "Best Regards\n\n((requester))" ;
// mock calls for the background element's current height
jest . spyOn ( backgroundEl , 'offsetHeight' , 'get' ) . mockImplementation ( ( ) => 248 ) ;
helpers . triggerEvent ( textarea , 'input' ) ;
expect ( window . getComputedStyle ( textarea ) . height ) . toEqual ( "248px" ) ;
} ) ;
2020-03-05 15:44:31 +00:00
test ( "If a resize changes the textarea's width, the width of the element below should still match" , ( ) => {
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2020-03-05 15:44:31 +00:00
backgroundEl = textarea . nextElementSibling ;
expect ( window . getComputedStyle ( textarea ) . width ) . toEqual ( "576px" ) ;
expect ( window . getComputedStyle ( backgroundEl ) . width ) . toEqual ( "576px" ) ;
textarea . style . width = "500px"
helpers . triggerEvent ( window , 'resize' ) ;
expect ( window . getComputedStyle ( textarea ) . width ) . toEqual ( "500px" ) ;
expect ( window . getComputedStyle ( backgroundEl ) . width ) . toEqual ( "500px" ) ;
} ) ;
2019-08-16 18:32:16 +01:00
describe ( "If a new variable is added to the textbox, it should also be added to the element below in a highlight tag" , ( ) => {
test ( "If the textbox is a <textarea>" , ( ) => {
textarea . textContent = "Dear ((title)) ((name))" ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = textarea . nextElementSibling ;
// add some more content with a new variable
textarea . textContent += "\nRef: ((reference))" ;
helpers . triggerEvent ( textarea , 'input' ) ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder' ) ;
expect ( highlightTags . length ) . toEqual ( 3 ) ;
expect ( highlightTags [ 2 ] . textContent ) . toEqual ( '((reference))' ) ;
} ) ;
test ( "If the textbox is an <input>" , ( ) => {
input . value = "Hospital appointment for ((name))" ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = input . nextElementSibling ;
// add some more content with a new variable
input . value += ", ref: ((reference))" ;
helpers . triggerEvent ( input , 'input' ) ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder' ) ;
expect ( highlightTags . length ) . toEqual ( 2 ) ;
expect ( highlightTags [ 0 ] . textContent ) . toEqual ( '((name))' ) ;
expect ( highlightTags [ 1 ] . textContent ) . toEqual ( '((reference))' ) ;
} ) ;
} ) ;
describe ( "If a new piece of optional text is added to the textbox, it should also be added to the element below in a highlight tag" , ( ) => {
test ( "If the textbox is a <textarea>" , ( ) => {
textarea . textContent = "Dear ((title)) ((name))" ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = textarea . nextElementSibling ;
// add some more content with some optional content inside
textarea . textContent += "\nYour appointment will be on ((date)). When you arrive, please go to the ((weekday??main entrance))((weekend??side entrance))" ;
helpers . triggerEvent ( textarea , 'input' ) ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder' ) ;
const optionalHighlightTags = backgroundEl . querySelectorAll ( '.placeholder-conditional' ) ;
expect ( highlightTags . length ) . toEqual ( 3 ) ;
expect ( optionalHighlightTags . length ) . toEqual ( 2 ) ;
expect ( optionalHighlightTags [ 0 ] . textContent ) . toEqual ( '((weekday??' ) ;
expect ( optionalHighlightTags [ 1 ] . textContent ) . toEqual ( '((weekend??' ) ;
} ) ;
test ( "If the textbox is an <input>" , ( ) => {
input . value = "Hospital appointment" ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = input . nextElementSibling ;
// add some more content with some optional content inside
input . value += "((important?? - IMPORTANT))" ;
helpers . triggerEvent ( input , 'input' ) ;
const optionalHighlightTags = backgroundEl . querySelectorAll ( '.placeholder-conditional' ) ;
expect ( optionalHighlightTags . length ) . toEqual ( 1 ) ;
expect ( optionalHighlightTags [ 0 ] . textContent ) . toEqual ( '((important??' ) ;
} ) ;
} ) ;
describe ( "If a variable is removed from the textbox, its highlight should also be removed" , ( ) => {
test ( "If the textbox is a <textarea>" , ( ) => {
textarea . textContent = `
Dear ( ( title ) ) ( ( name ) )
Ref : ( ( reference ) ) ` ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = textarea . nextElementSibling ;
// add some more content with a new variable
textarea . textContent = "Dear ((title)) ((name))" ;
helpers . triggerEvent ( textarea , 'input' ) ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder' ) ;
expect ( highlightTags . length ) . toEqual ( 2 ) ;
expect ( highlightTags [ 0 ] . textContent ) . toEqual ( '((title))' ) ;
expect ( highlightTags [ 1 ] . textContent ) . toEqual ( '((name))' ) ;
} ) ;
test ( "If the textbox is an <input>" , ( ) => {
input . value = "Hospital appointment for ((name)), ref: ((reference))" ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = input . nextElementSibling ;
// add some more content with a new variable
input . value = "Hospital appointment for ((name))" ;
helpers . triggerEvent ( input , 'input' ) ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder' ) ;
expect ( highlightTags . length ) . toEqual ( 1 ) ;
expect ( highlightTags [ 0 ] . textContent ) . toEqual ( '((name))' ) ;
} ) ;
} ) ;
describe ( "If a piece of optional text has been removed from the textbox, its highlight should also be removed" , ( ) => {
test ( "If the textbox is a <textarea>" , ( ) => {
textarea . textContent = `
Dear ( ( title ) ) ( ( name ) )
Your appointment will be on ( ( date ) ) . When you arrive , please go to the ( ( weekday ? ? main entrance ) ) ( ( weekend ? ? side entrance ) ) . ` ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = textarea . nextElementSibling ;
// add some more content with a new variable
textarea . textContent = `
Dear ( ( title ) ) ( ( name ) )
Your appointment will be on ( ( date ) ) . When you arrive , please go to the ( ( weekday ? ? main entrance ) ) ` ;
helpers . triggerEvent ( textarea , 'input' ) ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder' ) ;
const optionalHighlightTags = backgroundEl . querySelectorAll ( '.placeholder-conditional' ) ;
expect ( highlightTags . length ) . toEqual ( 3 ) ;
expect ( optionalHighlightTags . length ) . toEqual ( 1 ) ;
expect ( highlightTags [ 0 ] . textContent ) . toEqual ( '((title))' ) ;
expect ( highlightTags [ 1 ] . textContent ) . toEqual ( '((name))' ) ;
expect ( optionalHighlightTags [ 0 ] . textContent ) . toEqual ( '((weekday??' ) ;
expect ( highlightTags [ 2 ] . textContent ) . toEqual ( '((date))' ) ;
} ) ;
test ( "If the textbox is an <input>" , ( ) => {
input . value = "Hospital appointment for ((name))((important?? - IMPORTANT))" ;
// start module
2025-10-06 09:38:54 -04:00
window . NotifyModules . start ( ) ;
2019-08-16 18:32:16 +01:00
backgroundEl = input . nextElementSibling ;
// add some more content with a new variable
input . value = "Hospital appointment for ((name))"
helpers . triggerEvent ( input , 'input' ) ;
const highlightTags = backgroundEl . querySelectorAll ( '.placeholder' ) ;
const optionalHighlightTags = backgroundEl . querySelectorAll ( '.placeholder-conditional' ) ;
expect ( highlightTags . length ) . toEqual ( 1 ) ;
expect ( optionalHighlightTags . length ) . toEqual ( 0 ) ;
expect ( highlightTags [ 0 ] . textContent ) . toEqual ( '((name))' ) ;
} ) ;
} ) ;
} ) ;
} ) ;