diff --git a/src/frontend/eslint.config.mjs b/src/frontend/eslint.config.mjs index e85ffc51a..1034ca018 100644 --- a/src/frontend/eslint.config.mjs +++ b/src/frontend/eslint.config.mjs @@ -50,9 +50,16 @@ export default [ ...tseslint.configs.disableTypeChecked, }, { - files: ['tests/**/*.ts'], + files: ['tests/e2e/**/*.ts'], ...tseslint.configs.disableTypeChecked, }, + { + files: ['tests/unit/**/*.ts', 'tests/typecheck/**/*.ts'], + rules: { + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + }, + }, // Disable all formatting rules. prettierConfig, diff --git a/src/frontend/scripts/fetch-with-proxy.ts b/src/frontend/scripts/fetch-with-proxy.ts index ad718a1ff..16b1394e8 100644 --- a/src/frontend/scripts/fetch-with-proxy.ts +++ b/src/frontend/scripts/fetch-with-proxy.ts @@ -28,4 +28,4 @@ export function fetchWithProxy(url: string | URL, options: RequestInit = {}): Pr const parsedUrl = url instanceof URL ? url : new URL(url); return fetch(url, { ...options, agent: selectAgent(parsedUrl) }); -} \ No newline at end of file +} diff --git a/src/frontend/src/components/FooterLinks.astro b/src/frontend/src/components/FooterLinks.astro index 0c6879d75..279596495 100644 --- a/src/frontend/src/components/FooterLinks.astro +++ b/src/frontend/src/components/FooterLinks.astro @@ -5,7 +5,9 @@ import config from 'virtual:starlight/user-config'; const links = config.social || []; const gitHubLink = links.find((link) => link.label === 'GitHub')?.href ?? null; const discordLink = links.find((link) => link.label === 'Discord')?.href ?? null; -const is404 = Astro.url.pathname === '/404' || Astro.url.pathname === '/404/'; +const is404 = + Astro.locals.starlightRoute?.entry?.slug === '404' || + /^\/(?:[^/]+\/)?404\/?$/.test(Astro.url.pathname); --- { diff --git a/src/frontend/src/components/OsAwareTabs.astro b/src/frontend/src/components/OsAwareTabs.astro index 1b509c020..966d904f7 100644 --- a/src/frontend/src/components/OsAwareTabs.astro +++ b/src/frontend/src/components/OsAwareTabs.astro @@ -25,7 +25,7 @@ const { syncKey = 'terminal' } = Astro.props; function activateTabByLabel(starlightTabs, label) { const tabs = Array.from(starlightTabs.querySelectorAll('[role="tab"]')); const panels = Array.from(starlightTabs.querySelectorAll(':scope > [role="tabpanel"]')); - const tabIndex = tabs.findIndex((tab) => tab instanceof HTMLAnchorElement && tab.textContent?.trim() === label); + const tabIndex = tabs.findIndex((tab) => tab.textContent?.trim() === label); if (tabIndex < 0) return; diff --git a/src/frontend/tests/unit/custom-components.vitest.test.ts b/src/frontend/tests/unit/custom-components.vitest.test.ts index b00b889c0..082abf2d3 100644 --- a/src/frontend/tests/unit/custom-components.vitest.test.ts +++ b/src/frontend/tests/unit/custom-components.vitest.test.ts @@ -10,6 +10,7 @@ import CTABanner from '@components/CTABanner.astro'; import CapabilityGrid from '@components/CapabilityGrid.astro'; import CodespacesButton from '@components/CodespacesButton.astro'; import Expand from '@components/Expand.astro'; +import FooterLinks from '@components/FooterLinks.astro'; import FeatureShowcase from '@components/FeatureShowcase.astro'; import FluidGrid from '@components/FluidGrid.astro'; import FooterPreferences from '@components/FooterPreferences.astro'; @@ -754,4 +755,67 @@ describe('custom Astro component render coverage', () => { expect(html).toContain(`origHeight%3D${heroImage.height}`); expect(html).not.toContain('h=1000'); }); + + it('hides footer community links on localized 404 pages', async () => { + const html = normalizeHtml( + await renderComponent(FooterLinks, { + requestUrl: 'https://aspire.dev/ja/404/', + locals: { + starlightRoute: { + editUrl: + 'https://github.com/microsoft/aspire.dev/edit/main/src/frontend/src/content/docs/404.mdx', + entry: { + id: '404', + slug: '404', + filePath: 'src/content/docs/404.mdx', + data: {}, + }, + }, + }, + }) + ); + + expect(html).not.toContain('footer.community'); + expect(html).not.toContain('https://x.com/aspiredotdev'); + }); + + it('hides footer community links when the pathname fallback matches 404 routes', async () => { + const fallbackRoute: StarlightRoute = { + editUrl: + 'https://github.com/microsoft/aspire.dev/edit/main/src/frontend/src/content/docs/test.mdx', + entry: { + id: 'docs/test', + slug: '', + filePath: 'src/content/docs/test.mdx', + data: {}, + }, + }; + + for (const requestUrl of ['https://aspire.dev/404/', 'https://aspire.dev/ja/404/']) { + const html = normalizeHtml( + await renderComponent(FooterLinks, { + requestUrl, + locals: { + starlightRoute: fallbackRoute, + }, + }) + ); + + expect(html).not.toContain('footer.community'); + expect(html).not.toContain('https://x.com/aspiredotdev'); + } + }); + + it('renders OsAwareTabs activation logic without anchor-only tab assumptions', async () => { + const html = normalizeHtml( + await renderComponent(OsAwareTabs, { + props: { syncKey: 'terminal' }, + slots: { unix: 'echo unix', windows: 'Write-Host windows' }, + }) + ); + const inlineScript = html.split('