diff --git a/app/assets/javascripts/listEntry.js b/app/assets/javascripts/listEntry.js
index 07a5ffdfe..b5dd7c88e 100644
--- a/app/assets/javascripts/listEntry.js
+++ b/app/assets/javascripts/listEntry.js
@@ -1,10 +1,6 @@
(function (Modules) {
-
'use strict';
- var root = this,
- $ = this.jQuery;
-
var lists = [],
listEntry,
ListEntry;
@@ -62,6 +58,7 @@
getAttributesHTML = function (attrsByElm) {
var attrStr = '',
elmIdx = attrsByElm.length,
+ existingAttributes = [],
elmAttrs,
attrIdx;
@@ -69,7 +66,11 @@
elmAttrs = attrsByElm[elmIdx];
attrIdx = elmAttrs.length;
while (attrIdx--) {
- attrStr += attributeTemplate.render({ 'name': elmAttrs[attrIdx].name, 'value': elmAttrs[attrIdx].value });
+ // prevent duplicates
+ if ($.inArray(elmAttrs[attrIdx].name, existingAttributes) === -1) {
+ attrStr += attributeTemplate.render({ 'name': elmAttrs[attrIdx].name, 'value': elmAttrs[attrIdx].value });
+ existingAttributes.push(elmAttrs[attrIdx].name);
+ }
}
}
return attrStr;
diff --git a/tests/javascripts/listEntry.test.js b/tests/javascripts/listEntry.test.js
new file mode 100644
index 000000000..80c67e3ae
--- /dev/null
+++ b/tests/javascripts/listEntry.test.js
@@ -0,0 +1,337 @@
+beforeAll(() => {
+ // set up jQuery & HoganJS
+ window.jQuery = require('jquery');
+ $ = window.jQuery;
+ Hogan = require('hogan.js');
+
+ // load module code
+ require('govuk_frontend_toolkit/javascripts/govuk/modules.js');
+ require('../../app/assets/javascripts/listEntry.js');
+});
+
+afterAll(() => {
+ window.jQuery = null;
+ $ = null;
+
+ delete window.GOVUK;
+});
+
+describe("List entry", () => {
+ const domains = [
+ 'gov.uk',
+ 'dwp.gov.uk',
+ 'hmrc.gov.uk',
+ 'defra.gov.uk',
+ 'beis.gov.uk',
+ 'dcms.gov.uk',
+ 'dfe.gov.uk',
+ 'did.gov.uk',
+ 'dvla.gov.uk',
+ 'dvsa.gov.uk'
+ ];
+ let inputList;
+
+ const triggerEvent = (el, evtType) => {
+ const evt = new Event(evtType, {
+ bubbles: true,
+ cancelable: true
+ });
+
+ el.dispatchEvent(evt);
+ };
+
+ const setFieldValues = (num) => {
+ const items = inputList.querySelectorAll('.list-entry input[type=text]');
+
+ while (num--) {
+ items[num].setAttribute('value', domains[num]);
+ }
+ };
+
+ beforeEach(() => {
+
+ // set up DOM
+ let entries = () => {
+ let result = '';
+
+ for (let idx = 0; idx < 10; idx++) {
+ result += `
+
+
+
+
`;
+ }
+
+ return result;
+ };
+
+ document.body.innerHTML =
+ ``;
+
+ inputList = document.querySelector('.input-list');
+
+ });
+
+ afterEach(() => {
+
+ document.body.innerHTML = '';
+
+ });
+
+ describe("On page load", () => {
+
+ test("Should remove all the fields except the first 2 if no values are present", () => {
+
+ // start module
+ window.GOVUK.modules.start();
+
+ expect(inputList.querySelectorAll('.list-entry').length).toEqual(2);
+
+ });
+
+ test("Should remove all the fields except those first 2, and leave the first field as is if it has a value", () => {
+
+ let fields = inputList.querySelectorAll('.list-entry input[type=text]');
+
+ // set value of first field
+ fields[0].setAttribute('value', domains[0]);
+
+ // start module
+ window.GOVUK.modules.start();
+
+ // re-select fields, based on updated DOM
+ fields = inputList.querySelectorAll('.list-entry input[type=text]');
+
+ expect(fields.length).toEqual(2);
+ expect(fields[0].getAttribute('value')).toEqual(domains[0]);
+
+ });
+
+ test("Should remove all the fields except those first 2, and leave the second field as is if it has a value", () => {
+
+ let fields = inputList.querySelectorAll('.list-entry input[type=text]');
+
+ // set value of first field
+ fields[1].setAttribute('value', domains[1]);
+
+ // start module
+ window.GOVUK.modules.start();
+
+ // re-select fields, based on updated DOM
+ fields = inputList.querySelectorAll('.list-entry input[type=text]');
+
+ expect(fields.length).toEqual(2);
+ expect(fields[1].getAttribute('value')).toEqual(domains[1]);
+
+ });
+ test("Should remove all the fields except those with values if we have 4 values", () => {
+
+ const fourDomains = domains.slice(0, 4);
+ let fields = inputList.querySelectorAll('.list-entry input[type=text]');
+
+ // set values of first 4 fields
+ fourDomains.forEach((domain, idx) => { fields[idx].setAttribute('value', domain) });
+
+ // start module
+ window.GOVUK.modules.start();
+
+ // re-select fields, based on updated DOM
+ fields = inputList.querySelectorAll('.list-entry input[type=text]');
+
+ expect(fields.length).toEqual(4);
+ fourDomains.forEach((domain, idx) => {
+
+ expect(fields[idx].getAttribute('value')).toEqual(domain);
+
+ });
+
+ });
+ test("Should add 'remove' buttons to all fields except the first", () => {
+
+ // start module
+ window.GOVUK.modules.start();
+
+ inputList.querySelectorAll('.list-entry').forEach((listEntry, idx) => {
+
+ if (idx === 0) {
+ expect(listEntry.querySelector('.list-entry-remove')).toBeNull();
+ } else {
+ expect(listEntry.querySelector('.list-entry-remove')).not.toBeNull();
+ }
+
+ });
+
+ });
+
+ test("Should add an 'add feature' button to the bottom of the list", () => {
+
+ // start module
+ window.GOVUK.modules.start();
+
+ const listItems = inputList.children;
+
+ expect(listItems[listItems.length - 1]);
+
+ });
+
+ test("Should copy all shared attributes into the new fields", () => {
+
+ inputList.querySelectorAll('.list-entry input[type=text]').forEach((field, idx) => {
+
+ field.setAttribute('aria-describedby', 'hint1');
+ field.classList.add('top-level-domain');
+
+ });
+
+ // start module
+ window.GOVUK.modules.start();
+
+ // re-select fields, based on updated DOM
+ fields = inputList.querySelectorAll('.list-entry input[type=text]');
+
+ expect(fields[0].getAttribute('aria-describedby')).toEqual('hint1');
+ expect(fields[0].classList.contains('top-level-domain')).toBe(true);
+ });
+ });
+
+ describe("When the 'remove' button is clicked", () => {
+
+ beforeEach(() => {
+ setFieldValues(10);
+
+ // start module
+ window.GOVUK.modules.start();
+ });
+
+ test("Should remove the associated field", () => {
+
+ triggerEvent(inputList.querySelectorAll('.list-entry-remove')[0], 'click');
+
+ // list started with 10 fields
+ expect(inputList.querySelectorAll('.list-entry').length).toEqual(9);
+
+ });
+
+ test("Should leave the list with the right numbers", () => {
+
+ triggerEvent(inputList.querySelectorAll('.list-entry-remove')[0], 'click');
+
+ const newNums = Array.from(
+ inputList.querySelectorAll('.text-box-number-label')
+ )
+ .map((itemNum, idx) => {
+ return parseInt(itemNum.lastChild.nodeValue, 10);
+ });
+
+ expect(newNums).toEqual([1,2,3,4,5,6,7,8,9]);
+
+ });
+
+ test("Should leave the list with the right values if you remove the last one", () => {
+
+ // the first list item doesn't have a 'remove' button so there are only 9 for 10 items
+ triggerEvent(inputList.querySelectorAll('.list-entry-remove')[8], 'click');
+
+ // the items have their values set to the 10 domains
+ const expectedValues = domains.slice(0, -1);
+ const itemValues = Array.from(
+ inputList.querySelectorAll('.list-entry input[type=text]')
+ )
+ .map(item => item.getAttribute('value'));
+
+ expect(itemValues).toEqual(expectedValues);
+
+ });
+
+ test("Should leave the list with the right values if you remove the second one", () => {
+
+ // the first 'remove' button is attached to the second list item
+ triggerEvent(inputList.querySelectorAll('.list-entry-remove')[0], 'click');
+
+ // the items have their values set to the 10 domains
+ const expectedValues = domains.slice();
+ const itemValues = Array.from(
+ inputList.querySelectorAll('.list-entry input[type=text]')
+ )
+ .map(item => item.getAttribute('value'));
+
+ // remove second item
+ expectedValues.splice(1, 1)
+
+ expect(itemValues).toEqual(expectedValues);
+
+ });
+
+ test("Should add the 'add' button if the added question is the 10th field", () => {
+
+ triggerEvent(inputList.querySelectorAll('.list-entry-remove')[8], 'click');
+
+ expect(inputList.querySelector('.list-entry-add')).not.toBeNull();
+
+ });
+ });
+
+ describe("When the 'add feature' button is clicked", () => {
+ let addButton;
+
+ test("Should add a new field", () => {
+
+ // start module
+ window.GOVUK.modules.start();
+
+ triggerEvent(inputList.querySelector('.list-entry-add'), 'click');
+
+ // inputList defaults to 2 items
+ expect(inputList.querySelectorAll('.list-entry').length).toEqual(3);
+
+ });
+ test("Should update the number of fields users are allowed to enter if one is removed", () => {
+
+ // start module
+ window.GOVUK.modules.start();
+
+ triggerEvent(inputList.querySelectorAll('.list-entry-remove')[0], 'click');
+
+ expect(inputList.querySelector('.list-entry-add').textContent.trim())
+ .toEqual('Add another domain (9 remaining)');
+
+ });
+ test("Should update the number of fields users are allowed to enter if one is added", () => {
+
+ // start module
+ window.GOVUK.modules.start();
+
+ triggerEvent(inputList.querySelector('.list-entry-add'), 'click');
+
+ expect(inputList.querySelector('.list-entry-add').textContent.trim())
+ .toEqual('Add another domain (7 remaining)');
+
+ });
+ test("Should remove the 'add' button if the added question is the 10th field", () => {
+
+ setFieldValues(9);
+
+ // start module
+ window.GOVUK.modules.start();
+
+ triggerEvent(inputList.querySelector('.list-entry-add'), 'click');
+
+ expect(inputList.querySelector('.list-entry-add')).toBeNull();
+
+ });
+ });
+});