From 67e77b84837cf8d9654493024c500bcf23bbaedb Mon Sep 17 00:00:00 2001 From: diana-nita Date: Thu, 2 Nov 2023 15:21:51 +0000 Subject: [PATCH 1/4] Unit tests for header.js --- tests/integration/jsdom/header.test.js | 114 +++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 tests/integration/jsdom/header.test.js diff --git a/tests/integration/jsdom/header.test.js b/tests/integration/jsdom/header.test.js new file mode 100644 index 000000000..aed958ad1 --- /dev/null +++ b/tests/integration/jsdom/header.test.js @@ -0,0 +1,114 @@ +import Header from "../../../packages/components/header/header.js"; + +describe('Header class', () => { + beforeEach(() => { + document.body.innerHTML = ` +
+
+
  • Health A-Z
  • +
  • NHS services
  • +
  • Live Well
  • +
  • Mental health
  • +
  • Care and support
  • +
  • Pregnancy
  • +
  • Home
  • +
  • More
  • +
    +
    +
    +
    + ` + }) + it('Should create navigation elements in the DOM', async () => { + // Call the Header initialization function + await Header(); + + // Ensure the navigation elements are created in the DOM + expect(document.querySelector('.nhsuk-navigation')).not.toBeNull(); + }); + + it('Should toggle mobile menu visibility', async () => { + const toggleButton = document.querySelector('.nhsuk-header__menu-toggle'); + let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); + // Call the Header initialization function + await Header(); + + mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); + // Initially, the menu should be closed + expect(mobileMenuList.classList.contains('nhsuk-header__drop-down--hidden')).toBe(true); + + // Open the mobile menu + toggleButton.click(); + + expect(mobileMenuList.classList.contains('nhsuk-header__drop-down--hidden')).toBe(false); + + // Close the mobile menu + toggleButton.click(); + + expect(mobileMenuList.classList.contains('nhsuk-header__drop-down--hidden')).toBe(true); + }); + + it('Should close menu when escape key is pressed', async () => { + //define a event for the escape key + const escapeKeyEvent = new KeyboardEvent('keydown', { + key: 'Escape', + code: 'Escape', + keyCode: 27, + which: 27, + charCode: 27, + }); + const toggleButton = document.querySelector('.nhsuk-header__menu-toggle'); + + await Header(); + + //Expect the menu to be hidden initially + expect(document.querySelector('.nhsuk-header__drop-down').classList.contains('nhsuk-header__drop-down--hidden')).toBe(true); + + //Toogle the menu - open it + toggleButton.click(); + expect(document.querySelector('.nhsuk-header__drop-down').classList.contains('nhsuk-header__drop-down--hidden')).toBe(false); + + //Press the escape key to close it + document.dispatchEvent(escapeKeyEvent); + expect(document.querySelector('.nhsuk-header__drop-down').classList.contains('nhsuk-header__drop-down--hidden')).toBe(true); + }); + + it('Should setup the Mobile Menu Container during initialization', async () => { + const mobileMenuContainer = document.querySelector('.nhsuk-mobile-menu-container'); + expect(mobileMenuContainer.childElementCount).toBe(0); + await Header(); + expect(mobileMenuContainer.childElementCount).toBeGreaterThan(0); + }); + + it('Should setup the Mobile Menu List during initialization', async () => { + //Initially there won't be any ul elements inside the container- it gets added in the setupMobileMenu method + let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); + + //So we expect that to be null until it gets created + expect(mobileMenuList).toBe(null); + + // Call the Header initialization function + await Header(); + + //We update the variable to hold the ul element from the container that has been created + mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); + + expect(mobileMenuList).not.toBeNull(); + expect(mobileMenuList.classList).toContain('nhsuk-header__drop-down'); + expect(mobileMenuList.classList).toContain('nhsuk-header__drop-down--hidden'); + }); + + it.only('Should update navigation', async () => { + const mobileMenuToggleButton = document.querySelector('.nhsuk-header__menu-toggle'); + const mobileMenuContainer = document.querySelector('.nhsuk-mobile-menu-container'); + + expect(mobileMenuToggleButton.classList.contains('nhsuk-header__menu-toggle--visible')).toBe(false); + expect(mobileMenuContainer.classList.contains('nhsuk-mobile-menu-container--visible')).toBe(false); + + Header(); + + expect(mobileMenuToggleButton.classList.contains('nhsuk-header__menu-toggle--visible')).toEqual(true); + expect(mobileMenuContainer.classList.contains('nhsuk-mobile-menu-container--visible')).toEqual(true); + }); + +}) \ No newline at end of file From f3ac2eb910d5f46d90a8846516c30c23a2174e6e Mon Sep 17 00:00:00 2001 From: diana-nita Date: Fri, 3 Nov 2023 10:56:56 +0000 Subject: [PATCH 2/4] updated tests --- tests/integration/jsdom/header.test.js | 61 +++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/tests/integration/jsdom/header.test.js b/tests/integration/jsdom/header.test.js index aed958ad1..0c40c38de 100644 --- a/tests/integration/jsdom/header.test.js +++ b/tests/integration/jsdom/header.test.js @@ -19,6 +19,7 @@ describe('Header class', () => {
    ` }) + it('Should create navigation elements in the DOM', async () => { // Call the Header initialization function await Header(); @@ -98,17 +99,67 @@ describe('Header class', () => { expect(mobileMenuList.classList).toContain('nhsuk-header__drop-down--hidden'); }); - it.only('Should update navigation', async () => { + it('Should not update navigation when the available space is enough for all elements', async () => { const mobileMenuToggleButton = document.querySelector('.nhsuk-header__menu-toggle'); const mobileMenuContainer = document.querySelector('.nhsuk-mobile-menu-container'); + const navigationElement = document.querySelector('.nhsuk-navigation'); + const navigationList = document.querySelector('.nhsuk-header__navigation-list'); + let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); + + // Spy on offsetWidth property for navigation element + const navigationOffsetWidthSpy = jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get'); + // Mock offsetWidth for navigation element + navigationOffsetWidthSpy.mockImplementation(function () { + if (this === navigationElement) { + return 1000; // Mock navigation element offsetWidth + } + return 50; // Mock children offsetWidth + }); + + await Header(); + // breakpoints will be [50,100,150,200,250,300,350,400] + // the available space - navigation offsetWidth - will be greater than the last element from the breakpoints array + // meaning we don't need the mobile menu to get any items from the navigation expect(mobileMenuToggleButton.classList.contains('nhsuk-header__menu-toggle--visible')).toBe(false); expect(mobileMenuContainer.classList.contains('nhsuk-mobile-menu-container--visible')).toBe(false); + + mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); + expect(mobileMenuList.children).toHaveLength(0); + expect(navigationList.children).toHaveLength(8); - Header(); - - expect(mobileMenuToggleButton.classList.contains('nhsuk-header__menu-toggle--visible')).toEqual(true); - expect(mobileMenuContainer.classList.contains('nhsuk-mobile-menu-container--visible')).toEqual(true); + navigationOffsetWidthSpy.mockRestore(); }); + it('Should update navigation when the available space is not enough for all elements', async () => { + const mobileMenuToggleButton = document.querySelector('.nhsuk-header__menu-toggle'); + const mobileMenuContainer = document.querySelector('.nhsuk-mobile-menu-container'); + const navigationElement = document.querySelector('.nhsuk-navigation'); + const navigationList = document.querySelector('.nhsuk-header__navigation-list'); + let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); + + // Spy on offsetWidth property for navigation element + const navigationOffsetWidthSpy = jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get'); + // Mock offsetWidth for navigation element + navigationOffsetWidthSpy.mockImplementation(function () { + if (this === navigationElement) { + return 700; // Mock navigation element offsetWidth + } + return 100; // Mock children offsetWidth + }); + + await Header(); + + // breakpoints will be [100,200,300,400,500,600,700,800] + // the available space - navigation offsetWidth - will be smaller than the last element from the breakpoints array + // meaning we need the mobile menu to get 1 item from the navigation + expect(mobileMenuToggleButton.classList.contains('nhsuk-header__menu-toggle--visible')).toBe(true); + expect(mobileMenuContainer.classList.contains('nhsuk-mobile-menu-container--visible')).toBe(true); + + mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); + expect(mobileMenuList.children).toHaveLength(1); + expect(navigationList.children).toHaveLength(7); + + navigationOffsetWidthSpy.mockRestore(); + }); }) \ No newline at end of file From b3379c2a6c1cd9a3ccd1dff9b1e004e37e0ee882 Mon Sep 17 00:00:00 2001 From: diana-nita Date: Fri, 3 Nov 2023 12:17:59 +0000 Subject: [PATCH 3/4] added prettier format --- tests/integration/jsdom/header.test.js | 298 +++++++++++++------------ 1 file changed, 152 insertions(+), 146 deletions(-) diff --git a/tests/integration/jsdom/header.test.js b/tests/integration/jsdom/header.test.js index 0c40c38de..2c9847abb 100644 --- a/tests/integration/jsdom/header.test.js +++ b/tests/integration/jsdom/header.test.js @@ -1,8 +1,8 @@ -import Header from "../../../packages/components/header/header.js"; +import Header from '../../../packages/components/header/header.js' describe('Header class', () => { - beforeEach(() => { - document.body.innerHTML = ` + beforeEach(() => { + document.body.innerHTML = `
  • Health A-Z
  • @@ -18,148 +18,154 @@ describe('Header class', () => {
    ` + }) + + it('Should create navigation elements in the DOM', async () => { + // Call the Header initialization function + await Header() + + // Ensure the navigation elements are created in the DOM + expect(document.querySelector('.nhsuk-navigation')).not.toBeNull() + }) + + it('Should toggle mobile menu visibility', async () => { + const toggleButton = document.querySelector('.nhsuk-header__menu-toggle') + let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul') + // Call the Header initialization function + await Header() + + mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul') + // Initially, the menu should be closed + expect(mobileMenuList.classList.contains('nhsuk-header__drop-down--hidden')).toBe(true) + + // Open the mobile menu + toggleButton.click() + + expect(mobileMenuList.classList.contains('nhsuk-header__drop-down--hidden')).toBe(false) + + // Close the mobile menu + toggleButton.click() + + expect(mobileMenuList.classList.contains('nhsuk-header__drop-down--hidden')).toBe(true) + }) + + it('Should close menu when escape key is pressed', async () => { + //define a event for the escape key + const escapeKeyEvent = new KeyboardEvent('keydown', { + key: 'Escape', + code: 'Escape', + keyCode: 27, + which: 27, + charCode: 27 + }) + const toggleButton = document.querySelector('.nhsuk-header__menu-toggle') + + await Header() + + //Expect the menu to be hidden initially + expect( + document.querySelector('.nhsuk-header__drop-down').classList.contains('nhsuk-header__drop-down--hidden') + ).toBe(true) + + //Toogle the menu - open it + toggleButton.click() + expect( + document.querySelector('.nhsuk-header__drop-down').classList.contains('nhsuk-header__drop-down--hidden') + ).toBe(false) + + //Press the escape key to close it + document.dispatchEvent(escapeKeyEvent) + expect( + document.querySelector('.nhsuk-header__drop-down').classList.contains('nhsuk-header__drop-down--hidden') + ).toBe(true) + }) + + it('Should setup the Mobile Menu Container during initialization', async () => { + const mobileMenuContainer = document.querySelector('.nhsuk-mobile-menu-container') + expect(mobileMenuContainer.childElementCount).toBe(0) + await Header() + expect(mobileMenuContainer.childElementCount).toBeGreaterThan(0) + }) + + it('Should setup the Mobile Menu List during initialization', async () => { + //Initially there won't be any ul elements inside the container- it gets added in the setupMobileMenu method + let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul') + + //So we expect that to be null until it gets created + expect(mobileMenuList).toBe(null) + + // Call the Header initialization function + await Header() + + //We update the variable to hold the ul element from the container that has been created + mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul') + + expect(mobileMenuList).not.toBeNull() + expect(mobileMenuList.classList).toContain('nhsuk-header__drop-down') + expect(mobileMenuList.classList).toContain('nhsuk-header__drop-down--hidden') + }) + + it('Should not update navigation when the available space is enough for all elements', async () => { + const mobileMenuToggleButton = document.querySelector('.nhsuk-header__menu-toggle') + const mobileMenuContainer = document.querySelector('.nhsuk-mobile-menu-container') + const navigationElement = document.querySelector('.nhsuk-navigation') + const navigationList = document.querySelector('.nhsuk-header__navigation-list') + let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul') + + // Spy on offsetWidth property for navigation element + const navigationOffsetWidthSpy = jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get') + // Mock offsetWidth for navigation element + navigationOffsetWidthSpy.mockImplementation(function () { + if (this === navigationElement) { + return 1000 // Mock navigation element offsetWidth + } + return 50 // Mock children offsetWidth + }) + + await Header() + + // breakpoints will be [50,100,150,200,250,300,350,400] + // the available space - navigation offsetWidth - will be greater than the last element from the breakpoints array + // meaning we don't need the mobile menu to get any items from the navigation + expect(mobileMenuToggleButton.classList.contains('nhsuk-header__menu-toggle--visible')).toBe(false) + expect(mobileMenuContainer.classList.contains('nhsuk-mobile-menu-container--visible')).toBe(false) + + mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul') + expect(mobileMenuList.children).toHaveLength(0) + expect(navigationList.children).toHaveLength(8) + + navigationOffsetWidthSpy.mockRestore() + }) + + it('Should update navigation when the available space is not enough for all elements', async () => { + const mobileMenuToggleButton = document.querySelector('.nhsuk-header__menu-toggle') + const mobileMenuContainer = document.querySelector('.nhsuk-mobile-menu-container') + const navigationElement = document.querySelector('.nhsuk-navigation') + const navigationList = document.querySelector('.nhsuk-header__navigation-list') + let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul') + + // Spy on offsetWidth property for navigation element + const navigationOffsetWidthSpy = jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get') + // Mock offsetWidth for navigation element + navigationOffsetWidthSpy.mockImplementation(function () { + if (this === navigationElement) { + return 700 // Mock navigation element offsetWidth + } + return 100 // Mock children offsetWidth }) - it('Should create navigation elements in the DOM', async () => { - // Call the Header initialization function - await Header(); - - // Ensure the navigation elements are created in the DOM - expect(document.querySelector('.nhsuk-navigation')).not.toBeNull(); - }); - - it('Should toggle mobile menu visibility', async () => { - const toggleButton = document.querySelector('.nhsuk-header__menu-toggle'); - let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); - // Call the Header initialization function - await Header(); - - mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); - // Initially, the menu should be closed - expect(mobileMenuList.classList.contains('nhsuk-header__drop-down--hidden')).toBe(true); - - // Open the mobile menu - toggleButton.click(); - - expect(mobileMenuList.classList.contains('nhsuk-header__drop-down--hidden')).toBe(false); - - // Close the mobile menu - toggleButton.click(); - - expect(mobileMenuList.classList.contains('nhsuk-header__drop-down--hidden')).toBe(true); - }); - - it('Should close menu when escape key is pressed', async () => { - //define a event for the escape key - const escapeKeyEvent = new KeyboardEvent('keydown', { - key: 'Escape', - code: 'Escape', - keyCode: 27, - which: 27, - charCode: 27, - }); - const toggleButton = document.querySelector('.nhsuk-header__menu-toggle'); - - await Header(); - - //Expect the menu to be hidden initially - expect(document.querySelector('.nhsuk-header__drop-down').classList.contains('nhsuk-header__drop-down--hidden')).toBe(true); - - //Toogle the menu - open it - toggleButton.click(); - expect(document.querySelector('.nhsuk-header__drop-down').classList.contains('nhsuk-header__drop-down--hidden')).toBe(false); - - //Press the escape key to close it - document.dispatchEvent(escapeKeyEvent); - expect(document.querySelector('.nhsuk-header__drop-down').classList.contains('nhsuk-header__drop-down--hidden')).toBe(true); - }); - - it('Should setup the Mobile Menu Container during initialization', async () => { - const mobileMenuContainer = document.querySelector('.nhsuk-mobile-menu-container'); - expect(mobileMenuContainer.childElementCount).toBe(0); - await Header(); - expect(mobileMenuContainer.childElementCount).toBeGreaterThan(0); - }); - - it('Should setup the Mobile Menu List during initialization', async () => { - //Initially there won't be any ul elements inside the container- it gets added in the setupMobileMenu method - let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); - - //So we expect that to be null until it gets created - expect(mobileMenuList).toBe(null); - - // Call the Header initialization function - await Header(); - - //We update the variable to hold the ul element from the container that has been created - mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); - - expect(mobileMenuList).not.toBeNull(); - expect(mobileMenuList.classList).toContain('nhsuk-header__drop-down'); - expect(mobileMenuList.classList).toContain('nhsuk-header__drop-down--hidden'); - }); - - it('Should not update navigation when the available space is enough for all elements', async () => { - const mobileMenuToggleButton = document.querySelector('.nhsuk-header__menu-toggle'); - const mobileMenuContainer = document.querySelector('.nhsuk-mobile-menu-container'); - const navigationElement = document.querySelector('.nhsuk-navigation'); - const navigationList = document.querySelector('.nhsuk-header__navigation-list'); - let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); - - // Spy on offsetWidth property for navigation element - const navigationOffsetWidthSpy = jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get'); - // Mock offsetWidth for navigation element - navigationOffsetWidthSpy.mockImplementation(function () { - if (this === navigationElement) { - return 1000; // Mock navigation element offsetWidth - } - return 50; // Mock children offsetWidth - }); - - await Header(); - - // breakpoints will be [50,100,150,200,250,300,350,400] - // the available space - navigation offsetWidth - will be greater than the last element from the breakpoints array - // meaning we don't need the mobile menu to get any items from the navigation - expect(mobileMenuToggleButton.classList.contains('nhsuk-header__menu-toggle--visible')).toBe(false); - expect(mobileMenuContainer.classList.contains('nhsuk-mobile-menu-container--visible')).toBe(false); - - mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); - expect(mobileMenuList.children).toHaveLength(0); - expect(navigationList.children).toHaveLength(8); - - navigationOffsetWidthSpy.mockRestore(); - }); - - it('Should update navigation when the available space is not enough for all elements', async () => { - const mobileMenuToggleButton = document.querySelector('.nhsuk-header__menu-toggle'); - const mobileMenuContainer = document.querySelector('.nhsuk-mobile-menu-container'); - const navigationElement = document.querySelector('.nhsuk-navigation'); - const navigationList = document.querySelector('.nhsuk-header__navigation-list'); - let mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); - - // Spy on offsetWidth property for navigation element - const navigationOffsetWidthSpy = jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get'); - // Mock offsetWidth for navigation element - navigationOffsetWidthSpy.mockImplementation(function () { - if (this === navigationElement) { - return 700; // Mock navigation element offsetWidth - } - return 100; // Mock children offsetWidth - }); - - await Header(); - - // breakpoints will be [100,200,300,400,500,600,700,800] - // the available space - navigation offsetWidth - will be smaller than the last element from the breakpoints array - // meaning we need the mobile menu to get 1 item from the navigation - expect(mobileMenuToggleButton.classList.contains('nhsuk-header__menu-toggle--visible')).toBe(true); - expect(mobileMenuContainer.classList.contains('nhsuk-mobile-menu-container--visible')).toBe(true); - - mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul'); - expect(mobileMenuList.children).toHaveLength(1); - expect(navigationList.children).toHaveLength(7); - - navigationOffsetWidthSpy.mockRestore(); - }); -}) \ No newline at end of file + await Header() + + // breakpoints will be [100,200,300,400,500,600,700,800] + // the available space - navigation offsetWidth - will be smaller than the last element from the breakpoints array + // meaning we need the mobile menu to get 1 item from the navigation + expect(mobileMenuToggleButton.classList.contains('nhsuk-header__menu-toggle--visible')).toBe(true) + expect(mobileMenuContainer.classList.contains('nhsuk-mobile-menu-container--visible')).toBe(true) + + mobileMenuList = document.querySelector('.nhsuk-mobile-menu-container ul') + expect(mobileMenuList.children).toHaveLength(1) + expect(navigationList.children).toHaveLength(7) + + navigationOffsetWidthSpy.mockRestore() + }) +}) From e6c3e0bf184ec9ec80aed08a8db6cff980b848dc Mon Sep 17 00:00:00 2001 From: Mayank Patel Date: Mon, 13 Nov 2023 10:58:27 +0000 Subject: [PATCH 4/4] Update changelog.md --- CHANGELOG.md | 8 +++++++- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a70b61f8b..c65d869d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # NHS.UK frontend Changelog -## 8.0.2 - 019 October 2023 +## 8.0.3 - 13 November 2023 + +:wrench: **Fixes** + +- Updated header component unit tests ([PR 900](https://github.com/nhsuk/nhsuk-frontend/pull/900)). + +## 8.0.2 - 19 October 2023 :wrench: **Fixes** diff --git a/package-lock.json b/package-lock.json index eaae0ef9d..3ee530db7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nhsuk-frontend", - "version": "8.0.2", + "version": "8.0.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "nhsuk-frontend", - "version": "8.0.2", + "version": "8.0.3", "license": "MIT", "devDependencies": { "@babel/core": "^7.18.6", diff --git a/package.json b/package.json index e48a569ee..a2f09fe59 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nhsuk-frontend", - "version": "8.0.2", + "version": "8.0.3", "description": "NHS.UK frontend contains the code you need to start building user interfaces for NHS websites and services.", "scripts": { "prepare": "gulp bundle",