You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

111 lines
6.3 KiB

const assert = require('node:assert/strict');
const { chromium } = require('playwright');
const base = 'http://127.0.0.1:4173/pages';
const PNG = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIHWP4z8DwHwAFgAI/ScL/nwAAAABJRU5ErkJggg==';
(async () => {
const browser = await chromium.launch({ channel: 'msedge', headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 }, locale: 'zh-CN' });
const errors = [];
const apiCalls = [];
page.on('pageerror', (error) => errors.push(error.message));
page.on('request', (request) => { if (request.url().includes('/api/')) apiCalls.push(request.url()); });
// 未登录状态下的示例作品数量(后面用来确认没有被覆盖)
await page.goto(`${base}/me.html`);
await page.waitForTimeout(600);
const sampleWorks = await page.locator('.content-grid .content-card').count();
assert.ok(sampleWorks > 0, 'expected sample works on me.html');
// 1. 登录
await page.goto(`${base}/login.html?next=feed.html`);
await page.evaluate(() => window.YouthomeLocal.reset());
await page.reload();
await page.locator('#panel-password input[type="tel"]').fill('13800000000');
await page.locator('#panel-password input[type="password"]').fill('qinghe123');
await page.locator('#panel-password .btn-primary').click();
await page.waitForURL('**/feed.html');
// 2. 广场读取本地内容
await page.locator('.web-live-card').first().waitFor();
const feedCards = await page.locator('.web-live-card').count();
assert.ok(feedCards >= 6, `expected local feed cards, got ${feedCards}`);
// 3. 发布页发一条纯文字动态 → 详情
await page.goto(`${base}/publish-post.html`);
await page.locator('.post-textarea').fill('本地数据层冒烟测试:这是网页端发布的第一条动态。');
await page.locator('.web-publish-actions .btn-primary').click();
await page.waitForURL('**/post-detail.html?moment=*');
await page.locator('html:not(.notification-route-pending)').waitFor();
assert.ok((await page.locator('.post-detail-content').innerText()).includes('本地数据层冒烟测试'));
// 4. 详情互动:点赞、收藏、评论
await page.locator('.post-detail-actions [aria-label="点赞"]').click();
await page.waitForFunction(() => document.querySelector('.post-detail-actions [aria-label="点赞"] .count')?.textContent.trim() === '1', null, { timeout: 8000 });
await page.locator('.post-detail-actions [aria-label="收藏"]').click();
await page.waitForFunction(() => document.querySelector('.post-detail-actions [aria-label="收藏"]')?.classList.contains('saved'), null, { timeout: 8000 });
await page.locator('.comment-input').fill('自己给自己评论一条,验证评论写入。');
await page.locator('.btn-send').click();
await page.locator('.comment-item').first().waitFor();
assert.equal((await page.locator('.comment-count').innerText()).trim(), '共 1 条');
// 5. 发布页 → 草稿 → 草稿箱 → 发布
await page.goto(`${base}/publish-post.html`);
await page.locator('.post-textarea').fill('草稿流程验证内容');
await page.locator('.add-topic-btn').click();
await page.locator('.web-topic-input').fill('校园日常');
await page.locator('.web-topic-input').press('Enter');
await page.locator('input[type="file"]').setInputFiles({ name: 'photo.png', mimeType: 'image/png', buffer: Buffer.from(PNG, 'base64') });
await page.waitForFunction(() => document.querySelectorAll('.preview-images img').length === 1, null, { timeout: 8000 });
await page.locator('.web-publish-actions .btn-secondary').click();
await page.locator('.web-toast').getByText('草稿已保存').waitFor();
assert.ok(page.url().includes('draft='), 'draft id should be in the url');
await page.goto(`${base}/drafts.html`);
await page.locator('.draft-card').first().waitFor();
await page.locator('.draft-action-edit').first().click();
await page.waitForURL('**/publish-post.html?draft=*');
await page.waitForFunction(() => document.querySelector('.post-textarea')?.value === '草稿流程验证内容', null, { timeout: 8000 });
await page.locator('.web-publish-actions .btn-primary').click();
await page.waitForURL('**/post-detail.html?moment=*');
await page.locator('html:not(.notification-route-pending)').waitFor();
assert.ok((await page.locator('.post-detail-content').innerText()).includes('草稿流程验证内容'));
assert.equal(await page.locator('.post-detail-images img').count(), 1, 'uploaded image should show in detail');
// 6. 草稿发布后应被清理
await page.goto(`${base}/drafts.html`);
await page.locator('.web-live-empty').waitFor();
assert.equal((await page.locator('.draft-card').count()), 0);
// 7. 个人中心:示例作品保留,本地动态插在前面
await page.goto(`${base}/me.html`);
await page.locator('.content-grid .web-live-card').first().waitFor();
const liveCards = await page.locator('.content-grid .web-live-card').count();
assert.equal(liveCards, 2, `expected 2 local posts, got ${liveCards}`);
assert.equal(await page.locator('.content-grid .content-card').count(), sampleWorks + liveCards, 'sample works must be preserved');
// 8. 从个人中心进入本地动态详情
await page.locator('.content-grid .web-live-card').first().click();
await page.waitForURL('**/post-detail.html?moment=*');
await page.locator('html:not(.notification-route-pending)').waitFor();
// 9. 刷新后数据仍在(localStorage 持久化)
await page.reload();
await page.locator('html:not(.notification-route-pending)').waitFor();
assert.ok((await page.locator('.post-detail-content').innerText()).length > 0);
// 10. 移动端不出现横向溢出
await page.setViewportSize({ width: 390, height: 844 });
for (const name of ['feed', 'me', 'publish-post', 'drafts']) {
await page.goto(`${base}/${name}.html`);
await page.waitForTimeout(400);
const overflow = await page.evaluate(() => document.documentElement.scrollWidth - innerWidth);
assert.ok(overflow <= 0, `${name} overflows by ${overflow}px at 390px`);
}
assert.deepEqual(apiCalls, [], 'local mode must not call the backend');
assert.deepEqual(errors, []);
await browser.close();
console.log(`local publishing flow passed in Edge (sample works ${sampleWorks}, local posts ${liveCards})`);
})().catch((error) => { console.error(error); process.exitCode = 1; });