openvibers/openvibe.games Commits
The server's 404 page names the client's icon (<link rel="icon" type="image/svg+xml" href="/favicon.svg">, as the client pages do since 0f29769). Without it the browser asked for /favicon.ico, a second 404 and a console error on every not-found page (browser check on /__ovcheck-404, Host scripts/browser-check.js, WS-Q task 3). Test: apps/server/src/net/httpServer.test.ts
Changes
@@@ -1,8 +1,9 @@--import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'++import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'import { request, type IncomingHttpHeaders, type OutgoingHttpHeaders, type Server } from 'node:http'import type { AddressInfo } from 'node:net'import { tmpdir } from 'node:os'import { join } from 'node:path'++import { fileURLToPath } from 'node:url'import { createConsoleLogger } from '@openvibe/shared'import { afterAll, beforeAll, describe, expect, it } from 'vitest'import { ServerMetrics } from '../observability/metrics.js'@@@ -203,6 +204,8 @@ describe('unknown paths answer 404', () => {expect(r.body).toContain('<a href="/play">Play Scraplandia</a>')expect(r.body).toContain('<a href="/editor">Map editor</a>')expect(r.body).not.toMatch(/<script\b/)++ // Names the client's icon, so the browser does not ask /favicon.ico (a second 404).++ expect(r.body).toContain('<link rel="icon" type="image/svg+xml" href="/favicon.svg">')},)@@@ -300,4 +303,11 @@ describe('404 page', () => {expect(page).toContain(`/${'a'.repeat(199)}…</code>`)expect(page).not.toContain('a'.repeat(201))})++++ it('names the icon the client pages use, and that file ships with the client', () => {++ const page = notFoundPage('/nope', [])++ expect(page).toContain('<link rel="icon" type="image/svg+xml" href="/favicon.svg">')++ const icon = fileURLToPath(new URL('../../../client/public/favicon.svg', import.meta.url))++ expect(existsSync(icon)).toBe(true)++ })})@@@ -7,7 +7,10 @@ import { extname } from 'node:path'* portal and monitors read as up).** Page requests get a small self-contained HTML page that names the path and-- * links to the host's real entry points. API paths and clients asking for++ * links to the host's real entry points. It names the client's icon++ * (/favicon.svg, as the client pages do): without an icon link the browser++ * asks for /favicon.ico, which is itself a 404 and a console error (browser++ * check, OpenVibe.Host scripts/browser-check.js). API paths and clients asking for* JSON get `{"error":"not_found"}` (the shape /auth/* already answers). A* missing asset (any extension but .html) gets plain text.*/@@@ -55,6 +58,7 @@ export function notFoundPage(path: string, links: readonly NotFoundLink[]): stri<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="robots" content="noindex">++<link rel="icon" type="image/svg+xml" href="/favicon.svg"><title>Page not found · OpenVibe.Games</title><style>:root { color-scheme: dark; }
@@ -1,8 +1,9 @@
@@ -1,8 +1,9 @@
-import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
+import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { request, type IncomingHttpHeaders, type OutgoingHttpHeaders, type Server } from 'node:http'
import { request, type IncomingHttpHeaders, type OutgoingHttpHeaders, type Server } from 'node:http'
import type { AddressInfo } from 'node:net'
import type { AddressInfo } from 'node:net'
import { tmpdir } from 'node:os'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { join } from 'node:path'
+import { fileURLToPath } from 'node:url'
import { createConsoleLogger } from '@openvibe/shared'
import { createConsoleLogger } from '@openvibe/shared'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { ServerMetrics } from '../observability/metrics.js'
import { ServerMetrics } from '../observability/metrics.js'
@@ -203,6 +204,8 @@ describe('unknown paths answer 404', () => {
@@ -203,6 +204,8 @@ describe('unknown paths answer 404', () => {
expect(r.body).toContain('<a href="/play">Play Scraplandia</a>')
expect(r.body).toContain('<a href="/play">Play Scraplandia</a>')
expect(r.body).toContain('<a href="/editor">Map editor</a>')
expect(r.body).toContain('<a href="/editor">Map editor</a>')
expect(r.body).not.toMatch(/<script\b/)
expect(r.body).not.toMatch(/<script\b/)
+ // Names the client's icon, so the browser does not ask /favicon.ico (a second 404).
+ expect(r.body).toContain('<link rel="icon" type="image/svg+xml" href="/favicon.svg">')
},
},
)
)
@@ -300,4 +303,11 @@ describe('404 page', () => {
@@ -300,4 +303,11 @@ describe('404 page', () => {
expect(page).toContain(`/${'a'.repeat(199)}…</code>`)
expect(page).toContain(`/${'a'.repeat(199)}…</code>`)
expect(page).not.toContain('a'.repeat(201))
expect(page).not.toContain('a'.repeat(201))
})
})
+
+ it('names the icon the client pages use, and that file ships with the client', () => {
+ const page = notFoundPage('/nope', [])
+ expect(page).toContain('<link rel="icon" type="image/svg+xml" href="/favicon.svg">')
+ const icon = fileURLToPath(new URL('../../../client/public/favicon.svg', import.meta.url))
+ expect(existsSync(icon)).toBe(true)
+ })
})
})
@@ -7,7 +7,10 @@ import { extname } from 'node:path'
@@ -7,7 +7,10 @@ import { extname } from 'node:path'
* portal and monitors read as up).
* portal and monitors read as up).
*
*
* Page requests get a small self-contained HTML page that names the path and
* Page requests get a small self-contained HTML page that names the path and
- * links to the host's real entry points. API paths and clients asking for
+ * links to the host's real entry points. It names the client's icon
+ * (/favicon.svg, as the client pages do): without an icon link the browser
+ * asks for /favicon.ico, which is itself a 404 and a console error (browser
+ * check, OpenVibe.Host scripts/browser-check.js). API paths and clients asking for
* JSON get `{"error":"not_found"}` (the shape /auth/* already answers). A
* JSON get `{"error":"not_found"}` (the shape /auth/* already answers). A
* missing asset (any extension but .html) gets plain text.
* missing asset (any extension but .html) gets plain text.
*/
*/
@@ -55,6 +58,7 @@ export function notFoundPage(path: string, links: readonly NotFoundLink[]): stri
@@ -55,6 +58,7 @@ export function notFoundPage(path: string, links: readonly NotFoundLink[]): stri
<meta charset="utf-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
<meta name="robots" content="noindex">
+<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<title>Page not found · OpenVibe.Games</title>
<title>Page not found · OpenVibe.Games</title>
<style>
<style>
:root { color-scheme: dark; }
:root { color-scheme: dark; }