@markwharton/pwa-push 1.5.3 → 2.0.0
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.
- package/dist/client.d.ts +181 -0
- package/dist/client.js +412 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/pwa-push-sw.js +61 -39
- package/dist/{server/send.d.ts → server.d.ts} +6 -2
- package/dist/{server/send.js → server.js} +9 -7
- package/dist/shared.d.ts +22 -2
- package/dist/shared.js +9 -6
- package/dist/sw.d.ts +1 -1
- package/dist/sw.js +2 -2
- package/package.json +11 -8
- package/dist/__tests__/client/deviceId.test.d.ts +0 -1
- package/dist/__tests__/client/deviceId.test.js +0 -134
- package/dist/__tests__/client/encoding.test.d.ts +0 -1
- package/dist/__tests__/client/encoding.test.js +0 -89
- package/dist/__tests__/client/indexedDb.test.d.ts +0 -1
- package/dist/__tests__/client/indexedDb.test.js +0 -195
- package/dist/__tests__/client/renewal.test.d.ts +0 -1
- package/dist/__tests__/client/renewal.test.js +0 -170
- package/dist/__tests__/client/subscribe.test.d.ts +0 -1
- package/dist/__tests__/client/subscribe.test.js +0 -299
- package/dist/__tests__/server/send.test.d.ts +0 -1
- package/dist/__tests__/server/send.test.js +0 -226
- package/dist/client/deviceId.d.ts +0 -23
- package/dist/client/deviceId.js +0 -49
- package/dist/client/encoding.d.ts +0 -17
- package/dist/client/encoding.js +0 -32
- package/dist/client/index.d.ts +0 -4
- package/dist/client/index.js +0 -20
- package/dist/client/indexedDb.d.ts +0 -38
- package/dist/client/indexedDb.js +0 -89
- package/dist/client/renewal.d.ts +0 -31
- package/dist/client/renewal.js +0 -80
- package/dist/client/subscribe.d.ts +0 -88
- package/dist/client/subscribe.js +0 -176
- package/dist/server/index.d.ts +0 -1
- package/dist/server/index.js +0 -11
- package/dist/types.d.ts +0 -39
- package/dist/types.js +0 -11
|
@@ -1,195 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,170 +0,0 @@
|
|
|
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 error 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.ok).toBe(false);
|
|
90
|
-
(0, vitest_1.expect)(result.error).toBe('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.ok).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
|
-
});
|
|
108
|
-
(0, vitest_1.it)('returns error when resubscribe fails', async () => {
|
|
109
|
-
const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
|
|
110
|
-
vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
|
|
111
|
-
publicKey: 'test-key',
|
|
112
|
-
deviceId: 'device',
|
|
113
|
-
basePath: '/'
|
|
114
|
-
});
|
|
115
|
-
mockPushManager.subscribe.mockRejectedValueOnce(new Error('Failed'));
|
|
116
|
-
const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
|
|
117
|
-
const result = await handleSubscriptionChange(mockEvent, '/api/renew');
|
|
118
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
119
|
-
(0, vitest_1.expect)(result.error).toBe('Failed to create new subscription');
|
|
120
|
-
});
|
|
121
|
-
(0, vitest_1.it)('returns error when backend request fails', async () => {
|
|
122
|
-
const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
|
|
123
|
-
vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
|
|
124
|
-
publicKey: 'test-key',
|
|
125
|
-
deviceId: 'device',
|
|
126
|
-
basePath: '/'
|
|
127
|
-
});
|
|
128
|
-
global.fetch.mockResolvedValueOnce({
|
|
129
|
-
ok: false,
|
|
130
|
-
status: 500
|
|
131
|
-
});
|
|
132
|
-
const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
|
|
133
|
-
const result = await handleSubscriptionChange(mockEvent, '/api/renew');
|
|
134
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
135
|
-
(0, vitest_1.expect)(result.error).toBe('Renewal request failed: 500');
|
|
136
|
-
});
|
|
137
|
-
(0, vitest_1.it)('sends correct renewal request body', async () => {
|
|
138
|
-
const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
|
|
139
|
-
vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
|
|
140
|
-
publicKey: 'my-vapid-key',
|
|
141
|
-
deviceId: 'my-device-id',
|
|
142
|
-
basePath: '/myapp'
|
|
143
|
-
});
|
|
144
|
-
const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
|
|
145
|
-
await handleSubscriptionChange(mockEvent, '/api/push/renew');
|
|
146
|
-
(0, vitest_1.expect)(fetch).toHaveBeenCalledWith('/api/push/renew', {
|
|
147
|
-
method: 'POST',
|
|
148
|
-
headers: { 'Content-Type': 'application/json' },
|
|
149
|
-
body: vitest_1.expect.any(String)
|
|
150
|
-
});
|
|
151
|
-
const body = JSON.parse(fetch.mock.calls[0][1].body);
|
|
152
|
-
(0, vitest_1.expect)(body.deviceId).toBe('my-device-id');
|
|
153
|
-
(0, vitest_1.expect)(body.basePath).toBe('/myapp');
|
|
154
|
-
(0, vitest_1.expect)(body.subscription).toBeDefined();
|
|
155
|
-
});
|
|
156
|
-
(0, vitest_1.it)('handles network error', async () => {
|
|
157
|
-
const { getSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
|
|
158
|
-
vitest_1.vi.mocked(getSubscriptionData).mockResolvedValue({
|
|
159
|
-
publicKey: 'key',
|
|
160
|
-
deviceId: 'device',
|
|
161
|
-
basePath: '/'
|
|
162
|
-
});
|
|
163
|
-
global.fetch.mockRejectedValueOnce(new Error('Network error'));
|
|
164
|
-
const { handleSubscriptionChange } = await Promise.resolve().then(() => __importStar(require('../../client/renewal')));
|
|
165
|
-
const result = await handleSubscriptionChange(mockEvent, '/api/renew');
|
|
166
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
167
|
-
(0, vitest_1.expect)(result.error).toBe('Network error');
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,299 +0,0 @@
|
|
|
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 factory for client dependencies (needed after vi.resetModules)
|
|
38
|
-
function mockClientDependencies() {
|
|
39
|
-
vitest_1.vi.mock('../../client/deviceId', () => ({
|
|
40
|
-
getDeviceId: vitest_1.vi.fn(() => 'mock-device-id')
|
|
41
|
-
}));
|
|
42
|
-
vitest_1.vi.mock('../../client/indexedDb', () => ({
|
|
43
|
-
saveSubscriptionData: vitest_1.vi.fn(() => Promise.resolve()),
|
|
44
|
-
clearSubscriptionData: vitest_1.vi.fn(() => Promise.resolve())
|
|
45
|
-
}));
|
|
46
|
-
vitest_1.vi.mock('../../client/encoding', () => ({
|
|
47
|
-
urlBase64ToUint8Array: vitest_1.vi.fn(() => new Uint8Array([1, 2, 3]))
|
|
48
|
-
}));
|
|
49
|
-
}
|
|
50
|
-
// Initial mock setup (hoisted by Vitest)
|
|
51
|
-
mockClientDependencies();
|
|
52
|
-
// Setup browser mocks
|
|
53
|
-
const mockSubscription = {
|
|
54
|
-
endpoint: 'https://fcm.googleapis.com/test',
|
|
55
|
-
toJSON: () => ({
|
|
56
|
-
endpoint: 'https://fcm.googleapis.com/test',
|
|
57
|
-
keys: {
|
|
58
|
-
p256dh: 'test-p256dh',
|
|
59
|
-
auth: 'test-auth'
|
|
60
|
-
}
|
|
61
|
-
}),
|
|
62
|
-
unsubscribe: vitest_1.vi.fn(() => Promise.resolve(true))
|
|
63
|
-
};
|
|
64
|
-
const mockPushManager = {
|
|
65
|
-
subscribe: vitest_1.vi.fn(() => Promise.resolve(mockSubscription)),
|
|
66
|
-
getSubscription: vitest_1.vi.fn(() => Promise.resolve(mockSubscription))
|
|
67
|
-
};
|
|
68
|
-
const mockRegistration = {
|
|
69
|
-
pushManager: mockPushManager
|
|
70
|
-
};
|
|
71
|
-
// Helper to setup navigator with serviceWorker
|
|
72
|
-
function setupNavigatorWithServiceWorker() {
|
|
73
|
-
Object.defineProperty(global, 'navigator', {
|
|
74
|
-
value: {
|
|
75
|
-
serviceWorker: {
|
|
76
|
-
ready: Promise.resolve(mockRegistration)
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
writable: true,
|
|
80
|
-
configurable: true
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
// Helper to setup empty navigator (no serviceWorker)
|
|
84
|
-
function setupEmptyNavigator() {
|
|
85
|
-
Object.defineProperty(global, 'navigator', {
|
|
86
|
-
value: {},
|
|
87
|
-
writable: true,
|
|
88
|
-
configurable: true
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
(0, vitest_1.beforeEach)(() => {
|
|
92
|
-
vitest_1.vi.clearAllMocks();
|
|
93
|
-
// Mock Notification
|
|
94
|
-
global.Notification = {
|
|
95
|
-
permission: 'default',
|
|
96
|
-
requestPermission: vitest_1.vi.fn(() => Promise.resolve('granted'))
|
|
97
|
-
};
|
|
98
|
-
setupNavigatorWithServiceWorker();
|
|
99
|
-
// Mock window.PushManager
|
|
100
|
-
global.window = {
|
|
101
|
-
PushManager: {}
|
|
102
|
-
};
|
|
103
|
-
});
|
|
104
|
-
(0, vitest_1.describe)('Push subscription client', () => {
|
|
105
|
-
(0, vitest_1.describe)('getPermissionState', () => {
|
|
106
|
-
(0, vitest_1.it)('returns current notification permission', async () => {
|
|
107
|
-
global.Notification.permission = 'granted';
|
|
108
|
-
const { getPermissionState } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
109
|
-
(0, vitest_1.expect)(getPermissionState()).toBe('granted');
|
|
110
|
-
});
|
|
111
|
-
(0, vitest_1.it)('returns denied permission', async () => {
|
|
112
|
-
global.Notification.permission = 'denied';
|
|
113
|
-
const { getPermissionState } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
114
|
-
(0, vitest_1.expect)(getPermissionState()).toBe('denied');
|
|
115
|
-
});
|
|
116
|
-
(0, vitest_1.it)('returns default permission', async () => {
|
|
117
|
-
global.Notification.permission = 'default';
|
|
118
|
-
const { getPermissionState } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
119
|
-
(0, vitest_1.expect)(getPermissionState()).toBe('default');
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
(0, vitest_1.describe)('requestPermission', () => {
|
|
123
|
-
(0, vitest_1.it)('requests permission and returns result', async () => {
|
|
124
|
-
global.Notification.requestPermission.mockResolvedValue('granted');
|
|
125
|
-
const { requestPermission } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
126
|
-
const result = await requestPermission();
|
|
127
|
-
(0, vitest_1.expect)(result).toBe('granted');
|
|
128
|
-
(0, vitest_1.expect)(global.Notification.requestPermission).toHaveBeenCalled();
|
|
129
|
-
});
|
|
130
|
-
(0, vitest_1.it)('returns denied when user denies', async () => {
|
|
131
|
-
global.Notification.requestPermission.mockResolvedValue('denied');
|
|
132
|
-
const { requestPermission } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
133
|
-
const result = await requestPermission();
|
|
134
|
-
(0, vitest_1.expect)(result).toBe('denied');
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
(0, vitest_1.describe)('isPushSupported', () => {
|
|
138
|
-
(0, vitest_1.it)('returns true when serviceWorker and PushManager available', async () => {
|
|
139
|
-
const { isPushSupported } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
140
|
-
(0, vitest_1.expect)(isPushSupported()).toBe(true);
|
|
141
|
-
});
|
|
142
|
-
(0, vitest_1.it)('returns false when serviceWorker missing', async () => {
|
|
143
|
-
setupEmptyNavigator();
|
|
144
|
-
vitest_1.vi.resetModules();
|
|
145
|
-
const { isPushSupported } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
146
|
-
(0, vitest_1.expect)(isPushSupported()).toBe(false);
|
|
147
|
-
});
|
|
148
|
-
(0, vitest_1.it)('returns false when PushManager missing', async () => {
|
|
149
|
-
delete global.window.PushManager;
|
|
150
|
-
vitest_1.vi.resetModules();
|
|
151
|
-
const { isPushSupported } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
152
|
-
(0, vitest_1.expect)(isPushSupported()).toBe(false);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
(0, vitest_1.describe)('toPushSubscription', () => {
|
|
156
|
-
(0, vitest_1.it)('converts browser subscription to internal format', async () => {
|
|
157
|
-
const { toPushSubscription } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
158
|
-
const result = toPushSubscription(mockSubscription);
|
|
159
|
-
(0, vitest_1.expect)(result.endpoint).toBe('https://fcm.googleapis.com/test');
|
|
160
|
-
(0, vitest_1.expect)(result.keys.p256dh).toBe('test-p256dh');
|
|
161
|
-
(0, vitest_1.expect)(result.keys.auth).toBe('test-auth');
|
|
162
|
-
});
|
|
163
|
-
(0, vitest_1.it)('handles missing keys', async () => {
|
|
164
|
-
const { toPushSubscription } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
165
|
-
const subWithoutKeys = {
|
|
166
|
-
endpoint: 'https://example.com',
|
|
167
|
-
toJSON: () => ({ endpoint: 'https://example.com', keys: undefined })
|
|
168
|
-
};
|
|
169
|
-
const result = toPushSubscription(subWithoutKeys);
|
|
170
|
-
(0, vitest_1.expect)(result.keys.p256dh).toBe('');
|
|
171
|
-
(0, vitest_1.expect)(result.keys.auth).toBe('');
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
(0, vitest_1.describe)('subscribeToPush', () => {
|
|
175
|
-
(0, vitest_1.it)('returns error when push not supported', async () => {
|
|
176
|
-
setupEmptyNavigator();
|
|
177
|
-
vitest_1.vi.resetModules();
|
|
178
|
-
mockClientDependencies();
|
|
179
|
-
const { subscribeToPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
180
|
-
const result = await subscribeToPush('vapid-key');
|
|
181
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
182
|
-
(0, vitest_1.expect)(result.error).toBe('Push notifications not supported');
|
|
183
|
-
});
|
|
184
|
-
(0, vitest_1.it)('returns error when permission denied', async () => {
|
|
185
|
-
global.Notification.requestPermission.mockResolvedValue('denied');
|
|
186
|
-
const { subscribeToPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
187
|
-
const result = await subscribeToPush('vapid-key');
|
|
188
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
189
|
-
(0, vitest_1.expect)(result.error).toBe('Push permission denied');
|
|
190
|
-
});
|
|
191
|
-
(0, vitest_1.it)('returns subscription request on success', async () => {
|
|
192
|
-
global.Notification.requestPermission.mockResolvedValue('granted');
|
|
193
|
-
const { subscribeToPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
194
|
-
const result = await subscribeToPush('vapid-key', '/app');
|
|
195
|
-
(0, vitest_1.expect)(result.ok).toBe(true);
|
|
196
|
-
(0, vitest_1.expect)(result.data?.deviceId).toBe('mock-device-id');
|
|
197
|
-
(0, vitest_1.expect)(result.data?.basePath).toBe('/app');
|
|
198
|
-
(0, vitest_1.expect)(result.data?.subscription.endpoint).toBe('https://fcm.googleapis.com/test');
|
|
199
|
-
});
|
|
200
|
-
(0, vitest_1.it)('saves subscription data to IndexedDB', async () => {
|
|
201
|
-
global.Notification.requestPermission.mockResolvedValue('granted');
|
|
202
|
-
const { saveSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
|
|
203
|
-
const { subscribeToPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
204
|
-
await subscribeToPush('vapid-key', '/myapp');
|
|
205
|
-
(0, vitest_1.expect)(saveSubscriptionData).toHaveBeenCalledWith({
|
|
206
|
-
publicKey: 'vapid-key',
|
|
207
|
-
deviceId: 'mock-device-id',
|
|
208
|
-
basePath: '/myapp'
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
(0, vitest_1.it)('uses default basePath of "/"', async () => {
|
|
212
|
-
global.Notification.requestPermission.mockResolvedValue('granted');
|
|
213
|
-
const { subscribeToPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
214
|
-
const result = await subscribeToPush('vapid-key');
|
|
215
|
-
(0, vitest_1.expect)(result.data?.basePath).toBe('/');
|
|
216
|
-
});
|
|
217
|
-
(0, vitest_1.it)('handles subscription error', async () => {
|
|
218
|
-
global.Notification.requestPermission.mockResolvedValue('granted');
|
|
219
|
-
mockPushManager.subscribe.mockRejectedValueOnce(new Error('Network error'));
|
|
220
|
-
const { subscribeToPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
221
|
-
const result = await subscribeToPush('vapid-key');
|
|
222
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
223
|
-
(0, vitest_1.expect)(result.error).toBe('Network error');
|
|
224
|
-
});
|
|
225
|
-
(0, vitest_1.it)('handles non-Error throw', async () => {
|
|
226
|
-
global.Notification.requestPermission.mockResolvedValue('granted');
|
|
227
|
-
mockPushManager.subscribe.mockRejectedValueOnce('string error');
|
|
228
|
-
const { subscribeToPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
229
|
-
const result = await subscribeToPush('vapid-key');
|
|
230
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
231
|
-
(0, vitest_1.expect)(result.error).toBe('Subscription failed');
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
(0, vitest_1.describe)('unsubscribeFromPush', () => {
|
|
235
|
-
(0, vitest_1.it)('unsubscribes successfully', async () => {
|
|
236
|
-
const { unsubscribeFromPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
237
|
-
const result = await unsubscribeFromPush();
|
|
238
|
-
(0, vitest_1.expect)(result.ok).toBe(true);
|
|
239
|
-
(0, vitest_1.expect)(mockSubscription.unsubscribe).toHaveBeenCalled();
|
|
240
|
-
});
|
|
241
|
-
(0, vitest_1.it)('clears subscription data from IndexedDB', async () => {
|
|
242
|
-
const { clearSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
|
|
243
|
-
const { unsubscribeFromPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
244
|
-
await unsubscribeFromPush();
|
|
245
|
-
(0, vitest_1.expect)(clearSubscriptionData).toHaveBeenCalled();
|
|
246
|
-
});
|
|
247
|
-
(0, vitest_1.it)('succeeds when no subscription exists', async () => {
|
|
248
|
-
mockPushManager.getSubscription.mockResolvedValueOnce(null);
|
|
249
|
-
const { clearSubscriptionData } = await Promise.resolve().then(() => __importStar(require('../../client/indexedDb')));
|
|
250
|
-
const { unsubscribeFromPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
251
|
-
const result = await unsubscribeFromPush();
|
|
252
|
-
(0, vitest_1.expect)(result.ok).toBe(true);
|
|
253
|
-
(0, vitest_1.expect)(clearSubscriptionData).toHaveBeenCalled();
|
|
254
|
-
});
|
|
255
|
-
(0, vitest_1.it)('handles unsubscribe error', async () => {
|
|
256
|
-
mockPushManager.getSubscription.mockRejectedValueOnce(new Error('Failed'));
|
|
257
|
-
const { unsubscribeFromPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
258
|
-
const result = await unsubscribeFromPush();
|
|
259
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
260
|
-
(0, vitest_1.expect)(result.error).toBe('Failed');
|
|
261
|
-
});
|
|
262
|
-
(0, vitest_1.it)('handles non-Error throw', async () => {
|
|
263
|
-
mockPushManager.getSubscription.mockRejectedValueOnce('string error');
|
|
264
|
-
const { unsubscribeFromPush } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
265
|
-
const result = await unsubscribeFromPush();
|
|
266
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
267
|
-
(0, vitest_1.expect)(result.error).toBe('Unsubscribe failed');
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
(0, vitest_1.describe)('getCurrentSubscription', () => {
|
|
271
|
-
(0, vitest_1.it)('returns current subscription', async () => {
|
|
272
|
-
const { getCurrentSubscription } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
273
|
-
const result = await getCurrentSubscription();
|
|
274
|
-
(0, vitest_1.expect)(result.ok).toBe(true);
|
|
275
|
-
(0, vitest_1.expect)(result.data?.endpoint).toBe('https://fcm.googleapis.com/test');
|
|
276
|
-
});
|
|
277
|
-
(0, vitest_1.it)('returns error when no subscription', async () => {
|
|
278
|
-
mockPushManager.getSubscription.mockResolvedValueOnce(null);
|
|
279
|
-
const { getCurrentSubscription } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
280
|
-
const result = await getCurrentSubscription();
|
|
281
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
282
|
-
(0, vitest_1.expect)(result.error).toBe('No active subscription');
|
|
283
|
-
});
|
|
284
|
-
(0, vitest_1.it)('handles error', async () => {
|
|
285
|
-
mockPushManager.getSubscription.mockRejectedValueOnce(new Error('Service worker error'));
|
|
286
|
-
const { getCurrentSubscription } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
287
|
-
const result = await getCurrentSubscription();
|
|
288
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
289
|
-
(0, vitest_1.expect)(result.error).toBe('Service worker error');
|
|
290
|
-
});
|
|
291
|
-
(0, vitest_1.it)('handles non-Error throw', async () => {
|
|
292
|
-
mockPushManager.getSubscription.mockRejectedValueOnce('string error');
|
|
293
|
-
const { getCurrentSubscription } = await Promise.resolve().then(() => __importStar(require('../../client/subscribe')));
|
|
294
|
-
const result = await getCurrentSubscription();
|
|
295
|
-
(0, vitest_1.expect)(result.ok).toBe(false);
|
|
296
|
-
(0, vitest_1.expect)(result.error).toBe('Failed to get subscription');
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
});
|