@markwharton/pwa-push 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const vitest_1 = require("vitest");
37
+ // Storage for mock
38
+ let mockStore = {};
39
+ // Mock localStorage
40
+ const localStorageMock = {
41
+ getItem: vitest_1.vi.fn((key) => mockStore[key] || null),
42
+ setItem: vitest_1.vi.fn((key, value) => { mockStore[key] = value; }),
43
+ removeItem: vitest_1.vi.fn((key) => { delete mockStore[key]; }),
44
+ clear: vitest_1.vi.fn(() => { mockStore = {}; })
45
+ };
46
+ // Mock crypto.getRandomValues
47
+ const mockGetRandomValues = vitest_1.vi.fn((array) => {
48
+ for (let i = 0; i < array.length; i++) {
49
+ array[i] = Math.floor(Math.random() * 256);
50
+ }
51
+ return array;
52
+ });
53
+ // Helper to setup global mocks (needed after vi.resetModules)
54
+ function setupGlobalMocks() {
55
+ Object.defineProperty(global, 'localStorage', {
56
+ value: localStorageMock,
57
+ writable: true,
58
+ configurable: true
59
+ });
60
+ Object.defineProperty(global, 'crypto', {
61
+ value: { getRandomValues: mockGetRandomValues },
62
+ writable: true,
63
+ configurable: true
64
+ });
65
+ }
66
+ (0, vitest_1.beforeEach)(() => {
67
+ vitest_1.vi.resetModules();
68
+ mockStore = {};
69
+ localStorageMock.getItem.mockClear();
70
+ localStorageMock.setItem.mockClear();
71
+ localStorageMock.removeItem.mockClear();
72
+ setupGlobalMocks();
73
+ });
74
+ (0, vitest_1.describe)('Device ID utilities', () => {
75
+ (0, vitest_1.describe)('getDeviceId', () => {
76
+ (0, vitest_1.it)('returns existing deviceId from localStorage', async () => {
77
+ mockStore['push_device_id'] = 'existing-uuid';
78
+ const { getDeviceId } = await Promise.resolve().then(() => __importStar(require('../../client/deviceId')));
79
+ const result = getDeviceId();
80
+ (0, vitest_1.expect)(result).toBe('existing-uuid');
81
+ });
82
+ (0, vitest_1.it)('generates new UUID if not in localStorage', async () => {
83
+ const { getDeviceId } = await Promise.resolve().then(() => __importStar(require('../../client/deviceId')));
84
+ const result = getDeviceId();
85
+ (0, vitest_1.expect)(result).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
86
+ });
87
+ (0, vitest_1.it)('saves generated UUID to localStorage', async () => {
88
+ const { getDeviceId } = await Promise.resolve().then(() => __importStar(require('../../client/deviceId')));
89
+ const result = getDeviceId();
90
+ (0, vitest_1.expect)(localStorageMock.setItem).toHaveBeenCalledWith('push_device_id', result);
91
+ });
92
+ (0, vitest_1.it)('returns same ID on repeated calls', async () => {
93
+ const { getDeviceId } = await Promise.resolve().then(() => __importStar(require('../../client/deviceId')));
94
+ const id1 = getDeviceId();
95
+ const id2 = getDeviceId();
96
+ (0, vitest_1.expect)(id1).toBe(id2);
97
+ });
98
+ });
99
+ (0, vitest_1.describe)('clearDeviceId', () => {
100
+ (0, vitest_1.it)('removes deviceId from localStorage', async () => {
101
+ mockStore['push_device_id'] = 'test-uuid';
102
+ const { clearDeviceId } = await Promise.resolve().then(() => __importStar(require('../../client/deviceId')));
103
+ clearDeviceId();
104
+ (0, vitest_1.expect)(localStorageMock.removeItem).toHaveBeenCalledWith('push_device_id');
105
+ });
106
+ (0, vitest_1.it)('allows new ID generation after clear', async () => {
107
+ const { getDeviceId, clearDeviceId } = await Promise.resolve().then(() => __importStar(require('../../client/deviceId')));
108
+ getDeviceId();
109
+ clearDeviceId();
110
+ // Verify removeItem was called
111
+ (0, vitest_1.expect)(localStorageMock.removeItem).toHaveBeenCalledWith('push_device_id');
112
+ });
113
+ });
114
+ (0, vitest_1.describe)('UUID format', () => {
115
+ (0, vitest_1.it)('generates valid UUID v4 format', async () => {
116
+ const { getDeviceId } = await Promise.resolve().then(() => __importStar(require('../../client/deviceId')));
117
+ const uuid = getDeviceId();
118
+ // UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
119
+ // where y is 8, 9, a, or b
120
+ (0, vitest_1.expect)(uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
121
+ });
122
+ (0, vitest_1.it)('generates unique UUIDs across module resets', async () => {
123
+ const uuids = new Set();
124
+ for (let i = 0; i < 10; i++) {
125
+ vitest_1.vi.resetModules();
126
+ mockStore = {};
127
+ setupGlobalMocks();
128
+ const { getDeviceId } = await Promise.resolve().then(() => __importStar(require('../../client/deviceId')));
129
+ uuids.add(getDeviceId());
130
+ }
131
+ (0, vitest_1.expect)(uuids.size).toBe(10);
132
+ });
133
+ });
134
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const vitest_1 = require("vitest");
37
+ // Mock atob for Node.js environment
38
+ (0, vitest_1.beforeEach)(() => {
39
+ global.atob = (str) => Buffer.from(str, 'base64').toString('binary');
40
+ });
41
+ (0, vitest_1.describe)('urlBase64ToUint8Array', () => {
42
+ (0, vitest_1.it)('converts standard base64 to Uint8Array', async () => {
43
+ const { urlBase64ToUint8Array } = await Promise.resolve().then(() => __importStar(require('../../client/encoding')));
44
+ const base64 = 'SGVsbG8gV29ybGQ='; // "Hello World"
45
+ const result = urlBase64ToUint8Array(base64);
46
+ (0, vitest_1.expect)(result).toBeInstanceOf(Uint8Array);
47
+ const decoded = new TextDecoder().decode(result);
48
+ (0, vitest_1.expect)(decoded).toBe('Hello World');
49
+ });
50
+ (0, vitest_1.it)('handles URL-safe base64 characters', async () => {
51
+ const { urlBase64ToUint8Array } = await Promise.resolve().then(() => __importStar(require('../../client/encoding')));
52
+ // URL-safe base64 uses - instead of + and _ instead of /
53
+ const urlSafeBase64 = 'SGVsbG8-V29ybGRf'; // Contains - and _
54
+ const result = urlBase64ToUint8Array(urlSafeBase64);
55
+ (0, vitest_1.expect)(result).toBeInstanceOf(Uint8Array);
56
+ (0, vitest_1.expect)(result.length).toBeGreaterThan(0);
57
+ });
58
+ (0, vitest_1.it)('adds padding when needed', async () => {
59
+ const { urlBase64ToUint8Array } = await Promise.resolve().then(() => __importStar(require('../../client/encoding')));
60
+ // Base64 without padding (should be 'ab==' with padding)
61
+ const noPadding = 'YWI';
62
+ const result = urlBase64ToUint8Array(noPadding);
63
+ (0, vitest_1.expect)(result).toBeInstanceOf(Uint8Array);
64
+ const decoded = new TextDecoder().decode(result);
65
+ (0, vitest_1.expect)(decoded).toBe('ab');
66
+ });
67
+ (0, vitest_1.it)('works with VAPID-like keys', async () => {
68
+ const { urlBase64ToUint8Array } = await Promise.resolve().then(() => __importStar(require('../../client/encoding')));
69
+ // Typical VAPID public key format (65 bytes)
70
+ const vapidKey = 'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U';
71
+ const result = urlBase64ToUint8Array(vapidKey);
72
+ (0, vitest_1.expect)(result).toBeInstanceOf(Uint8Array);
73
+ (0, vitest_1.expect)(result.length).toBe(65); // VAPID public keys are 65 bytes
74
+ });
75
+ (0, vitest_1.it)('returns correct byte values', async () => {
76
+ const { urlBase64ToUint8Array } = await Promise.resolve().then(() => __importStar(require('../../client/encoding')));
77
+ // 'AQID' decodes to bytes [1, 2, 3]
78
+ const result = urlBase64ToUint8Array('AQID');
79
+ (0, vitest_1.expect)(result[0]).toBe(1);
80
+ (0, vitest_1.expect)(result[1]).toBe(2);
81
+ (0, vitest_1.expect)(result[2]).toBe(3);
82
+ });
83
+ (0, vitest_1.it)('handles empty string', async () => {
84
+ const { urlBase64ToUint8Array } = await Promise.resolve().then(() => __importStar(require('../../client/encoding')));
85
+ const result = urlBase64ToUint8Array('');
86
+ (0, vitest_1.expect)(result).toBeInstanceOf(Uint8Array);
87
+ (0, vitest_1.expect)(result.length).toBe(0);
88
+ });
89
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const vitest_1 = require("vitest");
37
+ // Mock IndexedDB
38
+ const mockObjectStore = {
39
+ put: vitest_1.vi.fn(),
40
+ get: vitest_1.vi.fn(),
41
+ delete: vitest_1.vi.fn()
42
+ };
43
+ const mockTransaction = {
44
+ objectStore: vitest_1.vi.fn(() => mockObjectStore),
45
+ oncomplete: null
46
+ };
47
+ const mockDatabase = {
48
+ transaction: vitest_1.vi.fn(() => mockTransaction),
49
+ objectStoreNames: {
50
+ contains: vitest_1.vi.fn(() => false)
51
+ },
52
+ createObjectStore: vitest_1.vi.fn(),
53
+ close: vitest_1.vi.fn()
54
+ };
55
+ const mockOpenRequest = {
56
+ result: mockDatabase,
57
+ error: null,
58
+ onsuccess: null,
59
+ onerror: null,
60
+ onupgradeneeded: null
61
+ };
62
+ (0, vitest_1.beforeEach)(() => {
63
+ vitest_1.vi.resetModules();
64
+ vitest_1.vi.clearAllMocks();
65
+ // Reset mock implementations
66
+ mockOpenRequest.result = mockDatabase;
67
+ mockOpenRequest.error = null;
68
+ global.indexedDB = {
69
+ open: vitest_1.vi.fn((dbName, version) => {
70
+ // Simulate async open
71
+ setTimeout(() => {
72
+ if (mockOpenRequest.onupgradeneeded) {
73
+ mockOpenRequest.onupgradeneeded({ target: mockOpenRequest });
74
+ }
75
+ if (mockOpenRequest.onsuccess) {
76
+ mockOpenRequest.onsuccess();
77
+ }
78
+ }, 0);
79
+ return mockOpenRequest;
80
+ })
81
+ };
82
+ });
83
+ (0, vitest_1.describe)('IndexedDB utilities', () => {
84
+ (0, vitest_1.describe)('saveSubscriptionData', () => {
85
+ (0, vitest_1.it)('saves data to IndexedDB', async () => {
86
+ const putRequest = {
87
+ onsuccess: null,
88
+ onerror: null,
89
+ result: undefined
90
+ };
91
+ mockObjectStore.put.mockReturnValue(putRequest);
92
+ const { saveSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
93
+ const savePromise = saveSubscriptionData({
94
+ publicKey: 'test-key',
95
+ deviceId: 'test-device',
96
+ basePath: '/app'
97
+ });
98
+ // Simulate success
99
+ await new Promise(resolve => setTimeout(resolve, 10));
100
+ if (putRequest.onsuccess)
101
+ putRequest.onsuccess();
102
+ await (0, vitest_1.expect)(savePromise).resolves.toBeUndefined();
103
+ (0, vitest_1.expect)(mockObjectStore.put).toHaveBeenCalledWith({ publicKey: 'test-key', deviceId: 'test-device', basePath: '/app' }, 'current');
104
+ });
105
+ (0, vitest_1.it)('opens database with correct name and version', async () => {
106
+ const putRequest = { onsuccess: null, onerror: null, result: undefined };
107
+ mockObjectStore.put.mockReturnValue(putRequest);
108
+ const { saveSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
109
+ saveSubscriptionData({
110
+ publicKey: 'key',
111
+ deviceId: 'device',
112
+ basePath: '/'
113
+ });
114
+ await new Promise(resolve => setTimeout(resolve, 10));
115
+ (0, vitest_1.expect)(global.indexedDB.open).toHaveBeenCalledWith('PushSubscriptionDB', 1);
116
+ });
117
+ });
118
+ (0, vitest_1.describe)('getSubscriptionData', () => {
119
+ (0, vitest_1.it)('retrieves data from IndexedDB', async () => {
120
+ const testData = {
121
+ publicKey: 'stored-key',
122
+ deviceId: 'stored-device',
123
+ basePath: '/stored'
124
+ };
125
+ const getRequest = {
126
+ onsuccess: null,
127
+ onerror: null,
128
+ result: testData
129
+ };
130
+ mockObjectStore.get.mockReturnValue(getRequest);
131
+ const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
132
+ const getPromise = getSubscriptionData();
133
+ // Simulate success
134
+ await new Promise(resolve => setTimeout(resolve, 10));
135
+ if (getRequest.onsuccess)
136
+ getRequest.onsuccess();
137
+ const result = await getPromise;
138
+ (0, vitest_1.expect)(result).toEqual(testData);
139
+ (0, vitest_1.expect)(mockObjectStore.get).toHaveBeenCalledWith('current');
140
+ });
141
+ (0, vitest_1.it)('returns null when no data exists', async () => {
142
+ const getRequest = {
143
+ onsuccess: null,
144
+ onerror: null,
145
+ result: undefined
146
+ };
147
+ mockObjectStore.get.mockReturnValue(getRequest);
148
+ const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
149
+ const getPromise = getSubscriptionData();
150
+ await new Promise(resolve => setTimeout(resolve, 10));
151
+ if (getRequest.onsuccess)
152
+ getRequest.onsuccess();
153
+ const result = await getPromise;
154
+ (0, vitest_1.expect)(result).toBeNull();
155
+ });
156
+ });
157
+ (0, vitest_1.describe)('clearSubscriptionData', () => {
158
+ (0, vitest_1.it)('deletes data from IndexedDB', async () => {
159
+ const deleteRequest = {
160
+ onsuccess: null,
161
+ onerror: null,
162
+ result: undefined
163
+ };
164
+ mockObjectStore.delete.mockReturnValue(deleteRequest);
165
+ const { clearSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
166
+ const clearPromise = clearSubscriptionData();
167
+ await new Promise(resolve => setTimeout(resolve, 10));
168
+ if (deleteRequest.onsuccess)
169
+ deleteRequest.onsuccess();
170
+ await (0, vitest_1.expect)(clearPromise).resolves.toBeUndefined();
171
+ (0, vitest_1.expect)(mockObjectStore.delete).toHaveBeenCalledWith('current');
172
+ });
173
+ });
174
+ (0, vitest_1.describe)('database initialization', () => {
175
+ (0, vitest_1.it)('creates object store on upgrade', async () => {
176
+ mockDatabase.objectStoreNames.contains.mockReturnValue(false);
177
+ const getRequest = { onsuccess: null, onerror: null, result: undefined };
178
+ mockObjectStore.get.mockReturnValue(getRequest);
179
+ const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
180
+ getSubscriptionData();
181
+ await new Promise(resolve => setTimeout(resolve, 10));
182
+ (0, vitest_1.expect)(mockDatabase.createObjectStore).toHaveBeenCalledWith('subscriptionData');
183
+ });
184
+ (0, vitest_1.it)('skips creating object store if exists', async () => {
185
+ mockDatabase.objectStoreNames.contains.mockReturnValue(true);
186
+ mockDatabase.createObjectStore.mockClear();
187
+ const getRequest = { onsuccess: null, onerror: null, result: undefined };
188
+ mockObjectStore.get.mockReturnValue(getRequest);
189
+ const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
190
+ getSubscriptionData();
191
+ await new Promise(resolve => setTimeout(resolve, 10));
192
+ (0, vitest_1.expect)(mockDatabase.createObjectStore).not.toHaveBeenCalled();
193
+ });
194
+ });
195
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const vitest_1 = require("vitest");
37
+ // Mock dependencies
38
+ vitest_1.vi.mock('../../client/indexedDb', () => ({
39
+ getSubscriptionData: vitest_1.vi.fn()
40
+ }));
41
+ vitest_1.vi.mock('../../client/encoding', () => ({
42
+ urlBase64ToUint8Array: vitest_1.vi.fn(() => new Uint8Array([1, 2, 3]))
43
+ }));
44
+ vitest_1.vi.mock('../../client/subscribe', () => ({
45
+ toPushSubscription: vitest_1.vi.fn((sub) => ({
46
+ endpoint: sub.endpoint,
47
+ keys: { p256dh: 'key', auth: 'auth' }
48
+ }))
49
+ }));
50
+ const mockSubscription = {
51
+ endpoint: 'https://fcm.googleapis.com/renewed',
52
+ toJSON: () => ({ keys: { p256dh: 'key', auth: 'auth' } })
53
+ };
54
+ const mockPushManager = {
55
+ subscribe: vitest_1.vi.fn(() => Promise.resolve(mockSubscription))
56
+ };
57
+ const mockRegistration = {
58
+ pushManager: mockPushManager
59
+ };
60
+ (0, vitest_1.beforeEach)(() => {
61
+ vitest_1.vi.clearAllMocks();
62
+ // Mock service worker global scope
63
+ Object.defineProperty(global, 'self', {
64
+ value: { registration: mockRegistration },
65
+ writable: true,
66
+ configurable: true
67
+ });
68
+ globalThis.registration = mockRegistration;
69
+ // Mock fetch
70
+ global.fetch = vitest_1.vi.fn(() => Promise.resolve({
71
+ ok: true,
72
+ status: 200
73
+ }));
74
+ // Mock console
75
+ global.console = {
76
+ ...console,
77
+ log: vitest_1.vi.fn(),
78
+ error: vitest_1.vi.fn()
79
+ };
80
+ });
81
+ (0, vitest_1.describe)('Push subscription renewal', () => {
82
+ (0, vitest_1.describe)('handleSubscriptionChange', () => {
83
+ const mockEvent = {};
84
+ (0, vitest_1.it)('returns false when no stored data', async () => {
85
+ const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
86
+ vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue(null);
87
+ const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
88
+ const result = await handleSubscriptionChange(mockEvent, '/api/renew');
89
+ (0, vitest_1.expect)(result).toBe(false);
90
+ (0, vitest_1.expect)(console.error).toHaveBeenCalledWith('No subscription data found for renewal');
91
+ });
92
+ (0, vitest_1.it)('renews subscription successfully', async () => {
93
+ const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
94
+ vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
95
+ publicKey: 'test-vapid-key',
96
+ deviceId: 'test-device-id',
97
+ basePath: '/app'
98
+ });
99
+ const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
100
+ const result = await handleSubscriptionChange(mockEvent, '/api/renew');
101
+ (0, vitest_1.expect)(result).toBe(true);
102
+ (0, vitest_1.expect)(fetch).toHaveBeenCalledWith('/api/renew', {
103
+ method: 'POST',
104
+ headers: { 'Content-Type': 'application/json' },
105
+ body: vitest_1.expect.stringContaining('test-device-id')
106
+ });
107
+ (0, vitest_1.expect)(console.log).toHaveBeenCalledWith('Push subscription renewed successfully');
108
+ });
109
+ (0, vitest_1.it)('returns false when resubscribe fails', async () => {
110
+ const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
111
+ vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
112
+ publicKey: 'test-key',
113
+ deviceId: 'device',
114
+ basePath: '/'
115
+ });
116
+ mockPushManager.subscribe.mockRejectedValueOnce(new Error('Failed'));
117
+ const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
118
+ const result = await handleSubscriptionChange(mockEvent, '/api/renew');
119
+ (0, vitest_1.expect)(result).toBe(false);
120
+ (0, vitest_1.expect)(console.error).toHaveBeenCalledWith('Failed to create new subscription');
121
+ });
122
+ (0, vitest_1.it)('returns false when backend request fails', async () => {
123
+ const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
124
+ vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
125
+ publicKey: 'test-key',
126
+ deviceId: 'device',
127
+ basePath: '/'
128
+ });
129
+ global.fetch.mockResolvedValueOnce({
130
+ ok: false,
131
+ status: 500
132
+ });
133
+ const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
134
+ const result = await handleSubscriptionChange(mockEvent, '/api/renew');
135
+ (0, vitest_1.expect)(result).toBe(false);
136
+ (0, vitest_1.expect)(console.error).toHaveBeenCalledWith('Renewal request failed:', 500);
137
+ });
138
+ (0, vitest_1.it)('sends correct renewal request body', async () => {
139
+ const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
140
+ vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
141
+ publicKey: 'my-vapid-key',
142
+ deviceId: 'my-device-id',
143
+ basePath: '/myapp'
144
+ });
145
+ const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
146
+ await handleSubscriptionChange(mockEvent, '/api/push/renew');
147
+ (0, vitest_1.expect)(fetch).toHaveBeenCalledWith('/api/push/renew', {
148
+ method: 'POST',
149
+ headers: { 'Content-Type': 'application/json' },
150
+ body: vitest_1.expect.any(String)
151
+ });
152
+ const body = JSON.parse(fetch.mock.calls[0][1].body);
153
+ (0, vitest_1.expect)(body.deviceId).toBe('my-device-id');
154
+ (0, vitest_1.expect)(body.basePath).toBe('/myapp');
155
+ (0, vitest_1.expect)(body.subscription).toBeDefined();
156
+ });
157
+ (0, vitest_1.it)('handles network error', async () => {
158
+ const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
159
+ vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
160
+ publicKey: 'key',
161
+ deviceId: 'device',
162
+ basePath: '/'
163
+ });
164
+ global.fetch.mockRejectedValueOnce(new Error('Network error'));
165
+ const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
166
+ const result = await handleSubscriptionChange(mockEvent, '/api/renew');
167
+ (0, vitest_1.expect)(result).toBe(false);
168
+ (0, vitest_1.expect)(console.error).toHaveBeenCalledWith('Subscription renewal failed:', vitest_1.expect.any(Error));
169
+ });
170
+ });
171
+ });
@@ -0,0 +1 @@
1
+ export {};