/** * 任务:更新项目 * * 逻辑: * 1. 打开仓库编辑页面 * 2. 在 Monaco 编辑器末尾追加内容 * 3. 点击提交修改按钮 */ const path = require('path'); const config = require(path.join(__dirname, '..', 'config')); const logger = require(path.join(__dirname, '..', 'utils', 'logger')); const { safeClose, safeGoto } = require(path.join(__dirname, '..', 'utils', 'page')); const { runDebugSteps } = require(path.join(__dirname, '..', 'utils', 'debugSteps')); /** * 执行更新项目任务 * @param {import('playwright').Page} page - 主页面 */ async function updateProject(page, options = {}) { logger.info('👉 执行项目更新'); const ctx = { page, newPage: null, frame: null, content: '', }; const steps = [ { name: '检查 GITCODE_TEST_REPO_URL 配置', page: (state) => state.page, run: async () => { if (!config.urls.testRepo) { throw new Error('未配置 GITCODE_TEST_REPO_URL,无法执行更新项目任务'); } }, inspect: async () => ({ testRepoConfigured: Boolean(config.urls.testRepo), }), }, { name: '打开仓库编辑页', page: (state) => state.newPage || state.page, run: async (state) => { state.newPage = await state.page.context().newPage(); state.currentPage = state.newPage; await safeGoto(state.newPage, config.urls.testRepo, { label: '仓库编辑页', timeout: config.navigationTimeout, waitFor: ['#codeartside'], }); logger.info('已进入仓库编辑页面'); }, inspect: async (state) => ({ codeartsideCount: state.newPage ? await state.newPage.locator('#codeartside').count() : 0, }), }, { name: '等待 #codeartside iframe', page: (state) => state.newPage, run: async (state) => { await state.newPage.locator('#codeartside').waitFor({ state: 'attached', timeout: 15000, }); state.frame = state.newPage.frameLocator('#codeartside'); }, inspect: async (state) => ({ codeartsideCount: await state.newPage.locator('#codeartside').count(), }), }, { name: '定位 Monaco 编辑器', page: (state) => state.newPage, run: async (state) => { await state.frame.locator('.monaco-editor').click({ position: { x: 200, y: 100 }, }); }, inspect: async (state) => ({ iframeCount: await state.newPage.locator('#codeartside').count(), monacoEditorCount: await state.frame.locator('.monaco-editor').count(), textareaCount: await state.frame.locator('textarea').count(), }), }, { name: '移动光标到文件末尾', page: (state) => state.newPage, run: async (state) => { await state.newPage.keyboard.press('Control+End'); await state.newPage.waitForTimeout(300); }, }, { name: '输入自动更新内容', page: (state) => state.newPage, run: async (state) => { state.content = `\n# 自动更新 - ${new Date().toISOString()}`; await state.newPage.keyboard.type(state.content, { delay: 50 }); logger.info('已写入内容'); }, }, { name: '点击第一个提交修改按钮', page: (state) => state.newPage, run: async (state) => { if (state.debug.dryRun) { logger.warn('DEBUG_DRY_RUN=true,跳过点击提交修改按钮'); return; } await state.newPage.locator('button:has-text("提交修改")').first().click(); await state.newPage.waitForTimeout(1000); }, inspect: async (state) => ({ submitButtonCount: await state.newPage.locator('button:has-text("提交修改")').count(), }), }, { name: '等待确认弹窗提交按钮', page: (state) => state.newPage, run: async (state) => { if (state.debug.dryRun) { logger.warn('DEBUG_DRY_RUN=true,跳过等待确认弹窗'); return; } const confirmBtn = state.newPage.locator('button:has-text("提交修改")').nth(1); await confirmBtn.waitFor({ state: 'visible', timeout: 10000 }); }, inspect: async (state) => ({ submitButtonCount: await state.newPage.locator('button:has-text("提交修改")').count(), dialogCount: await state.newPage.locator('[role="dialog"], .modal, .el-dialog').count(), }), }, { name: '点击弹窗里的提交修改', page: (state) => state.newPage, run: async (state) => { if (state.debug.dryRun) { logger.warn('DEBUG_DRY_RUN=true,跳过最终提交'); return; } const confirmBtn = state.newPage.locator('button:has-text("提交修改")').nth(1); await confirmBtn.click(); await state.newPage.waitForTimeout(1000); logger.info('项目更新已提交'); }, inspect: async (state) => ({ submitButtonCount: await state.newPage.locator('button:has-text("提交修改")').count(), }), }, ]; try { await runDebugSteps('更新项目', steps, ctx, options); } finally { await safeClose(ctx.newPage); } } module.exports = updateProject;