From 48f380e2901878257b635a8c8cb553584d28800e Mon Sep 17 00:00:00 2001 From: James Cruddas Date: Wed, 18 Mar 2026 21:57:27 +0000 Subject: [PATCH] Add focused Jest coverage tests and refresh lockfile --- CHANGELOG.md | 4 + assets/changelog.json | 7 ++ package-lock.json | 159 +++++++++++++------------------ tests/app.coverage-extra.test.js | 136 ++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 92 deletions(-) create mode 100644 tests/app.coverage-extra.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index b03827c..d4bb4c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to PWA Template will be documented in this file. This project adheres to a manual release process; update both this file and `assets/changelog.json` when shipping new versions so the in-app update summary stays accurate. +## [NEXT_VERSION] - [NEXT_DATE] + +- Add extra automated test scenarios for fallback states and navigation actions so reliability checks catch more edge cases. + ## 1.1.38 - 2026-03-18 - Align the default dark mode look and feel with WealthTrack so night-time screens are more consistent across both apps. diff --git a/assets/changelog.json b/assets/changelog.json index bbda1ba..66e06fc 100644 --- a/assets/changelog.json +++ b/assets/changelog.json @@ -1,4 +1,11 @@ [ + { + "version": "[NEXT_VERSION]", + "date": "[NEXT_DATE]", + "changes": [ + "Add more automated tests for edge-case behavior so app reliability is checked more thoroughly." + ] + }, { "version": "1.1.38", "date": "2026-03-18", diff --git a/package-lock.json b/package-lock.json index aa0504b..b45055a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "PWA Template", - "version": "1.1.37", + "version": "1.1.38", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "PWA Template", - "version": "1.1.37", + "version": "1.1.38", "devDependencies": { "@playwright/test": "^1.58.2", "@types/node": "^25.5.0", @@ -2486,6 +2486,19 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -3678,6 +3691,24 @@ "bser": "2.1.1" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -4667,19 +4698,6 @@ "fsevents": "^2.3.3" } }, - "node_modules/jest-haste-map/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-leak-detector": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", @@ -4731,19 +4749,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-message-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-mock": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", @@ -4953,19 +4958,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-validate": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", @@ -5051,15 +5043,13 @@ } }, "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "bin": { - "jiti": "lib/jiti-cli.mjs" + "jiti": "bin/jiti.js" } }, "node_modules/js-tokens": { @@ -6028,6 +6018,19 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -6416,13 +6419,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -6853,6 +6856,19 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7450,16 +7466,6 @@ "node": ">=14.0.0" } }, - "node_modules/tailwindcss/node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -7568,37 +7574,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tldts": { "version": "6.1.86", "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", diff --git a/tests/app.coverage-extra.test.js b/tests/app.coverage-extra.test.js new file mode 100644 index 0000000..0a4300c --- /dev/null +++ b/tests/app.coverage-extra.test.js @@ -0,0 +1,136 @@ +describe('app coverage additions', () => { + const originalFetch = global.fetch; + + beforeEach(() => { + jest.resetModules(); + localStorage.clear(); + sessionStorage.clear(); + document.documentElement.className = ''; + document.body.className = ''; + document.body.innerHTML = ` + + + + + + + + + + +
+
+
+
+ +
body
+
+
+ +
+
+ + +
+ + `; + + global.fetch = jest.fn(async (url) => { + if (String(url).includes('changelog')) { + return { ok: true, json: async () => [] }; + } + if (String(url).includes('version')) { + return { ok: true, json: async () => ({}) }; + } + return { ok: false, json: async () => ({}) }; + }); + + Object.defineProperty(window.navigator, 'serviceWorker', { + configurable: true, + value: { + getRegistration: jest.fn(async () => null), + ready: Promise.resolve(null), + register: jest.fn(async () => ({ unregister: jest.fn() })), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + }, + }); + }); + + afterEach(() => { + global.fetch = originalFetch; + jest.restoreAllMocks(); + }); + + test('falls back invalid stored theme choice to default', async () => { + localStorage.setItem('themeChoice', 'mystery'); + + require('../assets/js/app.js'); + document.dispatchEvent(new Event('DOMContentLoaded')); + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(document.getElementById('themeSelect').value).toBe('default'); + expect(document.documentElement.classList.contains('theme-inverted')).toBe(false); + expect(document.documentElement.classList.contains('theme-glass')).toBe(false); + }); + + test('prunes stale collapsible ids and persists toggle state after click', () => { + localStorage.setItem('collapsedCards', JSON.stringify({ stale: true })); + + require('../assets/js/app.js'); + document.dispatchEvent(new Event('DOMContentLoaded')); + + const trigger = document.querySelector('[data-collapsible-trigger]'); + trigger.click(); + + const collapsedState = JSON.parse(localStorage.getItem('collapsedCards')); + expect(collapsedState).not.toHaveProperty('stale'); + expect(collapsedState).toHaveProperty('example-card'); + }); + + test('handles go-settings action by navigating to settings view', () => { + require('../assets/js/app.js'); + document.dispatchEvent(new Event('DOMContentLoaded')); + + document.getElementById('goSettingsButton').click(); + + expect(document.getElementById('settings').classList.contains('active')).toBe(true); + expect(localStorage.getItem('activeView')).toBe('settings'); + }); + + test('uses window.confirm fallback when confirmation modal is unavailable', async () => { + document.getElementById('modal').remove(); + const confirmSpy = jest.spyOn(window, 'confirm').mockReturnValue(false); + + require('../assets/js/app.js'); + document.dispatchEvent(new Event('DOMContentLoaded')); + + document.getElementById('clearDataButton').click(); + + await new Promise((resolve) => setTimeout(resolve, 0)); + expect(confirmSpy).toHaveBeenCalledWith('This will erase all locally stored data. Continue?'); + }); + + test('shows empty changelog state and keeps default version label when payload is incomplete', async () => { + require('../assets/js/app.js'); + document.dispatchEvent(new Event('DOMContentLoaded')); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(document.querySelector('[data-changelog-empty]').classList.contains('hidden')).toBe(false); + expect(document.querySelector('[data-app-version]').textContent).toBe('0.0.0'); + }); +});