From 9d69a6cc67b172a6083d0a618046ef817f7257ad Mon Sep 17 00:00:00 2001 From: Ayo Date: Sun, 24 May 2026 15:15:51 +0200 Subject: [PATCH] chore: add vitest-dom & sw test --- package.json | 3 +- pnpm-lock.yaml | 60 +++++++++++++++++++++++++++++++ tests/sw.test.js | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 tests/sw.test.js diff --git a/package.json b/package.json index 86c13ea..6238099 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,8 @@ "typescript-eslint": "^8.59.3", "unified": "^11.0.5", "vite-plugin-static-copy": "^4.1.0", - "vitest": "^4.1.7" + "vitest": "^4.1.7", + "vitest-dom": "^0.1.1" }, "lint-staged": { "*.{js,mjs,astro,ts}": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f56674c..0d8ee1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,6 +111,9 @@ importers: vitest: specifier: ^4.1.7 version: 4.1.7(@types/node@25.8.0)(vite@7.3.3(@types/node@25.8.0)(jiti@2.7.0)(yaml@2.9.0)) + vitest-dom: + specifier: ^0.1.1 + version: 0.1.1(vitest@4.1.7(@types/node@25.8.0)(vite@7.3.3(@types/node@25.8.0)(jiti@2.7.0)(yaml@2.9.0))) packages: @@ -1088,6 +1091,10 @@ packages: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} @@ -1196,6 +1203,9 @@ packages: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -1267,6 +1277,9 @@ packages: resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -1690,6 +1703,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -1888,6 +1905,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} @@ -2288,6 +2308,10 @@ packages: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} + redent@4.0.0: + resolution: {integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==} + engines: {node: '>=12'} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -2523,6 +2547,10 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} + suf-log@2.5.3: resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==} @@ -2804,6 +2832,11 @@ packages: vite: optional: true + vitest-dom@0.1.1: + resolution: {integrity: sha512-n/bonR2hcRHCE5hlzG/P0yTXTUXx/gPtsaeUWP86ADfwo/+dHDpnTTV14qY7+kevsUbOZFYECu77MXY7AA0QSA==} + peerDependencies: + vitest: '>=0.31.0' + vitest@4.1.7: resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -3911,6 +3944,8 @@ snapshots: chai@6.2.2: {} + chalk@5.6.2: {} + character-entities-html4@2.1.0: {} character-entities-legacy@3.0.0: {} @@ -4028,6 +4063,8 @@ snapshots: css-what@6.2.2: {} + css.escape@1.5.1: {} + cssesc@3.0.0: {} csso@5.0.5: @@ -4093,6 +4130,8 @@ snapshots: diff@8.0.4: {} + dom-accessibility-api@0.6.3: {} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -4687,6 +4726,8 @@ snapshots: imurmurhash@0.1.4: {} + indent-string@5.0.0: {} + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -4890,6 +4931,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash-es@4.18.1: {} + log-update@6.1.0: dependencies: ansi-escapes: 7.3.0 @@ -5474,6 +5517,11 @@ snapshots: readdirp@5.0.0: {} + redent@4.0.0: + dependencies: + indent-string: 5.0.0 + strip-indent: 4.1.1 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.9 @@ -5862,6 +5910,8 @@ snapshots: dependencies: ansi-regex: 6.2.2 + strip-indent@4.1.1: {} + suf-log@2.5.3: dependencies: s.color: 0.0.15 @@ -6113,6 +6163,16 @@ snapshots: optionalDependencies: vite: 7.3.3(@types/node@25.8.0)(jiti@2.7.0)(yaml@2.9.0) + vitest-dom@0.1.1(vitest@4.1.7(@types/node@25.8.0)(vite@7.3.3(@types/node@25.8.0)(jiti@2.7.0)(yaml@2.9.0))): + dependencies: + aria-query: 5.3.2 + chalk: 5.6.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash-es: 4.18.1 + redent: 4.0.0 + vitest: 4.1.7(@types/node@25.8.0)(vite@7.3.3(@types/node@25.8.0)(jiti@2.7.0)(yaml@2.9.0)) + vitest@4.1.7(@types/node@25.8.0)(vite@7.3.3(@types/node@25.8.0)(jiti@2.7.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.7 diff --git a/tests/sw.test.js b/tests/sw.test.js new file mode 100644 index 0000000..6672e29 --- /dev/null +++ b/tests/sw.test.js @@ -0,0 +1,91 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { install } from 'vitest-dom' + +// Install DOM environment for vitest +install() + +// Mock the service worker globals +global.caches = { + keys: vi.fn(), + delete: vi.fn(), + open: vi.fn(), + match: vi.fn(), +} + +// Mock the global self object +global.self = { + skipWaiting: vi.fn(), + addEventListener: vi.fn(), +} + +// Mock the fetch function +global.fetch = vi.fn() + +// Import the service worker module +// Note: We need to use dynamic import to avoid module loading issues +let swModule + +beforeEach(async () => { + // Clear all mocks + vi.clearAllMocks() + + // Mock the module + swModule = await import('../src/sw.mjs') +}) + +describe('Service Worker', () => { + describe('cleanOldCaches', () => { + it('should delete old caches', async () => { + const cacheName = 'app-v000' + const oldCacheName = 'app-v001' + + global.caches.keys.mockResolvedValue([cacheName, oldCacheName]) + global.caches.delete.mockResolvedValue(true) + + await swModule.cleanOldCaches() + + expect(global.caches.delete).toHaveBeenCalledWith(oldCacheName) + }) + }) + + describe('addResourcesToCache', () => { + it('should add resources to cache', async () => { + const resources = ['/index.html', '/style.css'] + const cache = { addAll: vi.fn() } + + global.caches.open.mockResolvedValue(cache) + + await swModule.addResourcesToCache(resources) + + expect(cache.addAll).toHaveBeenCalledWith(resources) + }) + }) + + describe('networkFirst', () => { + it('should return network response when available', async () => { + const request = new Request('/test') + const networkResponse = new Response('network content') + + global.fetch.mockResolvedValue(networkResponse) + global.caches.open.mockResolvedValue({ put: vi.fn() }) + + const result = await swModule.networkFirst({ request }) + + expect(result).toEqual(networkResponse) + }) + + it('should return cached response when network fails', async () => { + const request = new Request('/test') + const cachedResponse = new Response('cached content') + + global.fetch.mockRejectedValue(new Error('Network error')) + global.caches.open.mockResolvedValue({ + match: vi.fn().mockResolvedValue(cachedResponse), + }) + + const result = await swModule.networkFirst({ request }) + + expect(result).toEqual(cachedResponse) + }) + }) +})