openvibers/openvibe.games Commits

Read the Games pages' descriptions, and give the leaderboard its canonical and card (#249)

OP OpenVibers committed 12 days ago · fb0304c742 · 5 files, +50 −12

Changes

ddiff --git a/apps/client/leaderboard.html b/apps/client/leaderboard.html
iindex 3c9f941..97e1e44 100644
---- a/apps/client/leaderboard.html
++++ b/apps/client/leaderboard.html
@@@ -4,10 +4,18 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Leaderboard · OpenVibe.Games</title>
-- <meta
-- name="description"
-- content="OpenVibe.Games leaderboards: Salvage Rush best rounds and Scraplandia haul points, all time and this week. Scores come only from what the server played out."
-- />
++ <meta name="description" content="OpenVibe.Games leaderboards: Salvage Rush best rounds and Scraplandia haul points, all time and this week. Scores come only from what the server played out." />
++ <link rel="canonical" href="https://openvibe.games/leaderboard.html" />
++ <meta property="og:type" content="website" />
++ <meta property="og:site_name" content="OpenVibe.Games" />
++ <meta property="og:title" content="Leaderboard · OpenVibe.Games" />
++ <meta property="og:description" content="OpenVibe.Games leaderboards: Salvage Rush best rounds and Scraplandia haul points, all time and this week. Scores come only from what the server played out." />
++ <meta property="og:url" content="https://openvibe.games/leaderboard.html" />
++ <meta property="og:image" content="https://openvibe.games/og-image.png" />
++ <meta property="og:image:width" content="1200" />
++ <meta property="og:image:height" content="630" />
++ <meta name="twitter:card" content="summary_large_image" />
++ <meta name="twitter:image" content="https://openvibe.games/og-image.png" />
<!-- The reader's theme, before the first paint: the page's own light rules follow it. -->
<script src="/shared/theme-loader.js"></script>
<style>
ddiff --git a/apps/client/play.html b/apps/client/play.html
iindex 5b4032a..2560b53 100644
---- a/apps/client/play.html
++++ b/apps/client/play.html
@@@ -5,10 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Scraplandia — OpenVibe.Games</title>
-- <meta
-- name="description"
-- content="Play Scraplandia: scrappy multiplayer survival-building in and around Scrap City, in your browser, no download. Part of the OpenVibe network."
-- />
++ <meta name="description" content="Play Scraplandia: scrappy multiplayer survival-building in and around Scrap City, in your browser, no download. Part of the OpenVibe network." />
<!-- The OpenVibe Frame: the user's theme before first paint, then navbar + footer. -->
<script src="/shared/theme-loader.js"></script>
<script src="/shared/navbar.js" defer></script>
ddiff --git a/apps/client/src/pages/pageMeta.test.ts b/apps/client/src/pages/pageMeta.test.ts
nnew file mode 100644
iindex 0000000..9d13079
---- /dev/null
++++ b/apps/client/src/pages/pageMeta.test.ts
@@@ -0,0 +1,33 @@
++/**
++ * The pages outside the game client's router are still read by search engines and share cards:
++ * each names a one-line meta description of 120–160 characters, and the leaderboard (linked from
++ * the game and the portal) names its own canonical URL and the site's og:image, the card the home
++ * uses. The description is one line so a plain line-based crawler sees it too.
++ */
++import { readFileSync } from 'node:fs'
++import { fileURLToPath } from 'node:url'
++import { describe, expect, it } from 'vitest'
++
++const client = (path: string) => fileURLToPath(new URL(`../../${path}`, import.meta.url))
++const html = (page: string) => readFileSync(client(page), 'utf8')
++const description = (page: string) =>
++ /<meta name="description" content="([^"]*)" \/>/.exec(html(page))?.[1] ?? ''
++
++describe('page metadata', () => {
++ it.each(['play.html', 'worlds.html', 'leaderboard.html'])(
++ '%s has a one-line description of 120 to 160 characters',
++ (page) => {
++ const d = description(page)
++ expect(d).not.toBe('')
++ expect(d.length).toBeGreaterThanOrEqual(120)
++ expect(d.length).toBeLessThanOrEqual(160)
++ expect(html(page)).toContain(`<meta name="description" content="${d}" />`)
++ },
++ )
++
++ it('leaderboard.html names its canonical URL and the site card', () => {
++ const page = html('leaderboard.html')
++ expect(page).toContain('<link rel="canonical" href="https://openvibe.games/leaderboard.html" />')
++ expect(page).toContain('<meta property="og:image" content="https://openvibe.games/og-image.png" />')
++ })
++})
ddiff --git a/apps/client/worlds.html b/apps/client/worlds.html
iindex c5997d1..4f0ebab 100644
---- a/apps/client/worlds.html
++++ b/apps/client/worlds.html
@@@ -4,10 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Creator worlds · OpenVibe.Games</title>
-- <meta
-- name="description"
-- content="Build your own 3D world in the OpenVibe.Games map editor and publish it for anyone to join in the browser. Browse the worlds players made."
-- />
++ <meta name="description" content="Build your own 3D world in the OpenVibe.Games map editor and publish it for anyone to join in the browser. Browse the worlds players made." />
<link rel="canonical" href="https://openvibe.games/worlds" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="OpenVibe.Games" />
ddiff --git a/test/apps__client__src__pages__pageMeta.test.js b/test/apps__client__src__pages__pageMeta.test.js
nnew file mode 100644
iindex 0000000..53fe684
---- /dev/null
++++ b/test/apps__client__src__pages__pageMeta.test.js
@@@ -0,0 +1,3 @@
++// file: 'apps/client/src/pages/pageMeta.test.ts'
++// covers: 'apps/client/src/pages'
++// Generated by node test/run.js --index; do not edit.