/** * 任务:查看热门 * * 逻辑: * 1. 找到"查看热门"任务卡片 * 2. 点击 toComplete 打开热门页面 * 3. 点击第一个热门项目卡片 * 4. 在新页面停留后关闭 */ const path = require('path'); const logger = require(path.join(__dirname, '..', 'utils', 'logger')); const { openNewPage, openLinkPage, safeClose, waitPageReady, waitForElements, getPageInfo, } = require(path.join(__dirname, '..', 'utils', 'page')); const { runDebugSteps } = require(path.join(__dirname, '..', 'utils', 'debugSteps')); const PROJECT_CARD_SELECTORS = [ '.hot-selection .recommend-card', '.hot-selection-list a.recommend-card', 'a.recommend-card', 'a[href*="source_module=home_hot_selection"]', 'a[href*="source_module=home_recommend"]', 'a[href*="/"][href*="?source_module="]', ]; /** * 执行查看热门任务 * @param {import('playwright').Page} page - 主页面 */ async function dailyTrending(page, options = {}) { logger.info('👉 执行查看热门'); const ctx = { page, trendingTask: null, trendingPage: null, cards: [], detailPage: null, }; const steps = [ { name: '查找查看热门任务卡片', page: (state) => state.page, run: async (state) => { const tasks = await state.page.locator('.novice-task-item-box').all(); state.trendingTask = await findTaskByTitle(tasks, '查看热门'); if (!state.trendingTask) { throw new Error('未找到"查看热门"任务卡片'); } }, inspect: async (state) => ({ taskCardCount: await state.page.locator('.novice-task-item-box').count(), titleLabelCount: await state.page.locator('.task-card-title__label-text').count(), toCompleteCount: await state.page.locator('.toComplete').count(), }), }, { name: '点击去完成并打开热门页', page: (state) => state.page, run: async (state) => { state.trendingPage = await openNewPage( state.page.context(), () => state.trendingTask.locator('.toComplete').click() ); state.currentPage = state.trendingPage; }, inspect: async (state) => ({ taskToCompleteVisible: await state.trendingTask.locator('.toComplete').isVisible().catch(() => false), }), }, { name: '查找热门项目卡片', page: (state) => state.trendingPage, run: async (state) => { state.cards = await findProjectCards(state.trendingPage); }, inspect: async (state) => ({ hotSelectionCount: await state.trendingPage.locator('.hot-selection').count(), recommendCardCount: await state.trendingPage.locator('a.recommend-card').count(), sourceModuleLinkCount: await state.trendingPage.locator('a[href*="source_module"]').count(), allLinkCount: await state.trendingPage.locator('a[href]').count(), }), }, { name: '打开第一个热门项目详情页', page: (state) => state.trendingPage, run: async (state) => { state.detailPage = await openLinkPage( state.trendingPage.context(), state.cards[0], { label: '热门项目详情页' } ); state.currentPage = state.detailPage; }, inspect: async (state) => ({ cardCount: state.cards.length, }), }, { name: '等待热门项目详情页加载', page: (state) => state.detailPage, run: async (state) => { await waitPageReady(state.detailPage); logger.info('热门项目详情页已打开'); }, }, ]; try { await runDebugSteps('查看热门', steps, ctx, options); } finally { await safeClose(ctx.detailPage); await safeClose(ctx.trendingPage); } } async function findProjectCards(page) { const cards = await waitForElements(page, PROJECT_CARD_SELECTORS, { interval: 1000, maxRetries: 15, minCount: 1, }); if (cards.length > 0) { logger.info(`热门项目候选数量: ${cards.length}`); return cards; } const info = await getPageInfo(page); throw new Error( `未找到热门项目卡片,页面结构可能已变化。页面标题: ${info.title}, 页面URL: ${info.url}` ); } /** * 根据标题查找任务卡片 */ async function findTaskByTitle(tasks, title) { for (const task of tasks) { try { const text = await task.locator('.task-card-title__label-text').innerText(); if (text.includes(title)) { return task; } } catch (_) { // 跳过 } } return null; } module.exports = dailyTrending;