diff --git a/app/assets/javascripts/previewPane.js b/app/assets/javascripts/previewPane.js index 7a8172861..514b02274 100644 --- a/app/assets/javascripts/previewPane.js +++ b/app/assets/javascripts/previewPane.js @@ -1,9 +1,8 @@ -(function () { +(function (global) { 'use strict'; - const root = this, - $ = this.jQuery; + $ = global.jQuery; let branding_style = $('.multiple-choice input[name="branding_style"]:checked'); @@ -34,4 +33,4 @@ $form.find('button[type="submit"]').text('Save'); $('fieldset').on('change', 'input[name="branding_style"]', setPreviewPane); -})(); +})(window); diff --git a/tests/javascripts/previewPane.test.js b/tests/javascripts/previewPane.test.js new file mode 100644 index 000000000..dd25031dc --- /dev/null +++ b/tests/javascripts/previewPane.test.js @@ -0,0 +1,234 @@ +const helpers = require('./support/helpers.js'); + +const emailPageURL = '/services/6658542f-0cad-491f-bec8-ab8457700ead/service-settings/set-email-branding'; +const emailPreviewConfirmationURL = '/services/6658542f-0cad-491f-bec8-ab8457700ead/service-settings/preview-email-branding'; +const letterPageURL = '/services/6658542f-0cad-491f-bec8-ab8457700ead/service-settings/set-letter-branding'; +const letterPreviewConfirmationURL = '/services/6658542f-0cad-491f-bec8-ab8457700ead/service-settings/preview-letter-branding'; + +let locationMock; + +beforeAll(() => { + + // mock calls to window.location + // default to the email page, the pathname can be changed inside specific tests + locationMock = new helpers.LocationMock(emailPageURL); + +}); + +afterAll(() => { + + // reset window.location to its original state + locationMock.reset(); + require('./support/teardown.js'); + +}); + +describe('Preview pane', () => { + + let form; + let radios; + + beforeEach(() => { + + const brands = { + "name": "branding_style", + "label": "Branding style", + "cssClasses": [], + "fields": [ + { + "label": "Department for Education", + "value": "dfe", + "checked": true + }, + { + "label": "Home Office", + "value": "ho", + "checked": false + }, + { + "label": "Her Majesty's Revenue and Customs", + "value": "hmrc", + "checked": false + }, + { + "label": "Department for Work and Pensions", + "value": "dwp", + "checked": false + } + ] + }; + + // set up DOM + document.body.innerHTML = + `
`; + + document.querySelector('.column-full').appendChild(helpers.getRadioGroup(brands)); + form = document.querySelector('form'); + radios = form.querySelector('fieldset'); + + }); + + afterEach(() => { + + document.body.innerHTML = ''; + + // we run the previewPane.js script every test + // the module cache needs resetting each time for the script to execute + jest.resetModules(); + + }); + + describe("If the page type is 'email'", () => { + + describe("When the page loads", () => { + + test("it should add the preview pane", () => { + + // run preview pane script + require('../../app/assets/javascripts/previewPane.js'); + + expect(document.querySelector('iframe')).not.toBeNull(); + + }); + + test("it should change the form to submit the selection instead of posting to a preview page", () => { + + // run preview pane script + require('../../app/assets/javascripts/previewPane.js'); + + expect(form.getAttribute('action')).toEqual(emailPreviewConfirmationURL); + + }); + + test("the preview pane should show the page for the selected brand", () => { + + // run preview pane script + require('../../app/assets/javascripts/previewPane.js'); + + const selectedValue = Array.from(radios.querySelectorAll('input[type=radio]')).filter(radio => radio.checked)[0].value; + + expect(document.querySelector('iframe').getAttribute('src')).toEqual(`/_email?branding_style=${selectedValue}`); + + }); + + test("the submit button should change from 'Preview' to 'Save'", () => { + + // run preview pane script + require('../../app/assets/javascripts/previewPane.js'); + + expect(document.querySelector('button[type=submit]').textContent).toEqual('Save'); + + }); + + }); + + describe("If the selection changes", () => { + + test("the page shown should match the selected brand", () => { + + // run preview pane script + require('../../app/assets/javascripts/previewPane.js'); + + const newSelection = radios.querySelectorAll('input[type=radio]')[1]; + + helpers.moveSelectionToRadio(newSelection); + + expect(document.querySelector('iframe').getAttribute('src')).toEqual(`/_email?branding_style=${newSelection.value}`); + + }); + + }); + + }); + + describe("If the page type is 'letter'", () => { + + beforeEach(() => { + + // set page URL and page type to 'letter' + window.location.pathname = letterPreviewConfirmationURL; + form.setAttribute('data-preview-type', 'letter'); + + }); + + describe("When the page loads", () => { + + test("it should add the preview pane", () => { + + // run preview pane script + require('../../app/assets/javascripts/previewPane.js'); + + expect(document.querySelector('iframe')).not.toBeNull(); + + }); + + test("it should change the form to submit the selection instead of posting to a preview page", () => { + + // run preview pane script + require('../../app/assets/javascripts/previewPane.js'); + + expect(form.getAttribute('action')).toEqual(letterPreviewConfirmationURL); + + }); + + test("the preview pane should show the page for the selected brand", () => { + + // run preview pane script + require('../../app/assets/javascripts/previewPane.js'); + + const selectedValue = Array.from(radios.querySelectorAll('input[type=radio]')).filter(radio => radio.checked)[0].value; + + expect(document.querySelector('iframe').getAttribute('src')).toEqual(`/_letter?branding_style=${selectedValue}`); + + }); + + test("the submit button should change from 'Preview' to 'Save'", () => { + + // run preview pane script + require('../../app/assets/javascripts/previewPane.js'); + + expect(document.querySelector('button[type=submit]').textContent).toEqual('Save'); + + }); + + }); + + describe("If the selection changes", () => { + + test("the page shown should match the selected brand", () => { + + // run preview pane script + require('../../app/assets/javascripts/previewPane.js'); + + const newSelection = radios.querySelectorAll('input[type=radio]')[1]; + + helpers.moveSelectionToRadio(newSelection); + + expect(document.querySelector('iframe').getAttribute('src')).toEqual(`/_letter?branding_style=${newSelection.value}`); + + }); + + }); + + }); + +}); diff --git a/tests/javascripts/stick-to-window-when-scrolling.test.js b/tests/javascripts/stick-to-window-when-scrolling.test.js index fec68663f..32bacaf9f 100644 --- a/tests/javascripts/stick-to-window-when-scrolling.test.js +++ b/tests/javascripts/stick-to-window-when-scrolling.test.js @@ -472,8 +472,8 @@ describe("Stick to top/bottom of window when scrolling", () => { // add another sticky element before the form footer radios = helpers.getRadioGroup({ cssClasses: ['js-stick-at-top-when-scrolling'], - name: 'send-time', - label: 'send time', + name: 'choose-send-time', + label: 'Choose send time', fields: [ { label: 'Now', @@ -604,7 +604,7 @@ describe("Stick to top/bottom of window when scrolling", () => { } }); - radios.querySelector('fieldset').insertAdjacentHTML('beforeend', helpers.getRadios(fields)); + radios.querySelector('fieldset').insertAdjacentHTML('beforeend', helpers.getRadios(fields, 'days')); radios.offsetHeight = 475; @@ -1146,8 +1146,8 @@ describe("Stick to top/bottom of window when scrolling", () => { // add another sticky element before the form footer radios = helpers.getRadioGroup({ cssClasses: ['js-stick-at-bottom-when-scrolling'], - name: 'send-time', - label: 'Send time', + name: 'choose-send-time', + label: 'Choose send time', fields: [ { label: 'Now', @@ -1280,7 +1280,7 @@ describe("Stick to top/bottom of window when scrolling", () => { } }); - radios.querySelector('fieldset').insertAdjacentHTML('beforeend', helpers.getRadios(fields)); + radios.querySelector('fieldset').insertAdjacentHTML('beforeend', helpers.getRadios(fields, 'days')); radios.offsetHeight = 475; diff --git a/tests/javascripts/support/helpers.js b/tests/javascripts/support/helpers.js index 7fef27e98..90c253306 100644 --- a/tests/javascripts/support/helpers.js +++ b/tests/javascripts/support/helpers.js @@ -1,3 +1,4 @@ +const globals = require('./helpers/globals.js'); const events = require('./helpers/events.js'); const domInterfaces = require('./helpers/dom_interfaces.js'); const html = require('./helpers/html.js'); @@ -6,6 +7,7 @@ const rendering = require('./helpers/rendering.js'); const forms = require('./helpers/forms.js'); const utilities = require('./helpers/utilities.js'); +exports.LocationMock = globals.LocationMock; exports.triggerEvent = events.triggerEvent; exports.clickElementWithMouse = events.clickElementWithMouse; exports.moveSelectionToRadio = events.moveSelectionToRadio; diff --git a/tests/javascripts/support/helpers/globals.js b/tests/javascripts/support/helpers/globals.js new file mode 100644 index 000000000..89fa44e08 --- /dev/null +++ b/tests/javascripts/support/helpers/globals.js @@ -0,0 +1,89 @@ +// helpers for mocking objects attached to the global space as properties, ie. window.location + +class LocationMock { + + constructor (URL) { + + this._location = window.location; + + // setting href sets all sub-properties + this.href = URL; + + // JSDOM sets window.location as non-configurable + // the only way to mock it, currently, is to replace it completely + delete window.location; + window.location = this; + + } + + get href () { + + return `${this.protocol}://${this.host}${this.pathname}${this.search}${this.hash}` + + } + + set href (value) { + + const partNames = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash']; + + const protocol = '(https:|http:)'; + const hostname = '[^\\/]+'; + const port = '(:\\d)'; + const pathname = '([^?]+)'; + const search = '([^#])'; + const hash = '(#[\\x00-\\x7F])'; // match any ASCII character + + const re = new RegExp(`^${protocol}{0,1}(?:\\/\\/){0,1}(${hostname}${port}{0,1}){0,1}${pathname}{0,1}${search}{0,1}${hash}{0,1}$`); + const match = value.match(re) + + if (match === null) { throw Error(`${value} is not a valid URL`); } + + match.forEach((part, idx) => { + + let partName; + + // 0 index is whole match, we want the groups + if (idx > 0) { + partName = partNames[idx - 1]; + + if (part !== undefined) { + this[partName] = part; + } else if (!(partName in this)) { // only get value from window.location if property not set + this[partName] = this._location[partName]; + } + } + + }); + + } + + get host () { + + return `${this.hostname}:${this.port}`; + + } + + set host (value) { + + const parts = value.split(':'); + + this.hostname = parts[0]; + this.protocol = parts[1]; + + } + + // origin is read-only + get origin () { + + return `${this.protol}://${this.hostname}`; + + } + + reset () { + + window.location = this._location; + + } +} + +exports.LocationMock = LocationMock; diff --git a/tests/javascripts/support/helpers/html.js b/tests/javascripts/support/helpers/html.js index 7b3d78eb4..e0b8aa22e 100644 --- a/tests/javascripts/support/helpers/html.js +++ b/tests/javascripts/support/helpers/html.js @@ -1,6 +1,6 @@ // helpers for generating patterns of HTML -function getRadios (fields) { +function getRadios (fields, name) { const result = ''; return fields.map((field, idx) => { @@ -8,8 +8,8 @@ function getRadios (fields) { return `