From 0e4d3ef663f56aede55479393346f9955989445d Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 13 Feb 2026 09:15:01 +0100 Subject: [PATCH] test(e2e): add Playwright test for avatar cache invalidation Uploads two different avatars in sequence and verifies the header avatar src changes after the second upload. This confirms both the backend cache (via DelPrefix) and the frontend cache are properly invalidated when a new avatar is uploaded. --- frontend/tests/e2e/user/settings.spec.ts | 48 +++++++++++++++++++++++ frontend/tests/fixtures/image-blue.png | Bin 0 -> 683 bytes 2 files changed, 48 insertions(+) create mode 100644 frontend/tests/fixtures/image-blue.png diff --git a/frontend/tests/e2e/user/settings.spec.ts b/frontend/tests/e2e/user/settings.spec.ts index 4e8749ea2..44637ca86 100644 --- a/frontend/tests/e2e/user/settings.spec.ts +++ b/frontend/tests/e2e/user/settings.spec.ts @@ -40,6 +40,54 @@ test.describe('User Settings', () => { await expect(page.locator('.global-notification')).toContainText('Success', {timeout: 10000}) }) + test('Invalidates avatar cache when uploading a new avatar', async ({authenticatedPage: page}) => { + await page.goto('/user/settings/avatar') + await page.waitForLoadState('networkidle') + + const uploadRadio = page.locator('input[name=avatarProvider][value=upload]') + await expect(uploadRadio).toBeVisible({timeout: 5000}) + await uploadRadio.click() + + const fileInput = page.locator('input[type=file]') + const uploadButton = page.locator('[data-cy="uploadAvatar"]') + const headerAvatar = page.locator('.username-dropdown-trigger img.avatar') + const notification = page.locator('.global-notification') + + // Upload first avatar (image.jpg) + await fileInput.setInputFiles('tests/fixtures/image.jpg') + await expect(uploadButton).toBeEnabled({timeout: 10000}) + + const firstUploadPromise = page.waitForResponse(response => + response.url().includes('avatar') && response.request().method() === 'PUT', + ) + await uploadButton.click() + const firstResponse = await firstUploadPromise + expect(firstResponse.ok()).toBe(true) + await expect(notification).toContainText('Success', {timeout: 10000}) + + // Wait for the header avatar to update and capture its src + await expect(headerAvatar).toHaveAttribute('src', /blob:|data:/, {timeout: 10000}) + const firstAvatarSrc = await headerAvatar.getAttribute('src') + + // Wait for the notification to disappear before uploading again + await expect(notification).not.toBeVisible({timeout: 10000}) + + // Upload second avatar (image-blue.png) + await fileInput.setInputFiles('tests/fixtures/image-blue.png') + await expect(uploadButton).toBeEnabled({timeout: 10000}) + + const secondUploadPromise = page.waitForResponse(response => + response.url().includes('avatar') && response.request().method() === 'PUT', + ) + await uploadButton.click() + const secondResponse = await secondUploadPromise + expect(secondResponse.ok()).toBe(true) + await expect(notification).toContainText('Success', {timeout: 10000}) + + // Verify the header avatar changed to a different blob URL + await expect(headerAvatar).not.toHaveAttribute('src', firstAvatarSrc!, {timeout: 10000}) + }) + test('Updates the name', async ({authenticatedPage: page}) => { await page.goto('/user/settings/general') await page.waitForLoadState('networkidle') diff --git a/frontend/tests/fixtures/image-blue.png b/frontend/tests/fixtures/image-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..35fdedb9a57245cbd398fb8e5ac31fa11aeaa1cb GIT binary patch literal 683 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k2}mkgS)OEIU@G!-aSW-L^Y$DgCxd~&kqw{o wtAam@cquY%daKI*!*R+e8Vv!ehCqv5`MEXMHkvVH0TUsEr>mdKI;Vst0DGl>2LJ#7 literal 0 HcmV?d00001