Simplify connection flow, mobile transfer UI, and header actions

This commit is contained in:
2026-10-12 18:14:54 +09:00
parent 5f431ee153
commit 4f0231da3d
22 changed files with 820 additions and 131 deletions

View File

@@ -46,6 +46,27 @@ try {
});
await page.goto(url);
await page.locator('.app-shell[data-online="true"]').waitFor();
assert.equal(
await page
.locator('.header-actions > .theme-toggle,.header-actions > .pwa-install-button')
.count(),
0,
);
const more = page.getByRole('button', { name: '더보기', exact: true });
await more.click();
await page.getByRole('menu', { name: '더보기', exact: true }).waitFor();
await page.keyboard.press('End');
assert.equal(await page.evaluate(() => document.activeElement.textContent.trim()), '서비스 정보');
await page.keyboard.press('ArrowUp');
assert.match(await page.evaluate(() => document.activeElement.textContent.trim()), /테마/);
await page.keyboard.press('Escape');
assert.equal(await more.getAttribute('aria-expanded'), 'false');
await more.click();
await page.getByRole('menuitem', { name: '서비스 정보', exact: true }).click();
await page.getByRole('dialog', { name: '서비스 정보', exact: true }).waitFor();
assert.equal(await more.getAttribute('aria-expanded'), 'false');
await page.getByRole('button', { name: '대화상자 닫기', exact: true }).click();
assert.equal(await page.getByText('어디로 보낼까요?', { exact: true }).count(), 0);
assert.equal(await page.locator('.devices-stage .spin,.section-heading h2 span').count(), 0);
await page.screenshot({ path: 'tests/e2e/main-empty-device.png', fullPage: true });
@@ -165,6 +186,65 @@ try {
assert.equal(linkBounds.width, bounds.width);
await page.screenshot({ path: 'tests/e2e/send-sheet.png' });
await page.getByRole('button', { name: '전송 화면 닫기' }).click();
await page.locator('.footer-device-summary').click();
const settings = page.getByRole('dialog', { name: '설정', exact: true });
assert.equal(
await page
.getByLabel('기기 이름', { exact: true })
.evaluate((el) => document.activeElement === el),
false,
'모바일 설정에서 키보드를 자동으로 열지 않음',
);
assert.equal(
await page
.getByLabel('기기 이름', { exact: true })
.evaluate((el) => getComputedStyle(el).fontSize),
'16px',
);
const settingsBounds = await settings.boundingBox();
assert(Math.abs(settingsBounds.y + settingsBounds.height - 900) < 2, '모바일 설정 하단 시트');
await page.getByLabel('기기 이름', { exact: true }).focus();
await page.evaluate(() => {
Object.defineProperty(visualViewport, 'height', { configurable: true, value: 480 });
visualViewport.dispatchEvent(new Event('resize'));
});
await page.waitForFunction(
() => document.documentElement.style.getPropertyValue('--hop-visible-height') === '480px',
);
const keyboardBounds = await settings.boundingBox();
assert(
keyboardBounds.y >= 0 && Math.abs(keyboardBounds.y + keyboardBounds.height - 480) < 2,
'키보드가 열린 가시 영역 안의 설정 시트',
);
await page.screenshot({ path: 'tests/e2e/device-settings-mobile-keyboard.png' });
await page.evaluate(() => {
delete visualViewport.height;
visualViewport.dispatchEvent(new Event('resize'));
});
await page.getByRole('button', { name: '대화상자 닫기', exact: true }).click();
await page.getByRole('button', { name: '로그인', exact: false }).first().click();
for (const input of [
page.getByLabel('이메일', { exact: true }),
page.getByLabel('비밀번호', { exact: true }),
]) {
assert.equal(await input.evaluate((el) => getComputedStyle(el).fontSize), '16px');
await input.focus();
assert.equal(
await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth),
true,
);
}
await page.screenshot({ path: 'tests/e2e/login-mobile.png' });
await more.click();
const menuBounds = await page.getByRole('menu', { name: '더보기', exact: true }).boundingBox();
assert(
menuBounds.x >= 0 && menuBounds.x + menuBounds.width <= 390,
'모바일 더보기 메뉴 가로 넘침 없음',
);
await page.screenshot({ path: 'tests/e2e/header-more-mobile.png' });
await page.keyboard.press('Escape');
await page.getByRole('button', { name: 'hop. 홈', exact: true }).click();
await page.setViewportSize({ width: 1280, height: 1000 });
await page.locator('.peer-card').first().click();
const dialog = await page.locator('.send-dialog').boundingBox();
@@ -186,8 +266,32 @@ try {
await page.getByLabel('다른 기기에서 발견 허용').check();
await page.locator('.footer-device-summary').filter({ hasText: '발견 허용' }).waitFor();
await page.getByRole('button', { name: '대화상자 닫기', exact: true }).click();
await page.getByRole('button', { name: 'QR · 코드로 연결', exact: true }).click();
await page.getByRole('button', { name: '연결 공간 만들기' }).click();
await page.getByRole('button', { name: '다른 기기 연결', exact: true }).click();
assert.equal(await page.locator('.connection-choice').count(), 2);
assert.equal(await page.getByRole('checkbox', { name: '참여할 때 내 승인 받기' }).count(), 0);
assert.equal(await page.getByLabel('연결 코드', { exact: true }).count(), 0);
await page.screenshot({ path: 'tests/e2e/connection-choose-desktop.png' });
await page.setViewportSize({ width: 390, height: 844 });
await page.getByRole('button', { name: '연결 공간 참여', exact: true }).click();
const joinInput = page.getByLabel('연결 코드', { exact: true });
assert.equal(await joinInput.getAttribute('placeholder'), '초대 코드 4자리');
assert.equal(await joinInput.evaluate((el) => getComputedStyle(el).fontSize), '16px');
await joinInput.fill('ABC');
assert.equal(
await page.getByRole('button', { name: '참여하기', exact: true }).isDisabled(),
true,
);
await joinInput.fill('ABCD');
assert.equal(await page.getByRole('button', { name: '참여하기', exact: true }).isEnabled(), true);
await page.screenshot({ path: 'tests/e2e/connection-join-mobile.png' });
await page.getByRole('button', { name: '연결 방법 선택으로 돌아가기', exact: true }).click();
await page.getByRole('button', { name: '연결 공간 만들기', exact: true }).click();
await page.getByRole('checkbox', { name: '참여할 때 내 승인 받기' }).waitFor();
assert.equal(await page.getByLabel('연결 코드', { exact: true }).count(), 0);
await page.getByRole('button', { name: '만들기', exact: true }).click();
assert.match(await page.locator('.invite-code').innerText(), /^[A-HJ-NP-Z2-9]{4}$/);
assert.equal(await page.locator('.local-qr-note,.invite-display .spin').count(), 0);
await page.screenshot({ path: 'tests/e2e/connection-invite-mobile.png' });
const code = (await page.locator('.invite-code').innerText()).replace(/\s/g, '');
const cookie = sockets[1].io.opts.extraHeaders.Cookie;
const token = cookie.split('=')[1];
@@ -202,13 +306,10 @@ try {
body: JSON.stringify({ code }),
});
assert.equal(response.status, 200);
await page.getByRole('dialog', { name: 'QR · 코드로 연결' }).waitFor({ state: 'hidden' });
await page.locator('.modal-backdrop').waitFor({ state: 'hidden' });
await page.waitForFunction(() => document.querySelectorAll('.peer-card').length === 1);
await page.screenshot({ path: 'tests/e2e/main-single-device.png', fullPage: true });
assert.equal(
await page.getByRole('button', { name: 'QR · 코드로 연결', exact: true }).count(),
0,
);
assert.equal(await page.getByRole('button', { name: '다른 기기 연결', exact: true }).count(), 0);
await page.getByRole('button', { name: '방 나가기', exact: true }).click();
await page.waitForFunction(() => document.querySelectorAll('.peer-card').length === 12);
assert.deepEqual(errors, []);