feat(embed): photo click opens album page in a new tab (user decision 2026-09-24)

Reverts the in-host detail modal interaction: click now window.open's the album page (/a/:token?lang=, no /p/ deep link) with noopener — same target for middle-click/right-click via the real <a> href. The loader's open-photo modal code remains but is no longer triggered.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-24 15:25:41 +08:00
parent bd4db7b458
commit c199d0630c
2 changed files with 21 additions and 57 deletions
+13 -33
View File
@@ -309,44 +309,24 @@ test.describe.serial('E04 interactions: pause, click lock, keyboard, destroy', (
expect(after).toBeLessThan(resumedAt); // 恢复滚动(translateY 更负)
});
test('photo click opens viewer deep link for the clicked photo (§4.1)', async ({ page }) => {
test('photo click opens album page in a new tab (user decision 2026-09-24)', async ({ page }) => {
await openHost(page, { width: 1440, height: 900 });
const frame = page.locator('iframe[src*="/embed/"][src*="v=2"]');
await frame.waitFor();
// 悬停冻结 + 坐标直击当前命中的照片(.first() 可能已滚出视口——flaky 源)。
// 悬停冻结(点击防错图语义下稳定命中)。
const box = await frame.boundingBox();
const cx = (box!.x ?? 0) + 90;
const cy = (box!.y ?? 0) + 140;
await page.mouse.move(cx, cy);
await page.mouse.move((box?.x ?? 0) + 90, (box?.y ?? 0) + 140);
await page.waitForTimeout(400);
const embedFrame0 = page.frames().find((f) => f.url().includes('v=2'));
expect(embedFrame0).toBeDefined();
const photoId = await embedFrame0!.evaluate(([_x, _y]) => {
const el = document.elementFromPoint(_x, _y)?.closest('a.stream-photo');
return el?.getAttribute('data-photo-id') ?? '';
}, [90, 140] as const);
expect(photoId).not.toBe('');
// 宿主内弹框(用户决策 2026-09-23):不新开标签,宿主页覆盖层 + 详情 iframe。
let popupOpened = false;
page.once('popup', () => {
popupOpened = true;
});
await page.mouse.click(cx, cy);
const modalFrame = page.locator('iframe[title="PhotoLive photo detail"]');
await modalFrame.waitFor({ timeout: 5000 });
expect(popupOpened).toBe(false);
await expect
.poll(async () => {
const f = page.frames().find((fr) => (fr.url().includes('/p/') ?? false));
return f?.url() ?? '';
})
.toContain(`/p/${photoId}`);
await page.screenshot({ path: `${OUT_ROOT}/actual/server/photo-modal.png` });
await page
.locator('button[aria-label*="Close photo detail"], button[aria-label*="关闭照片详情"]')
.click();
await expect(modalFrame).toHaveCount(0);
const popupPromise = page.waitForEvent('popup', { timeout: 6000 });
await page.mouse.click((box?.x ?? 0) + 90, (box?.y ?? 0) + 140);
const popup = await popupPromise;
// 新标签打开的是相册页(/a/:token),不含照片深链 /p/。
expect(popup.url()).toMatch(/\/a\/[^/]+\?lang=/);
expect(popup.url()).not.toContain('/p/');
await popup.close();
// 宿主内不出现弹框。
await page.waitForTimeout(600);
expect(await page.locator('iframe[title="PhotoLive photo detail"]').count()).toBe(0);
});
test('drag beyond 8px cancels click (§4.1)', async ({ page }) => {