mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 16:14:07 +00:00
A notification for one message gets buttons where the browser has room for them, and a click on its body works as before. The service worker has no login, so an open tab takes the action first, through the store actions. With no tab open a new same-origin route, /api/push/action, does Mark read and Archive with the context cookie. When the cookie is gone, the token has expired or the login can't be told apart, the message opens in the app instead. Reply opens the composer through a `?reply=1` link, with any text typed on the notification. - `public/sw.js`: buttons, asking open tabs, the route, `sync` handling - `app/api/push/action`: same-origin, checks the login and the account, honours the admin's Archive gate, files by the mirrored archive mode - `lib/pwa-notify`: the page's side, the mirror for the worker, and `requestOutboxSync`, which the offline queue calls - Background Sync, thin version: a `sync` event tells open tabs to flush Two push-preview-route assertions change: they match the whole answer for a named message, which now also carries `login`, what the action route checks so a stale notification can't act on whoever holds the cookie slot now.
36 lines
1.4 KiB
TypeScript
36 lines
1.4 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { queuePatch, queueSend } from '../queue';
|
|
|
|
// Whatever goes into the queue asks for a Background Sync, once it's stored.
|
|
|
|
const requestOutboxSync = vi.fn();
|
|
const stored: unknown[] = [];
|
|
vi.mock('@/lib/pwa-notify/outbox-sync', () => ({ requestOutboxSync: () => requestOutboxSync(stored.length) }));
|
|
vi.mock('../db', () => ({
|
|
idbAdd: vi.fn(async (_store: string, record: unknown) => { stored.push(record); return stored.length; }),
|
|
idbDeleteAccount: vi.fn(), idbGet: vi.fn(), idbGetAll: vi.fn(), idbGetAllByAccount: vi.fn(), idbUpdate: vi.fn(), idbWrite: vi.fn(),
|
|
}));
|
|
vi.mock('../crypto', () => ({
|
|
sealJson: vi.fn(async () => ({ iv: 'i', data: 'd' })),
|
|
openJson: vi.fn(), openBytes: vi.fn(), sealBytes: vi.fn(),
|
|
}));
|
|
|
|
beforeEach(() => {
|
|
stored.length = 0;
|
|
requestOutboxSync.mockClear();
|
|
});
|
|
|
|
describe('the offline queue', () => {
|
|
it('asks for a sync after storing a change', async () => {
|
|
await queuePatch('acc', 'jmap', {} as never);
|
|
expect(requestOutboxSync).toHaveBeenCalledTimes(1);
|
|
expect(requestOutboxSync).toHaveBeenCalledWith(1);
|
|
});
|
|
|
|
it('asks for a sync after storing a message, and still returns its id', async () => {
|
|
const id = await queueSend('acc', {} as never, {} as never);
|
|
expect(id).toBe(1);
|
|
expect(requestOutboxSync).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|