open-design/apps/web/tests/components/plugins-home-html-surface.test.tsx
pftom 244e8b7981 feat(daemon): enhance plugin preview handling and add fallback mechanisms
- Implemented a new `collectPluginPreviewCandidates` function to gather potential HTML assets for plugins, improving the robustness of the preview endpoint.
- Introduced `discoverPluginHtmlAssets` to scan common directories for HTML files, ensuring that plugins with missing declared entries can still provide a valid preview.
- Updated the `/api/plugins/:id/preview` route to utilize the new candidate collection and discovery functions, enhancing the user experience by preventing blank tiles in the gallery.
- Added comprehensive tests for the new fallback logic to ensure reliability and correctness in various scenarios.

This update significantly improves the plugin preview functionality, ensuring users have access to valid previews even when manifest entries are outdated or missing.
2026-05-12 16:45:24 +08:00

94 lines
2.9 KiB
TypeScript

// @vitest-environment jsdom
// Plugins-home HTML preview surface — reachability fallback.
//
// The home gallery used to render a permanently-blank tile when a
// plugin declared an `od.preview.entry` that 404'd on the daemon
// (the iframe quietly painted the JSON error envelope as white).
// The HtmlSurface now probes the URL once per session and swaps
// in a typographic fallback tile when the URL is unreachable.
//
// This file:
// - asserts the iframe is mounted when the probe succeeds;
// - asserts the typographic fallback renders (and the iframe is
// skipped) when the probe reports the URL is unreachable.
import { describe, expect, it, afterEach, beforeEach, vi } from 'vitest';
import { cleanup, render, waitFor } from '@testing-library/react';
import {
HtmlSurface,
__resetHtmlSurfaceProbeCacheForTests,
} from '../../src/components/plugins-home/cards/HtmlSurface';
import type { HtmlPreviewSpec } from '../../src/components/plugins-home/preview';
const PREVIEW: HtmlPreviewSpec = {
kind: 'html',
src: '/api/plugins/example-html-ppt/preview',
label: 'index.html',
source: 'preview',
};
const okResponse = (): Response =>
({ ok: true, status: 200 } as unknown as Response);
const notFoundResponse = (): Response =>
({ ok: false, status: 404 } as unknown as Response);
beforeEach(() => {
__resetHtmlSurfaceProbeCacheForTests();
});
afterEach(() => {
cleanup();
__resetHtmlSurfaceProbeCacheForTests();
vi.unstubAllGlobals();
vi.restoreAllMocks();
});
describe('HtmlSurface reachability probe', () => {
it('renders the iframe once the URL probes OK and the auto-arm window elapses', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(okResponse()));
const { container } = render(
<HtmlSurface
preview={PREVIEW}
pluginId="example-html-ppt"
pluginTitle="Html Ppt"
inView
/>,
);
// First the skeleton frame should appear, then after the 280ms
// arm timer the iframe should mount.
await waitFor(
() => {
expect(container.querySelector('iframe')).toBeTruthy();
},
{ timeout: 2000 },
);
expect(
container.querySelector('[data-testid="plugins-home-html-fallback"]'),
).toBeNull();
});
it('renders the typographic fallback (no iframe) when the URL 404s', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(notFoundResponse()));
const { container } = render(
<HtmlSurface
preview={PREVIEW}
pluginId="example-html-ppt"
pluginTitle="Html Ppt"
inView
/>,
);
await waitFor(
() => {
expect(
container.querySelector('[data-testid="plugins-home-html-fallback"]'),
).toBeTruthy();
},
{ timeout: 2000 },
);
expect(container.querySelector('iframe')).toBeNull();
expect(
container.querySelector('.plugins-home__html-fallback-glyph')?.textContent,
).toBe('H');
});
});