From caba7b8cbb9bed38c27e1fde4c668936957a3cd0 Mon Sep 17 00:00:00 2001
From: mango <1711456624@qq.com>
Date: Wed, 29 Jul 2026 18:46:04 +0800
Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20test(ai):=20=E6=94=B9=E7=94=A8?=
=?UTF-8?q?=E8=A1=8C=E4=B8=BA=E6=96=AD=E8=A8=80=E9=AA=8C=E8=AF=81=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E5=9D=97=E4=BE=9D=E8=B5=96=E8=BE=B9=E7=95=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...essageCodeBlock.dependencyBoundary.test.ts | 140 +++++++++++++++---
1 file changed, 123 insertions(+), 17 deletions(-)
diff --git a/frontend/src/components/ai/messageBubble/AIMessageCodeBlock.dependencyBoundary.test.ts b/frontend/src/components/ai/messageBubble/AIMessageCodeBlock.dependencyBoundary.test.ts
index 576ea03b..fa523886 100644
--- a/frontend/src/components/ai/messageBubble/AIMessageCodeBlock.dependencyBoundary.test.ts
+++ b/frontend/src/components/ai/messageBubble/AIMessageCodeBlock.dependencyBoundary.test.ts
@@ -1,27 +1,133 @@
-import { readFileSync } from 'node:fs';
-import { describe, expect, it } from 'vitest';
+import React from 'react';
+import { act, create, type ReactTestRenderer } from 'react-test-renderer';
+import { describe, expect, it, vi } from 'vitest';
-const source = readFileSync(new URL('./AIMessageCodeBlock.tsx', import.meta.url), 'utf8');
-const viteConfigSource = readFileSync(new URL('../../../../vite.config.ts', import.meta.url), 'utf8');
+import viteConfig from '../../../../vite.config';
+import { buildOverlayWorkbenchTheme } from '../../../utils/overlayWorkbenchTheme';
+import { AIMessageCodeBlock } from './AIMessageCodeBlock';
+
+const dependencyMocks = vi.hoisted(() => ({
+ completeRegistryLoad: vi.fn(),
+ mermaidInitialize: vi.fn(),
+ mermaidLoad: vi.fn(),
+ mermaidRender: vi.fn(async () => ({ svg: '' })),
+ prismLightLoad: vi.fn(),
+ registerLanguage: vi.fn(),
+}));
+
+vi.mock('react-syntax-highlighter', () => {
+ dependencyMocks.completeRegistryLoad();
+ return { default: () => null };
+});
+
+vi.mock('react-syntax-highlighter/dist/esm/prism-light', () => {
+ dependencyMocks.prismLightLoad();
+ return {
+ default: Object.assign(() => null, {
+ registerLanguage: dependencyMocks.registerLanguage,
+ }),
+ };
+});
+
+vi.mock('mermaid', () => {
+ dependencyMocks.mermaidLoad();
+ return {
+ default: {
+ initialize: dependencyMocks.mermaidInitialize,
+ render: dependencyMocks.mermaidRender,
+ },
+ };
+});
+
+vi.mock('antd', () => ({
+ Tooltip: ({ children }: { children: React.ReactNode }) => children,
+ message: { error: vi.fn() },
+}));
+
+vi.mock('@ant-design/icons', () => ({
+ CheckOutlined: () => null,
+ CopyOutlined: () => null,
+ PlayCircleOutlined: () => null,
+}));
+
+vi.mock('../../common/ResizableDraggableModal', () => ({
+ default: Object.assign(() => null, { confirm: vi.fn() }),
+}));
+
+const codeHighlightDependencies = [
+ 'react-syntax-highlighter/dist/esm/prism-light',
+ 'react-syntax-highlighter/dist/esm/languages/prism/bash',
+ 'react-syntax-highlighter/dist/esm/languages/prism/css',
+ 'react-syntax-highlighter/dist/esm/languages/prism/diff',
+ 'react-syntax-highlighter/dist/esm/languages/prism/go',
+ 'react-syntax-highlighter/dist/esm/languages/prism/ini',
+ 'react-syntax-highlighter/dist/esm/languages/prism/java',
+ 'react-syntax-highlighter/dist/esm/languages/prism/javascript',
+ 'react-syntax-highlighter/dist/esm/languages/prism/json',
+ 'react-syntax-highlighter/dist/esm/languages/prism/jsx',
+ 'react-syntax-highlighter/dist/esm/languages/prism/markdown',
+ 'react-syntax-highlighter/dist/esm/languages/prism/markup',
+ 'react-syntax-highlighter/dist/esm/languages/prism/php',
+ 'react-syntax-highlighter/dist/esm/languages/prism/python',
+ 'react-syntax-highlighter/dist/esm/languages/prism/ruby',
+ 'react-syntax-highlighter/dist/esm/languages/prism/rust',
+ 'react-syntax-highlighter/dist/esm/languages/prism/sql',
+ 'react-syntax-highlighter/dist/esm/languages/prism/toml',
+ 'react-syntax-highlighter/dist/esm/languages/prism/tsx',
+ 'react-syntax-highlighter/dist/esm/languages/prism/typescript',
+ 'react-syntax-highlighter/dist/esm/languages/prism/yaml',
+ 'react-syntax-highlighter/dist/esm/styles/prism/vsc-dark-plus',
+ 'react-syntax-highlighter/dist/esm/styles/prism/vs',
+];
+
+const renderCodeBlock = (className: string, children: string) => React.createElement(AIMessageCodeBlock, {
+ className,
+ children,
+ darkMode: false,
+ overlayTheme: buildOverlayWorkbenchTheme(false),
+});
describe('AIMessageCodeBlock dependency boundary', () => {
- it('does not pull the complete syntax-highlighter language registry into the AI panel chunk', () => {
- expect(source).not.toMatch(/from\s+['"]react-syntax-highlighter['"]/);
- expect(source).toMatch(/react-syntax-highlighter\/dist\/esm\/prism-light/);
+ it('loads the lightweight syntax highlighter without the complete language registry', () => {
+ expect(dependencyMocks.completeRegistryLoad).not.toHaveBeenCalled();
+ expect(dependencyMocks.prismLightLoad).toHaveBeenCalledOnce();
+ expect(dependencyMocks.registerLanguage).toHaveBeenCalledTimes(20);
});
- it('loads Mermaid only when a Mermaid fenced block is rendered', () => {
- expect(source).not.toMatch(/^import\s+mermaid\s+from\s+['"]mermaid['"];?$/m);
- expect(source).toMatch(/await\s+import\(['"]mermaid['"]\)/);
- });
+ it('loads Mermaid only when a Mermaid fenced block is rendered', async () => {
+ let renderer: ReactTestRenderer | undefined;
+ let mermaidContainer: { innerHTML: string } | undefined;
- it('pre-bundles every static syntax-highlighter entry before Wails opens the panel', () => {
- const syntaxHighlighterImports = Array.from(source.matchAll(
- /from\s+['"](react-syntax-highlighter\/dist\/esm\/[^'"]+)['"]/g,
- )).map((match) => match[1]);
+ try {
+ act(() => {
+ renderer = create(renderCodeBlock('language-sql', 'SELECT 1;'), {
+ createNodeMock: (element) => {
+ if (element.type === 'div' && element.props.className === 'ai-mermaid-container') {
+ mermaidContainer = { innerHTML: '' };
+ return mermaidContainer;
+ }
+ return {};
+ },
+ });
+ });
+ expect(dependencyMocks.mermaidLoad).not.toHaveBeenCalled();
- for (const dependency of syntaxHighlighterImports) {
- expect(viteConfigSource).toContain(`'${dependency}'`);
+ await act(async () => {
+ renderer?.update(renderCodeBlock('language-mermaid', 'graph TD; A-->B;'));
+ });
+ await vi.waitFor(() => {
+ expect(dependencyMocks.mermaidLoad).toHaveBeenCalledOnce();
+ expect(dependencyMocks.mermaidInitialize).toHaveBeenCalledWith({ startOnLoad: false, theme: 'default' });
+ expect(dependencyMocks.mermaidRender).toHaveBeenCalledWith(expect.stringMatching(/^mermaid-/), 'graph TD; A-->B;');
+ expect(mermaidContainer?.innerHTML).toBe('');
+ });
+ } finally {
+ act(() => renderer?.unmount());
}
});
+
+ it('pre-bundles every static syntax-highlighter dependency', () => {
+ const includedDependencies = viteConfig.optimizeDeps?.include || [];
+ expect(includedDependencies).toEqual(expect.arrayContaining(codeHighlightDependencies));
+ });
});