Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion e2e/tests/production-build.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { test, expect } from '@playwright/test';
import { existsSync, readdirSync } from 'fs';
import { existsSync, readdirSync, readFileSync } from 'fs';

// These tests check the LOCAL web/dist build and hit localhost:PREVIEW_PORT directly.
// They must not run when PW_BASE_URL targets a remote host (staging/prod gate).
Expand Down Expand Up @@ -116,6 +116,43 @@ test('built dist serves the legacy privacy-policy and EULA legal pages', async (
}
});

// Cutover regression guard: the rewrite shipped with NO favicon link (empty tab
// icon, /favicon.ico 404) and no web manifest. This asserts the canonical favicon
// + the PWA manifest and every icon it declares are served 200 from the built dist.
test('built dist serves the favicon and a valid PWA manifest with resolvable icons', async ({
page,
}) => {
// index.html must link the favicon + manifest (browsers fetch /favicon.ico
// otherwise → empty tab icon, the original regression).
const html = readFileSync(resolve(DIST, 'index.html'), 'utf8');
expect(html, 'built index.html lost its favicon <link rel="icon">').toMatch(
/rel="icon"/,
);
expect(html, 'built index.html lost its <link rel="manifest">').toMatch(
/rel="manifest"/,
);

// The manifest is served, valid, and every icon it declares resolves 200.
const manifestResp = await page.goto(`${PREVIEW_URL}manifest.webmanifest`);
expect(manifestResp?.status(), 'GET /manifest.webmanifest must be 200').toBe(
200,
);
const manifest = JSON.parse(await manifestResp.text());
expect(manifest.icons.length, 'manifest declares no icons').toBeGreaterThan(
0,
);
// A real installable PWA needs 192 + 512 PNG icons.
const pngSizes = manifest.icons
.filter((i) => i.type === 'image/png')
.map((i) => i.sizes);
expect(pngSizes).toEqual(expect.arrayContaining(['192x192', '512x512']));

for (const icon of manifest.icons) {
const r = await page.goto(`${PREVIEW_URL}${icon.src.replace(/^\//, '')}`);
expect(r?.status(), `manifest icon ${icon.src} must resolve 200`).toBe(200);
}
});

test('built app renders the diagram with no dev-only /@fs/ URL and a 200 core asset', async ({
page,
}) => {
Expand Down
9 changes: 9 additions & 0 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Canonical ZenUML favicon (served from web/public). Without an icon link the
browser requests a non-existent /favicon.ico and shows an empty tab icon. -->
<link rel="icon" type="image/svg+xml" href="/zenuml-logo.svg" />
<link rel="icon" type="image/png" href="/favicon-128x128.png" />
<link rel="apple-touch-icon" href="/icon-192.png" />
<!-- PWA: web app manifest + theme color (icons rendered from BrandLogo artwork).
The legacy app shipped only an extension manifest; this is a real web manifest. -->
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#2e94d4" />
<title>ZenUML — Sequence Diagrams</title>
<!-- Design system "Drafting Table": Hanken Grotesk (UI), IBM Plex Mono
(code-adjacent metadata), Instrument Serif (editorial accents). -->
Expand Down
Binary file added web/public/favicon-128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/public/icon-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/public/icon-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/public/icon-maskable-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions web/public/manifest.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "ZenUML Sequence",
"short_name": "ZenUML",
"description": "Real-time & offline tool for generating sequence diagrams",
"start_url": "/",
"scope": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#2e94d4",
"icons": [
{ "src": "/favicon-128x128.png", "sizes": "128x128", "type": "image/png" },
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
{ "src": "/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" },
{ "src": "/zenuml-logo.svg", "sizes": "any", "type": "image/svg+xml" }
]
}
31 changes: 31 additions & 0 deletions web/public/zenuml-logo-maskable.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading