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", diff --git a/tests/integration/jsdom/header.test.js b/tests/integration/jsdom/header.test.js new file mode 100644 index 000000000..2c9847abb --- /dev/null +++ b/tests/integration/jsdom/header.test.js @@ -0,0 +1,171 @@ +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('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() + }) +})