diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 8b09afa..88fbf77 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -27,6 +27,7 @@ export default defineConfig({ ['meta', { httpEquiv: 'Origin-Trial', content: ORIGIN_TRIAL_WEBMCP }], ['script', { src: '/neuron-js/webmcp/webmcp.js' }], ['script', { src: '/neuron-js/webmcp/neuron-webmcp.js', defer: 'true' }], + ['script', { async: 'true', src: 'https://news.google.com/swg/js/v1/publisher.js' }], ], ignoreDeadLinks: true, markdown: { diff --git a/docs/.vitepress/theme/PreferredSourceFooter.vue b/docs/.vitepress/theme/PreferredSourceFooter.vue new file mode 100644 index 0000000..a74c80f --- /dev/null +++ b/docs/.vitepress/theme/PreferredSourceFooter.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 1142adf..c292208 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,7 +1,9 @@ import DefaultTheme from "vitepress/theme"; import { inBrowser } from "vitepress"; import type { EnhanceAppContext } from "vitepress"; +import { h } from "vue"; import mermaid from "mermaid"; +import PreferredSourceFooter from "./PreferredSourceFooter.vue"; import "./custom.css"; const isDarkMode = () => document.documentElement.classList.contains("dark"); @@ -56,6 +58,11 @@ const scheduleRender = () => { export default { extends: DefaultTheme, + Layout() { + return h(DefaultTheme.Layout, null, { + "layout-bottom": () => h(PreferredSourceFooter), + }); + }, enhanceApp({ router }: EnhanceAppContext) { if (!inBrowser) { return; @@ -68,6 +75,9 @@ export default { router.onAfterRouteChanged = async (to) => { await afterRouteChange?.(to); scheduleRender(); + // The Google preferred-source button lives in the layout-bottom slot, + // which VitePress keeps mounted across SPA route changes; publisher.js + // initializes it once on load — verified in the browser E2E. }; }, setup() { diff --git a/tests/contracts/preferred-source-footer.test.ts b/tests/contracts/preferred-source-footer.test.ts new file mode 100644 index 0000000..653cc3c --- /dev/null +++ b/tests/contracts/preferred-source-footer.test.ts @@ -0,0 +1,33 @@ +// NJS-SEO-6 follow-up: contract test for the Google "Add to preferred sources" +// footer button on the documentation site. +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +const read = (path: string) => readFileSync(path, "utf8"); + +describe("Google preferred-source footer button", () => { + it("loads the official publisher.js in the VitePress head", () => { + expect(read("docs/.vitepress/config.ts")).toContain( + "https://news.google.com/swg/js/v1/publisher.js", + ); + }); + + it("renders the standard button container in the layout-bottom footer", () => { + const component = read("docs/.vitepress/theme/PreferredSourceFooter.vue"); + expect(component).toContain("google-add-preferred-source-btn"); + expect(component).toContain("preferred-source-footer"); + // theme aware: light/dark follows the site theme + expect(component).toContain("isDark"); + }); + + it("mounts the footer via the layout-bottom slot", () => { + const theme = read("docs/.vitepress/theme/index.ts"); + expect(theme).toContain("PreferredSourceFooter"); + expect(theme).toContain("layout-bottom"); + }); + + it("states the eligible source honestly (domain, not subdirectory)", () => { + const component = read("docs/.vitepress/theme/PreferredSourceFooter.vue"); + expect(component).toContain("sebasoft.github.io"); + }); +});