Files
makelore/tests/e2e/canvas-my-works-layout-harness.mjs
T

94 lines
6.1 KiB
JavaScript

import { createServer, normalizePath } from 'vite';
import { resolve } from 'node:path';
export async function createCanvasMyWorksServer({ port = 0 } = {}) {
const entry = normalizePath(resolve('tests/e2e/__canvas-my-works-layout-harness.tsx'));
const server = await createServer({
configFile: false,
cacheDir: 'node_modules/.vite-canvas-my-works-layout',
resolve: { alias: { '@': resolve('src') } },
esbuild: { jsx: 'automatic' },
server: { host: '127.0.0.1', port, strictPort: port !== 0 },
plugins: [{
name: 'isolated-canvas-my-works-layout', enforce: 'pre',
resolveId(id) { if (id === '/__canvas-my-works-layout-harness.tsx') return entry; },
load(id) {
if (id.endsWith('/src/lib/image-workspace.ts')) return `
import { fixtureBootstrap, fixtureWorkspaces, fixtureWorks } from '/tests/e2e/canvas-my-works-layout-fixture.ts';
export const IMAGE_WORKSPACE_CREATE_PROJECT_EVENT = 'qa:image-workspace:create-project';
export class ImageWorkspaceApiError extends Error {}
export const createImageWorkspaceOperationId = () => 'qa-operation';
let projectSequence = 0;
export const createImageWorkspaceProjectId = () => 'qa-workspace-created-' + ++projectSequence;
export const fetchImageWorkspace = async () => structuredClone(fixtureBootstrap);
export const fetchImageWorkspaceProject = async id => {
const workspace = fixtureWorkspaces.find(workspace => workspace.workspace.workspaceId === id);
if (!workspace) throw new Error('Unknown local QA Workspace');
return structuredClone(workspace);
};
export const fetchImageGeneratedWorks = async () => ({ works: structuredClone(fixtureWorks), nextOffset: null, unavailableWorkspaceIds: [], historyTruncated: false });
export const openImageWorkspaceEvents = async () => ({ addEventListener() {}, close() {} });
export const resolveImageWorkspaceAssetUrl = async path => path;
export const saveImageWorkspaceAsset = async () => ({ status: 'cancelled' });
const forbidden = async () => { throw new Error('Local QA does not create, edit, delete, upload, or generate'); };
export const createImageWorkspaceProject = forbidden;
export const deleteImageWorkspaceProject = forbidden;
export const renameImageWorkspaceProject = forbidden;
export const submitImageWorkspaceCommand = forbidden;
export const uploadImageWorkspaceAsset = forbidden;
`;
if (id.endsWith('/src/lib/host-api.ts')) return `
export const setDesktopBackgroundLease = async () => undefined;
export const hostApiFetch = async () => { throw new Error('Local QA forbids Host API requests'); };
`;
if (id === entry) return `
import { createRoot } from 'react-dom/client';
import { ImageCanvas } from '/src/pages/ImageCanvas/index.tsx';
import { DesignWorksRail } from '/src/pages/ImageCanvas/DesignWorksRail.tsx';
import { useImageWorkspaceStore } from '/src/stores/image-workspace.ts';
import { useAuthStore } from '/src/stores/auth.ts';
import { fixtureBootstrap, fixtureWorkspaces } from '/tests/e2e/canvas-my-works-layout-fixture.ts';
import '/src/styles/globals.css';
useAuthStore.setState({ initialized: true, accessToken: 'qa-only', user: { username: '本地样例', userId: 'qa-only', authorities: [], tenantId: null, deptId: null }, isAuthenticated: () => true });
useImageWorkspaceStore.setState({ status: 'ready', bootstrap: fixtureBootstrap, activeWorkspaceId: fixtureWorkspaces[0].workspace.workspaceId, workspace: fixtureWorkspaces[0] });
window.__canvasMyWorksQA = {
setDraft: value => useImageWorkspaceStore.getState().setChatDraft(value),
state: () => ({ draft: useImageWorkspaceStore.getState().chatDraft, workspaceId: useImageWorkspaceStore.getState().activeWorkspaceId }),
};
// DOM-only media fixture: actual repository images are posters. No video
// bytes are played, generated, downloaded, or billed in layout tests.
document.addEventListener('error', event => { if (event.target instanceof HTMLVideoElement) event.stopImmediatePropagation(); }, true);
const videoFixture = new MutationObserver(() => document.querySelectorAll('video').forEach(video => {
if (video.dataset.qaPoster) return;
video.dataset.qaPoster = 'true';
video.poster = '/src/assets/module-robot.webp';
video.removeAttribute('src');
queueMicrotask(() => video.dispatchEvent(new Event('loadedmetadata')));
}));
videoFixture.observe(document.body, { childList: true, subtree: true });
function CanvasFrame() {
const workspace = useImageWorkspaceStore(state => state.workspace);
return <div style={{display: 'flex', height: '100dvh', minWidth: 0}}>
<style>{'@media(min-width:1024px){.canvas-my-works-qa-projects{display:block}}@media(max-width:1023px){.canvas-my-works-qa-projects{display:none}}'}</style>
<aside className="canvas-my-works-qa-projects" style={{width: 224, flexShrink: 0, borderRight: '1px solid hsl(var(--border))'}}>
<DesignWorksRail workspace={workspace} />
</aside>
<main style={{flex: 1, minWidth: 0, height: '100%'}}><ImageCanvas /></main>
</div>;
}
createRoot(document.getElementById('root')).render(<CanvasFrame />);
`;
},
configureServer(vite) {
vite.middlewares.use((req, res, next) => {
if (req.url !== '/' && !req.url?.startsWith('/?')) return next();
res.setHeader('Content-Type', 'text/html');
res.end('<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"></head><body><div id="root"></div><script type="module" src="/__canvas-my-works-layout-harness.tsx"></script></body></html>');
});
},
}],
});
await server.listen();
return server;
}