- Remove dead precache manifest from SW (no fetch handler to consume it) - Set empty globPatterns in vite-plugin-pwa config - Fix bottom chin bar padding: use scaled safe-area token instead of raw inset - Add 'New Session' PWA shortcut (client + server manifest) - Add PWA meta tags to mobile.html (manifest, Apple icons, theme-color) - Fix static site.webmanifest: add id/scope, correct description - Remove duplicate apple-mobile-web-app-title from index.html - Move SW registration to module scope (earlier, no window.load delay) - Update manifest route tests for new shortcut order
141 lines
4.3 KiB
JavaScript
141 lines
4.3 KiB
JavaScript
import { describe, expect, it } from 'vitest';
|
|
import { registerPwaManifestRoute } from './pwa-manifest-routes.js';
|
|
|
|
const createResponse = () => ({
|
|
headers: new Map(),
|
|
contentType: '',
|
|
body: '',
|
|
setHeader(name, value) {
|
|
this.headers.set(name, value);
|
|
return this;
|
|
},
|
|
type(value) {
|
|
this.contentType = value;
|
|
return this;
|
|
},
|
|
send(value) {
|
|
this.body = value;
|
|
return this;
|
|
},
|
|
});
|
|
|
|
describe('PWA manifest route', () => {
|
|
it('does not fall back to unrelated global session shortcuts for scoped manifests', async () => {
|
|
const routes = new Map();
|
|
const app = {
|
|
get(route, handler) {
|
|
routes.set(route, handler);
|
|
},
|
|
};
|
|
const originalFetch = globalThis.fetch;
|
|
const fetchCalls = [];
|
|
globalThis.fetch = async (url) => {
|
|
fetchCalls.push(String(url));
|
|
const sessions = String(url).includes('?directory=')
|
|
? []
|
|
: [
|
|
{
|
|
id: 'other-session',
|
|
title: 'Other project',
|
|
directory: '/workspace/other',
|
|
time: { updated: 2 },
|
|
},
|
|
];
|
|
return {
|
|
ok: true,
|
|
json: async () => sessions,
|
|
};
|
|
};
|
|
|
|
try {
|
|
registerPwaManifestRoute(app, {
|
|
process: { platform: 'darwin' },
|
|
resolveProjectDirectory: async () => ({ directory: '/workspace/app' }),
|
|
buildOpenCodeUrl: (route) => route,
|
|
getOpenCodeAuthHeaders: () => ({}),
|
|
readSettingsFromDiskMigrated: async () => ({}),
|
|
normalizePwaAppName: (value, fallback) => typeof value === 'string' && value.trim() ? value.trim() : fallback,
|
|
normalizePwaOrientation: (value, fallback) => typeof value === 'string' && value.trim() ? value.trim() : fallback,
|
|
});
|
|
|
|
const handler = routes.get('/manifest.webmanifest');
|
|
const res = createResponse();
|
|
await handler({ query: {} }, res);
|
|
|
|
const manifest = JSON.parse(res.body);
|
|
expect(fetchCalls).toHaveLength(2);
|
|
expect(manifest.shortcuts).toEqual([
|
|
{
|
|
name: 'New Session',
|
|
short_name: 'New',
|
|
description: 'Start a new coding session',
|
|
url: '/',
|
|
icons: [{ src: '/pwa-192.png', sizes: '192x192', type: 'image/png' }],
|
|
},
|
|
{
|
|
name: 'Appearance Settings',
|
|
short_name: 'Settings',
|
|
description: 'Open appearance settings',
|
|
url: '/?settings=appearance',
|
|
icons: [{ src: '/pwa-192.png', sizes: '192x192', type: 'image/png' }],
|
|
},
|
|
]);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
it('includes child session shortcuts for root-scoped manifests', async () => {
|
|
const routes = new Map();
|
|
const app = {
|
|
get(route, handler) {
|
|
routes.set(route, handler);
|
|
},
|
|
};
|
|
const originalFetch = globalThis.fetch;
|
|
const fetchCalls = [];
|
|
globalThis.fetch = async (url) => {
|
|
fetchCalls.push(String(url));
|
|
return {
|
|
ok: true,
|
|
json: async () => [
|
|
{
|
|
id: 'root-child',
|
|
title: 'Root child',
|
|
directory: '/workspace/app',
|
|
time: { updated: 2 },
|
|
},
|
|
],
|
|
};
|
|
};
|
|
|
|
try {
|
|
registerPwaManifestRoute(app, {
|
|
process: { platform: 'darwin' },
|
|
resolveProjectDirectory: async () => ({ directory: '/' }),
|
|
buildOpenCodeUrl: (route) => route,
|
|
getOpenCodeAuthHeaders: () => ({}),
|
|
readSettingsFromDiskMigrated: async () => ({}),
|
|
normalizePwaAppName: (value, fallback) => typeof value === 'string' && value.trim() ? value.trim() : fallback,
|
|
normalizePwaOrientation: (value, fallback) => typeof value === 'string' && value.trim() ? value.trim() : fallback,
|
|
});
|
|
|
|
const handler = routes.get('/manifest.webmanifest');
|
|
const res = createResponse();
|
|
await handler({ query: {} }, res);
|
|
|
|
const manifest = JSON.parse(res.body);
|
|
expect(fetchCalls).toEqual(['/session?directory=%2F']);
|
|
expect(manifest.shortcuts).toContainEqual({
|
|
name: 'Root child',
|
|
short_name: 'Root child',
|
|
description: 'Open recent session',
|
|
url: '/?session=root-child',
|
|
icons: [{ src: '/pwa-192.png', sizes: '192x192', type: 'image/png' }],
|
|
});
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
});
|