From 61c0cbbefe32cbcbc394f5a46b07fbec90bfe80a Mon Sep 17 00:00:00 2001 From: lllleolin-max Date: Tue, 22 Sep 2026 12:52:36 +0800 Subject: [PATCH] fix(theme): preserve alt overrides for themed images (#5457) --- __tests__/e2e/theme-image/image.test.ts | 38 +++++++++++++++++ __tests__/e2e/theme-image/index.md | 28 +++++++++++++ .../theme-default/components/VPImage.test.ts | 41 +++++++++++++++++++ .../theme-default/components/VPImage.vue | 4 +- 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 __tests__/e2e/theme-image/image.test.ts create mode 100644 __tests__/e2e/theme-image/index.md create mode 100644 __tests__/unit/client/theme-default/components/VPImage.test.ts diff --git a/__tests__/e2e/theme-image/image.test.ts b/__tests__/e2e/theme-image/image.test.ts new file mode 100644 index 000000000..1822edaff --- /dev/null +++ b/__tests__/e2e/theme-image/image.test.ts @@ -0,0 +1,38 @@ +describe('themeable image alt overrides', () => { + beforeEach(async () => { + await goto('/theme-image/') + }) + + test.each(['light', 'dark'])('uses the alt prop in %s mode', async (mode) => { + await page.evaluate((mode) => { + document.documentElement.classList.toggle('dark', mode === 'dark') + }, mode) + + expect( + await page.locator('.image-override img:visible').getAttribute('alt') + ).toBe('Override description') + expect( + await page.locator('.image-decorative img:visible').getAttribute('alt') + ).toBe('') + expect( + await page.locator('.image-fallback img:visible').getAttribute('alt') + ).toBe('Image description') + }) + + test('updates both images when the alt prop changes', async () => { + const descriptions = () => + page + .locator('.image-override img') + .evaluateAll((images) => + images.map((image) => image.getAttribute('alt')) + ) + + await page.getByRole('button', { name: 'Update description' }).click() + await expect + .poll(descriptions) + .toEqual(['Updated description', 'Updated description']) + + await page.getByRole('button', { name: 'Clear description' }).click() + await expect.poll(descriptions).toEqual(['', '']) + }) +}) diff --git a/__tests__/e2e/theme-image/index.md b/__tests__/e2e/theme-image/index.md new file mode 100644 index 000000000..c7ea85f4c --- /dev/null +++ b/__tests__/e2e/theme-image/index.md @@ -0,0 +1,28 @@ + + +# Themeable image descriptions + +
+ +
+ +
+ +
+ +
+ +
+ + + diff --git a/__tests__/unit/client/theme-default/components/VPImage.test.ts b/__tests__/unit/client/theme-default/components/VPImage.test.ts new file mode 100644 index 000000000..66719d376 --- /dev/null +++ b/__tests__/unit/client/theme-default/components/VPImage.test.ts @@ -0,0 +1,41 @@ +// @ts-expect-error the unit typecheck uses tsc; Vite compiles this SFC at runtime +import VPImage from 'client/theme-default/components/VPImage.vue' +import { createSSRApp } from 'vue' +import { renderToString } from 'vue/server-renderer' + +vi.mock('vitepress', () => ({ withBase: (path: string) => path })) + +const images = [ + { name: 'string', image: 'logo.svg', fallback: '', count: 1 }, + { + name: 'single source', + image: { src: 'logo.svg', alt: 'Image description' }, + fallback: 'Image description', + count: 1 + }, + { + name: 'themed sources', + image: { light: 'light.svg', dark: 'dark.svg', alt: 'Image description' }, + fallback: 'Image description', + count: 2 + }, + { + name: 'themed sources without alt', + image: { light: 'light.svg', dark: 'dark.svg' }, + fallback: '', + count: 2 + } +] + +describe.each(images)('VPImage with $name', ({ image, fallback, count }) => { + test.each(['Override description', '', undefined])( + 'respects the alt prop %j', + async (alt) => { + const html = await renderToString(createSSRApp(VPImage, { image, alt })) + const descriptions = [...html.matchAll(/ alt(?:="([^"]*)")?/g)].map( + ([, value]) => value ?? '' + ) + expect(descriptions).toEqual(Array(count).fill(alt ?? fallback)) + } + ) +}) diff --git a/src/client/theme-default/components/VPImage.vue b/src/client/theme-default/components/VPImage.vue index e2d606210..fb04af238 100644 --- a/src/client/theme-default/components/VPImage.vue +++ b/src/client/theme-default/components/VPImage.vue @@ -23,13 +23,13 @@ defineOptions({ inheritAttrs: false })