From 46efabd184fc5eb4c9455094debebc69231fcb18 Mon Sep 17 00:00:00 2001 From: dreamhunter2333 Date: Sun, 23 Aug 2026 19:41:04 +0800 Subject: [PATCH] fix: address user mail review feedback --- CHANGELOG.md | 2 +- CHANGELOG_EN.md | 2 +- e2e/tests/api/user-send-mail.spec.ts | 29 ++------------ e2e/tests/browser/user-send-mail.spec.ts | 39 +++++++++++-------- frontend/src/views/index/SendMail.vue | 6 +-- frontend/src/views/user/UserMailClient.vue | 5 +-- .../docs/en/guide/feature/send-mail-api.md | 2 - .../docs/zh/guide/feature/send-mail-api.md | 2 - worker/src/mails_api/send_mail_api.ts | 12 ++---- worker/src/user_api/index.ts | 2 - worker/src/user_api/user_send_mail_api.ts | 31 +++------------ 11 files changed, 39 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aefd6c..62ff9f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ - test: |E2E| 覆盖 D1 数据库大小响应、配置键隔离,以及数据库页面套餐选择的持久化与刷新恢复 - fix: |E2E| 覆盖发信页面草稿编辑、正文格式切换及 HTML 预览 -- test: |E2E| 覆盖用户 JWT 发信接口的地址归属、额度扣减、实际投递和发件箱操作,以及用户中心查看地址凭证、切换发件地址和按地址过滤发件箱的完整流程 +- fix: |E2E| 覆盖用户 JWT 发信接口的地址归属、额度扣减、实际投递和发件箱操作,以及用户中心查看地址凭证、切换发件地址和按地址过滤发件箱的完整流程 ## v1.11.0 diff --git a/CHANGELOG_EN.md b/CHANGELOG_EN.md index 0c1cccd..7f386b8 100644 --- a/CHANGELOG_EN.md +++ b/CHANGELOG_EN.md @@ -26,7 +26,7 @@ - test: |E2E| Cover the D1 database-size response, config-key isolation, and persistence of the database-page plan selection across reloads - fix: |E2E| Cover draft editing, content-format switching, and HTML preview in the send-mail composer -- test: |E2E| Cover address ownership, balance decrement, delivery, and sent-item operations through the User JWT API, plus user-center credential display, sender switching, and sent-item filtering by address +- fix: |E2E| Cover address ownership, balance decrement, delivery, and sent-item operations through the User JWT API, plus user-center credential display, sender switching, and sent-item filtering by address ## v1.11.0 diff --git a/e2e/tests/api/user-send-mail.spec.ts b/e2e/tests/api/user-send-mail.spec.ts index c65ab5e..27cdd56 100644 --- a/e2e/tests/api/user-send-mail.spec.ts +++ b/e2e/tests/api/user-send-mail.spec.ts @@ -64,7 +64,7 @@ test.describe('User send mail API', () => { userId = user.userId; const bound = await createTestAddress(request, 'user-send-bound-'); const accessRequest = await createTestAddress(request, 'user-send-access-'); - const outsider = await createTestAddress(request, 'user-send-outsider-'); + const outsider = await createTestAddress(request, 'usr-outsider-'); addresses.push(bound, accessRequest, outsider); await bindAddress(request, user.jwt, bound.jwt); await bindAddress(request, user.jwt, accessRequest.jwt); @@ -166,12 +166,6 @@ test.describe('User send mail API', () => { )); expect(outsiderMail).toBeTruthy(); - const unauthorizedAddressDeleteRes = await request.delete( - `${WORKER_URL}/user_api/address/${outsider.address_id}/sendbox/${outsiderMail.id}`, - { headers: { 'x-user-token': user.jwt } }, - ); - expect(unauthorizedAddressDeleteRes.status()).toBe(400); - const unauthorizedDeleteRes = await request.delete( `${WORKER_URL}/user_api/sendbox/${outsiderMail.id}`, { headers: { 'x-user-token': user.jwt } }, @@ -189,16 +183,6 @@ test.describe('User send mail API', () => { ); expect((await updatedSettingsRes.json()).send_balance).toBe(9); - const sendboxRes = await request.get( - `${WORKER_URL}/user_api/address/${bound.address_id}/sendbox?limit=20&offset=0`, - { headers: { 'x-user-token': user.jwt } }, - ); - expect(sendboxRes.ok()).toBe(true); - const sendbox = await sendboxRes.json(); - expect(sendbox.count).toBe(1); - expect(sendbox.results).toHaveLength(1); - expect(JSON.parse(sendbox.results[0].raw).subject).toBe(subject); - const userSendboxRes = await request.get( `${WORKER_URL}/user_api/sendbox?limit=20&offset=0`, { headers: { 'x-user-token': user.jwt } }, @@ -207,6 +191,7 @@ test.describe('User send mail API', () => { const userSendbox = await userSendboxRes.json(); expect(userSendbox.count).toBe(1); expect(userSendbox.results).toHaveLength(1); + expect(JSON.parse(userSendbox.results[0].raw).subject).toBe(subject); const filteredSendboxRes = await request.get( `${WORKER_URL}/user_api/sendbox?limit=20&offset=0&address=${encodeURIComponent(bound.address)}`, @@ -222,20 +207,14 @@ test.describe('User send mail API', () => { expect(outsiderFilterRes.ok()).toBe(true); expect((await outsiderFilterRes.json()).count).toBe(0); - const outsiderSendboxRes = await request.get( - `${WORKER_URL}/user_api/address/${outsider.address_id}/sendbox?limit=20&offset=0`, - { headers: { 'x-user-token': user.jwt } }, - ); - expect(outsiderSendboxRes.status()).toBe(400); - const deleteRes = await request.delete( - `${WORKER_URL}/user_api/sendbox/${sendbox.results[0].id}`, + `${WORKER_URL}/user_api/sendbox/${userSendbox.results[0].id}`, { headers: { 'x-user-token': user.jwt } }, ); expect(deleteRes.ok()).toBe(true); const emptySendboxRes = await request.get( - `${WORKER_URL}/user_api/address/${bound.address_id}/sendbox?limit=20&offset=0`, + `${WORKER_URL}/user_api/sendbox?limit=20&offset=0`, { headers: { 'x-user-token': user.jwt } }, ); const emptySendbox = await emptySendboxRes.json(); diff --git a/e2e/tests/browser/user-send-mail.spec.ts b/e2e/tests/browser/user-send-mail.spec.ts index 9e97faa..e8c2359 100644 --- a/e2e/tests/browser/user-send-mail.spec.ts +++ b/e2e/tests/browser/user-send-mail.spec.ts @@ -28,7 +28,7 @@ async function createUser(request: APIRequestContext) { test.describe('User send mail page', () => { test('selects a bound address, sends mail, and opens its sent items', async ({ page }) => { const request = await apiRequest.newContext(); - let address: Awaited> | undefined; + const addresses: Awaited>[] = []; let userId: number | undefined; let originalUserSettings: Record | undefined; @@ -48,14 +48,18 @@ test.describe('User send mail page', () => { const user = await createUser(request); userId = user.userId; - address = await createTestAddress(request, 'user-send-browser-address-'); - const bindRes = await request.post(`${WORKER_URL}/user_api/bind_address`, { - headers: { - Authorization: `Bearer ${address.jwt}`, - 'x-user-token': user.jwt, - }, - }); - expect(bindRes.ok()).toBe(true); + const address = await createTestAddress(request, 'usr-browser-'); + const secondAddress = await createTestAddress(request, 'usr-second-'); + addresses.push(address, secondAddress); + for (const boundAddress of addresses) { + const bindRes = await request.post(`${WORKER_URL}/user_api/bind_address`, { + headers: { + Authorization: `Bearer ${boundAddress.jwt}`, + 'x-user-token': user.jwt, + }, + }); + expect(bindRes.ok()).toBe(true); + } await page.goto(`${FRONTEND_URL}/en/`); await page.evaluate((userJwt) => { @@ -67,7 +71,7 @@ test.describe('User send mail page', () => { const credentialResponse = page.waitForResponse((response) => ( response.request().method() === 'GET' && new URL(response.url()).pathname - === `/user_api/bind_address_jwt/${address!.address_id}` + === `/user_api/bind_address_jwt/${address.address_id}` )); const addressRow = page.getByRole('row').filter({ hasText: address.address }); await addressRow.getByRole('button', { name: 'Address Credential' }).click(); @@ -75,15 +79,16 @@ test.describe('User send mail page', () => { await expect(page.getByRole('dialog')).toContainText(address.address); await page.getByRole('button', { name: 'close' }).click(); + await page.getByText('Send Mail', { exact: true }).click(); + await expect(page.getByRole('heading', { name: 'Compose email', exact: true })).toBeVisible(); + const settingsResponse = page.waitForResponse((response) => ( new URL(response.url()).pathname - === `/user_api/address/${address!.address_id}/settings` + === `/user_api/address/${address.address_id}/settings` )); - await page.getByText('Send Mail', { exact: true }).click(); + await page.locator('.address-picker-select').click(); + await page.locator('.n-base-select-option').filter({ hasText: address.address }).click(); expect((await settingsResponse).ok()).toBe(true); - - await expect(page.getByRole('heading', { name: 'Compose email', exact: true })).toBeVisible(); - await expect(page.locator('.composer-title')).toContainText(address.address); await expect(page.locator('.address-picker-select')).toContainText(address.address); const subject = `Browser user send ${Date.now()}`; @@ -95,7 +100,7 @@ test.describe('User send mail page', () => { const sendResponse = page.waitForResponse((response) => ( response.request().method() === 'POST' && new URL(response.url()).pathname - === `/user_api/address/${address!.address_id}/send_mail` + === `/user_api/address/${address.address_id}/send_mail` )); const sendboxResponse = page.waitForResponse((response) => ( response.request().method() === 'GET' @@ -109,7 +114,7 @@ test.describe('User send mail page', () => { await expect(page.getByText(subject, { exact: true })).toBeVisible({ timeout: 15_000 }); } finally { try { - if (address) await deleteAddress(request, address.jwt); + await Promise.allSettled(addresses.map((address) => deleteAddress(request, address.jwt))); if (userId !== undefined) { await request.delete(`${WORKER_URL}/admin/users/${userId}`); } diff --git a/frontend/src/views/index/SendMail.vue b/frontend/src/views/index/SendMail.vue index ad13ba9..b710f56 100644 --- a/frontend/src/views/index/SendMail.vue +++ b/frontend/src/views/index/SendMail.vue @@ -26,10 +26,6 @@ const props = defineProps({ type: Number, default: 0, }, - userAddressMode: { - type: Boolean, - default: false, - }, addressOptions: { type: Array, default: () => [], @@ -50,7 +46,7 @@ const { const { t } = useScopedI18n('views.index.SendMail') -const isUserAddressMode = computed(() => props.userAddressMode || props.addressId > 0) +const isUserAddressMode = computed(() => props.addressId > 0) const mailSettings = computed(() => ( isUserAddressMode.value ? userAddressSettings.value : settings.value )) diff --git a/frontend/src/views/user/UserMailClient.vue b/frontend/src/views/user/UserMailClient.vue index 8ff8150..84ee063 100644 --- a/frontend/src/views/user/UserMailClient.vue +++ b/frontend/src/views/user/UserMailClient.vue @@ -97,10 +97,9 @@ onMounted(fetchAddresses)