import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron'; const CANONICAL_PROJECT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u; async function readProjectConfig(projectPath: string): Promise> { return JSON.parse(await readFile(path.join(projectPath, '.makelore', 'project.json'), 'utf8')) as Record; } async function selectProgrammingProjectFolder( app: Parameters[0], projectPath: string, ) { await app.evaluate(({ ipcMain }, selectedPath) => { ipcMain.removeHandler('dialog:open'); ipcMain.handle('dialog:open', async () => ({ canceled: false, filePaths: [selectedPath] })); }, projectPath); const page = await getStableWindow(app); await expect(page.getByTestId('ai-module-selection-page')).toBeVisible(); await page.getByTestId('ai-module-option-programming').click(); await expect(page.getByTestId('main-layout')).toBeVisible(); return page; } test.describe('Coding project identity UX', () => { test('keeps the service identity automatic and invisible to young users', async ({ launchElectronApp }) => { const projectPath = await mkdtemp(path.join(tmpdir(), 'makelore-identity-e2e-')); const app = await launchElectronApp({ skipSetup: true }); try { const page = await selectProgrammingProjectFolder(app, projectPath); await page.getByTestId('sidebar-create-project').click(); const createDialog = page.getByRole('dialog', { name: '新建项目' }); await expect(createDialog.getByText(/项目 ID|项目身份|独立副本/u)).toHaveCount(0); await expect(createDialog.getByRole('radio', { name: '创建新的项目 ID' })).toHaveCount(0); await expect(createDialog.getByRole('radio', { name: '绑定已有项目 ID' })).toHaveCount(0); await createDialog.getByRole('button', { name: '选择路径' }).click(); await expect(createDialog.getByLabel('项目路径')).toHaveValue(projectPath); await createDialog.getByRole('button', { name: '确认创建' }).click(); await expect(page.getByTestId('coding-chat-empty-agent')).toBeVisible(); const createdConfig = await readProjectConfig(projectPath); const generatedProjectId = String(createdConfig.projectId); expect(generatedProjectId).toMatch(CANONICAL_PROJECT_ID); await page.getByRole('button', { name: '创建项目智能体' }).click(); await expect(page.getByTestId('project-configuration-page')).toBeVisible(); await expect(page.getByTestId('project-identity-card')).toHaveCount(0); await expect(page.getByTestId('legacy-project-identity-card')).toHaveCount(0); await expect(page.getByTestId('independent-copy-button')).toHaveCount(0); await expect(page.getByText(/项目 ID|项目身份|独立副本/u)).toHaveCount(0); const legacyConfig = await readProjectConfig(projectPath); delete legacyConfig.projectId; await writeFile( path.join(projectPath, '.makelore', 'project.json'), JSON.stringify(legacyConfig, null, 2), ); await page.reload(); await expect(page.getByTestId('project-configuration-page')).toBeVisible(); const repairedConfig = await readProjectConfig(projectPath); const repairedProjectId = String(repairedConfig.projectId); expect(repairedProjectId).toMatch(CANONICAL_PROJECT_ID); expect(repairedProjectId).not.toBe(generatedProjectId); await expect(page.getByTestId('project-identity-card')).toHaveCount(0); await expect(page.getByTestId('legacy-project-identity-card')).toHaveCount(0); await expect(page.getByText(/项目 ID|项目身份|独立副本/u)).toHaveCount(0); } finally { await closeElectronApp(app); await rm(projectPath, { recursive: true, force: true }); } }); });