feat: integrate pixel teacher presence and resilient classroom preview

This commit is contained in:
鲨鱼辣椒 committed 2026-09-23 18:23:53 +08:00
1 parent 12d800ec51
commit 7951cca700
60 files changed
+3313 -180

No files matched your search

+27
View File
@@ -5,6 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { handleAgentBrowserRoutes } from '@electron/api/routes/agent-browser';
import { CodingWorkPreviewService } from '@electron/coding-runtime/work-preview';
import {
getRendererCapability,
RENDERER_CAPABILITY_HEADER,
@@ -85,6 +86,7 @@ describe('Agent Browser Host API routes', () => {
});
afterEach(async () => {
vi.restoreAllMocks();
await Promise.all(
temporaryDirectories.splice(0).map((directory) => rm(directory, { recursive: true, force: true })),
);
@@ -96,10 +98,34 @@ describe('Agent Browser Host API routes', () => {
return await realpath(directory);
}
it('allows only the Renderer to request automatic startup for the active project', async () => {
const projectPath = await temporaryProject('makelore-work-preview-route-');
const ensure = vi.spyOn(CodingWorkPreviewService.prototype, 'ensure').mockResolvedValue({ status: 'ready', browser: snapshot(projectPath) });
const ctx = context(projectPath, {});
const release = vi.fn();
const acquireLease = vi.fn(() => release);
Object.assign(ctx, { codingProducts: { conversations: {}, runtime: {} }, lifecycle: { acquireLease } });
for (const [headers, projectId, expected] of [
[{}, 'project-1', 403],
[{ [RENDERER_CAPABILITY_HEADER]: getRendererCapability() }, 'another-project', 403],
[{ [RENDERER_CAPABILITY_HEADER]: getRendererCapability() }, 'project-1', 200],
] as const) {
const response = createResponse();
await handleAgentBrowserRoutes(createRequest('POST', { project_id: projectId, request_id: 'action-1', conversation_id: 'c1' }, headers), response.res, new URL('http://localhost/api/agent-browser/ensure-work'), ctx);
expect(response.res.statusCode).toBe(expected);
}
expect(ensure).toHaveBeenCalledTimes(1);
expect(acquireLease).toHaveBeenCalledTimes(1);
expect(release).toHaveBeenCalledTimes(1);
expect(ensure).toHaveBeenCalledWith(expect.objectContaining({ id: 'project-1', path: projectPath }), 'action-1', 'c1');
});
it('opens the browser for the active project and emits a visible hint', async () => {
const projectPath = await temporaryProject('niancode-agent-browser-route-');
const remember = vi.spyOn(CodingWorkPreviewService.prototype, 'remember');
const open = vi.fn().mockResolvedValue(snapshot(projectPath));
const ctx = context(projectPath, { open });
Object.assign(ctx, { codingProducts: { conversations: {}, runtime: {} } });
const response = createResponse();
const handled = await handleAgentBrowserRoutes(
@@ -114,6 +140,7 @@ describe('Agent Browser Host API routes', () => {
new URL('http://127.0.0.1/api/agent-browser/open'),
ctx,
);
expect(remember).toHaveBeenCalledWith(expect.objectContaining({ id: 'project-1', path: projectPath }), snapshot(projectPath));
expect(handled).toBe(true);
expect(response.res.statusCode).toBe(200);