diff --git a/app/assets/javascripts/fileUpload.js b/app/assets/javascripts/fileUpload.js index a246651e5..08218d0ca 100644 --- a/app/assets/javascripts/fileUpload.js +++ b/app/assets/javascripts/fileUpload.js @@ -11,20 +11,21 @@ function announceUploadStatusFromElement() { srRegion.textContent = ''; setTimeout(() => { srRegion.textContent = message; - console.log(message); }, 50); } } -document.addEventListener('DOMContentLoaded', () => { - announceUploadStatusFromElement(); -}); +// Exported for use in tests +function initUploadStatusAnnouncer() { + document.addEventListener('DOMContentLoaded', () => { + announceUploadStatusFromElement(); + }); +} (function(Modules) { "use strict"; Modules.FileUpload = function() { - this.submit = () => this.$form.trigger('submit'); this.showCancelButton = () => { @@ -38,24 +39,25 @@ document.addEventListener('DOMContentLoaded', () => { this.start = function(component) { this.$form = $(component); - // Handle "Upload your file" button click — CSP-safe version this.$form.on('click', '[data-module="upload-trigger"]', function () { const inputId = $(this).data('file-input-id'); const fileInput = document.getElementById(inputId); if (fileInput) fileInput.click(); }); - // Clear the form if the user navigates back to the page $(window).on("pageshow", () => this.$form[0].reset()); - // Watch for file input changes this.$form.on('change', '.file-upload-field', () => { this.submit(); this.showCancelButton(); }); - }; - }; - })(window.GOVUK.Modules); + +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + announceUploadStatusFromElement, + initUploadStatusAnnouncer + }; +} diff --git a/tests/javascripts/fileUpload.domLoad.test.js b/tests/javascripts/fileUpload.domLoad.test.js new file mode 100644 index 000000000..1fb641a8c --- /dev/null +++ b/tests/javascripts/fileUpload.domLoad.test.js @@ -0,0 +1,26 @@ +// This will use the real function and listener +const { + initUploadStatusAnnouncer +} = require('../../app/assets/javascripts/fileUpload.js'); + +jest.useFakeTimers(); + +test('writes upload message to the live region on DOMContentLoaded', () => { + // Setup the DOM + document.body.innerHTML = ` +
Old message
+ File upload successful + `; + + // Register the listener (same as page load does) + initUploadStatusAnnouncer(); + + // Simulate the page load event + document.dispatchEvent(new Event('DOMContentLoaded')); + + // Live region will be cleared first, then updated + jest.advanceTimersByTime(50); + + const srRegion = document.getElementById('upload-status-live'); + expect(srRegion.textContent).toBe('File upload successful'); +}); diff --git a/tests/javascripts/fileUpload.test.js b/tests/javascripts/fileUpload.test.js index 29c8b711e..e45c45e7a 100644 --- a/tests/javascripts/fileUpload.test.js +++ b/tests/javascripts/fileUpload.test.js @@ -1,13 +1,12 @@ const helpers = require('./support/helpers.js'); -beforeAll(() => { - require('../../app/assets/javascripts/fileUpload.js'); -}); +const { announceUploadStatusFromElement } = require('../../app/assets/javascripts/fileUpload.js'); afterAll(() => { require('./support/teardown.js'); }); + describe('File upload', () => { let form; @@ -82,3 +81,106 @@ describe('File upload', () => { }); }); + +describe('File upload "upload-trigger" click handler', () => { + let form; + + beforeEach(() => { + document.body.innerHTML = ` +
+ + +
+ `; + + form = document.querySelector('form'); + + // Register the module + window.GOVUK.modules.start(); + }); + + afterEach(() => { + document.body.innerHTML = ''; + }); + + test('clicking upload trigger simulates file input click', () => { + const uploadButton = form.querySelector('[data-module="upload-trigger"]'); + const fileInput = document.getElementById('test-file-input'); + + // Spy on fileInput.click + const clickSpy = jest.spyOn(fileInput, 'click').mockImplementation(() => {}); + + // Trigger the click + helpers.triggerEvent(uploadButton, 'click'); + + expect(clickSpy).toHaveBeenCalled(); + + clickSpy.mockRestore(); + }); +}); + +describe('announceUploadStatusFromElement', () => { + beforeEach(() => { + jest.useFakeTimers(); + document.body.innerHTML = ` +
+ `; + }); + + afterEach(() => { + jest.useRealTimers(); + document.body.innerHTML = ''; + }); + + test('announces error message from #upload-error', () => { + document.body.innerHTML += ` + File upload failed + `; + + const srRegion = document.getElementById('upload-status-live'); + + // Call the function + announceUploadStatusFromElement(); + + // Confirm it clears first + expect(srRegion.textContent).toBe(''); + + // Fast-forward the timer + jest.advanceTimersByTime(50); + + // Confirm it updates after delay + expect(srRegion.textContent).toBe('File upload failed'); + }); + + test('announces success message from #upload-success if no error is present', () => { + document.body.innerHTML += ` + File upload successful + `; + + const srRegion = document.getElementById('upload-status-live'); + + announceUploadStatusFromElement(); + + expect(srRegion.textContent).toBe(''); + + jest.advanceTimersByTime(50); + + expect(srRegion.textContent).toBe('File upload successful'); + }); + + test('does nothing if neither success nor error is present', () => { + const srRegion = document.getElementById('upload-status-live'); + + srRegion.textContent = 'Old message'; + + announceUploadStatusFromElement(); + + // Should not clear or update if no message element is found + expect(srRegion.textContent).toBe('Old message'); + + jest.advanceTimersByTime(50); + + // Still unchanged + expect(srRegion.textContent).toBe('Old message'); + }); +});