Add plain text clipboard transfers and refine input feedback
This commit is contained in:
@@ -107,6 +107,11 @@ try {
|
||||
window.__hopClipboard = 'https://example.com/focus-refresh';
|
||||
window.dispatchEvent(new Event('focus'));
|
||||
});
|
||||
await sender.waitForFunction(
|
||||
(value) => document.querySelector('.current-url')?.value === value,
|
||||
secret,
|
||||
);
|
||||
await sender.getByRole('button', { name: '다시 가져오기', exact: true }).click();
|
||||
await sender.waitForFunction(
|
||||
(value) => document.querySelector('.current-url')?.value === value,
|
||||
'https://example.com/focus-refresh',
|
||||
@@ -123,7 +128,7 @@ try {
|
||||
await sender.evaluate(() => {
|
||||
window.__hopClipboard = 'https://example.com/?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E';
|
||||
});
|
||||
await sender.getByRole('button', { name: '클립보드 가져오기', exact: true }).click();
|
||||
await sender.getByRole('button', { name: '다시 가져오기', exact: true }).click();
|
||||
await sender.waitForFunction(
|
||||
(value) => document.querySelector('.current-url')?.value === value,
|
||||
secret,
|
||||
@@ -218,7 +223,7 @@ try {
|
||||
window.__hopClipboardDenied = true;
|
||||
});
|
||||
await sender.locator('.peer-card').first().click();
|
||||
await sender.getByRole('button', { name: '파일 선택', exact: true }).waitFor();
|
||||
await sender.getByLabel('보낼 텍스트', { exact: true }).waitFor();
|
||||
assert.equal(await sender.locator('.clipboard-link-card').count(), 0);
|
||||
const fileBytes = Buffer.from('브라우저 파일 전달 검증');
|
||||
const dragged = await sender.evaluateHandle(() => {
|
||||
@@ -326,16 +331,29 @@ try {
|
||||
window.__hopClipboard = '일반 텍스트는 전송하지 않습니다.';
|
||||
});
|
||||
await second.locator('.peer-card').filter({ hasText: receiverCode }).click();
|
||||
await second.getByRole('button', { name: '파일 선택', exact: true }).waitFor();
|
||||
assert.equal(
|
||||
await second.locator('.clipboard-link-card').count(),
|
||||
0,
|
||||
'일반 텍스트를 링크로 제안하면 안 됨',
|
||||
);
|
||||
await second.getByRole('button', { name: '대신 링크 붙여넣기', exact: true }).click();
|
||||
await second.getByLabel('보낼 텍스트', { exact: true }).waitFor();
|
||||
const urls = ['https://example.com/path?x=1', 'http://192.168.1.10/private'];
|
||||
await second.getByRole('textbox', { name: '보낼 URL' }).fill(urls.join('\n'));
|
||||
await second.getByRole('button', { name: '전송하기', exact: true }).click();
|
||||
await second.getByRole('button', { name: '전송 화면 닫기', exact: true }).click();
|
||||
// Old API callers may still send multiple URLs; the new clipboard UI treats these as text.
|
||||
const sourceCookie = (await a.cookies(url)).find((cookie) => cookie.name === 'hop_guest');
|
||||
const targetCookie = (await c.cookies(url)).find((cookie) => cookie.name === 'hop_guest');
|
||||
const targetGuest = runtime.service.guest(targetCookie.value);
|
||||
const legacyHeaders = {
|
||||
Origin: url,
|
||||
'X-CSRF-Token': runtime.service.csrf(sourceCookie.value),
|
||||
'Idempotency-Key': crypto.randomUUID(),
|
||||
};
|
||||
const legacyRequest = await a.request.post(`${url}/api/transfer-requests`, {
|
||||
headers: legacyHeaders,
|
||||
data: {
|
||||
targetDeviceId: runtime.service.device(targetGuest.id).id,
|
||||
kind: 'url',
|
||||
count: 2,
|
||||
byteSize: Buffer.byteLength(urls.join('\n')),
|
||||
},
|
||||
});
|
||||
assert.equal(legacyRequest.status(), 200);
|
||||
const legacyId = (await legacyRequest.json()).requestId;
|
||||
await urlReceiver.getByRole('button', { name: '수락', exact: true }).waitFor();
|
||||
assert(!(await urlReceiver.locator('body').innerText()).includes(urls[0]));
|
||||
await c.route('https://example.com/**', (route) =>
|
||||
@@ -347,9 +365,11 @@ try {
|
||||
const opened = [];
|
||||
urlReceiver.on('popup', (page) => opened.push(page));
|
||||
await urlReceiver.getByRole('button', { name: '수락', exact: true }).click();
|
||||
await second
|
||||
.getByRole('button', { name: '전송 취소', exact: false })
|
||||
.waitFor({ state: 'hidden' });
|
||||
const legacyComplete = await a.request.post(`${url}/api/transfers/${legacyId}/complete`, {
|
||||
headers: { ...legacyHeaders, 'Idempotency-Key': crypto.randomUUID() },
|
||||
data: { urls },
|
||||
});
|
||||
assert.equal(legacyComplete.status(), 200);
|
||||
assert.equal(opened.length, 2);
|
||||
await Promise.all(
|
||||
opened.map((page) => page.waitForURL((address) => urls.includes(address.href))),
|
||||
@@ -365,8 +385,8 @@ try {
|
||||
window.open = () => null;
|
||||
});
|
||||
await second.locator('.peer-card').filter({ hasText: receiverCode }).click();
|
||||
await second.getByRole('button', { name: '대신 링크 붙여넣기', exact: true }).click();
|
||||
await second.getByRole('textbox', { name: '보낼 URL' }).fill('https://example.com/blocked');
|
||||
|
||||
await second.locator('.current-url, .current-text').fill('https://example.com/blocked');
|
||||
await second.getByRole('button', { name: '전송하기', exact: true }).click();
|
||||
await urlReceiver.getByRole('button', { name: '수락', exact: true }).click();
|
||||
await urlReceiver.locator('.delivery-blocked').waitFor();
|
||||
@@ -405,7 +425,7 @@ try {
|
||||
await second.locator('.device-name').filter({ hasText: '재연결된 Mac' }).waitFor();
|
||||
assert.deepEqual(errors, []);
|
||||
console.log(
|
||||
'PASS: 두 브라우저 QR·코드 연결, 수락 전 본문 비공개, 클립보드 URL·복수 URL 전달과 일반 텍스트 무시, 수락 후 자동 새 탭 URL 열기, opener 격리, 읽음 동기화, 이름 유지, 다중 탭·CSRF 재연결 복구, 390px 가로 넘침 없음, 실제 파일 업로드·자동 다운로드, 수락 중 지연된 과거 조회에도 전달 완료, 스크롤 뒤 중앙의 고대비 수락 팝업, 검정/라이트 전환, 버튼 자리의 전송 취소·익명 내역 숨김·새로고침 중복 실행 방지.',
|
||||
'PASS: 두 브라우저 QR·코드 연결, 수락 전 본문 비공개, 클립보드 URL·기존 복수 URL API 호환과 클립보드 입력, 수락 후 자동 새 탭 URL 열기, opener 격리, 읽음 동기화, 이름 유지, 다중 탭·CSRF 재연결 복구, 390px 가로 넘침 없음, 실제 파일 업로드·자동 다운로드, 수락 중 지연된 과거 조회에도 전달 완료, 스크롤 뒤 중앙의 고대비 수락 팝업, 검정/라이트 전환, 버튼 자리의 전송 취소·익명 내역 숨김·새로고침 중복 실행 방지.',
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
Reference in New Issue
Block a user