Files
AppFlowy-Web/cypress/e2e/account/avatar/avatar-api.cy.ts
2025-11-17 13:19:36 +08:00

195 lines
7.6 KiB
TypeScript

import { avatarTestUtils } from './avatar-test-utils';
import { AccountSelectors, AvatarUiSelectors } from '../../../support/selectors';
import { testLog } from '../../../support/test-helpers';
const { generateRandomEmail, setupBeforeEach, imports } = avatarTestUtils;
const { updateUserMetadata, AuthTestUtils, AvatarSelectors, WorkspaceSelectors } = imports;
describe('Avatar API', () => {
beforeEach(() => {
setupBeforeEach();
});
describe('Avatar Upload via API', () => {
it('should update avatar URL via API and display in UI', () => {
const testEmail = generateRandomEmail();
const authUtils = new AuthTestUtils();
const testAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=test';
testLog.info( 'Step 1: Visit login page');
cy.visit('/login', { failOnStatusCode: false });
cy.wait(2000);
testLog.info( 'Step 2: Sign in with test account');
authUtils.signInWithTestUrl(testEmail);
cy.url({ timeout: 30000 }).should('include', '/app');
cy.wait(3000);
testLog.info( 'Step 3: Update avatar via API');
updateUserMetadata(testAvatarUrl).then((response) => {
testLog.info( `API Response: ${JSON.stringify(response)}`);
expect(response.status).to.equal(200);
});
testLog.info( 'Step 4: Reload page to see updated avatar');
cy.reload();
cy.wait(3000);
testLog.info( 'Step 5: Open Account Settings to verify avatar');
WorkspaceSelectors.dropdownTrigger().click();
cy.wait(1000);
AccountSelectors.settingsButton().click();
AvatarSelectors.accountSettingsDialog().should('be.visible');
testLog.info( 'Step 6: Verify avatar image is displayed in Account Settings');
// Note: Account Settings dialog may not display avatar directly
// The avatar is displayed via getUserIconUrl which prioritizes workspace member avatar
// Since we updated user metadata (icon_url), it should be available
// But the actual display location might be in the workspace dropdown or elsewhere
// Wait for any avatar image to be present and loaded
// The AvatarImage component loads asynchronously and sets opacity to 0 while loading
AvatarUiSelectors.image()
.should('exist')
.should(($imgs) => {
// Find the first visible avatar image (opacity not 0)
let foundVisible = false;
$imgs.each((index, img) => {
const $img = Cypress.$(img);
const opacity = $img.css('opacity');
const src = $img.attr('src');
if (opacity !== '0' && src && src.length > 0) {
foundVisible = true;
return false; // break
}
});
expect(foundVisible, 'At least one avatar image should be visible').to.be.true;
});
// Verify that the avatar image has loaded (check for non-empty src and visible state)
AvatarUiSelectors.image().then(($imgs) => {
let foundLoaded = false;
$imgs.each((index, img) => {
const $img = Cypress.$(img);
const opacity = parseFloat($img.css('opacity') || '0');
const src = $img.attr('src') || '';
if (opacity > 0 && src.length > 0) {
foundLoaded = true;
testLog.info( `Found loaded avatar image with src: ${src.substring(0, 50)}...`);
return false; // break
}
});
expect(foundLoaded, 'At least one avatar image should be loaded and visible').to.be.true;
});
});
it('test direct API call', () => {
const testEmail = generateRandomEmail();
const authUtils = new AuthTestUtils();
const testAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=test';
testLog.info( '========== Step 1: Visit login page ==========');
cy.visit('/login', { failOnStatusCode: false });
cy.wait(2000);
testLog.info( '========== Step 2: Sign in with test account ==========');
authUtils.signInWithTestUrl(testEmail);
cy.url({ timeout: 30000 }).should('include', '/app');
cy.wait(3000);
testLog.info( '========== Step 3: Get token from localStorage ==========');
cy.window()
.its('localStorage')
.invoke('getItem', 'token')
.then((tokenStr) => {
testLog.info( `Token string: ${tokenStr ? 'Found' : 'Not found'}`);
const token = JSON.parse(tokenStr);
const accessToken = token.access_token;
testLog.info( `Access token: ${accessToken ? 'Present (length: ' + accessToken.length + ')' : 'Missing'}`);
});
testLog.info( '========== Step 4: Making API request ==========');
testLog.info( `URL: ${avatarTestUtils.APPFLOWY_BASE_URL}/api/user/update`);
testLog.info( `Avatar URL: ${testAvatarUrl}`);
updateUserMetadata(testAvatarUrl).then((response) => {
testLog.info( '========== Step 5: Checking response ==========');
testLog.info( `Response is null: ${response === null}`);
testLog.info( `Response type: ${typeof response}`);
testLog.info( `Response status: ${response?.status}`);
testLog.info( `Response body: ${JSON.stringify(response?.body)}`);
testLog.info( `Response headers: ${JSON.stringify(response?.headers)}`);
expect(response).to.not.be.null;
expect(response.status).to.equal(200);
if (response.body) {
testLog.info( `Response body code: ${response.body.code}`);
testLog.info( `Response body message: ${response.body.message}`);
}
});
});
it('should display emoji as avatar via API', () => {
const testEmail = generateRandomEmail();
const authUtils = new AuthTestUtils();
const testEmoji = '🎨';
testLog.info( 'Step 1: Visit login page');
cy.visit('/login', { failOnStatusCode: false });
cy.wait(2000);
testLog.info( 'Step 2: Sign in with test account');
authUtils.signInWithTestUrl(testEmail);
cy.url({ timeout: 30000 }).should('include', '/app');
cy.wait(3000);
testLog.info( 'Step 3: Update avatar to emoji via API');
updateUserMetadata(testEmoji).then((response) => {
expect(response).to.not.be.null;
expect(response.status).to.equal(200);
});
testLog.info( 'Step 4: Reload page');
cy.reload();
cy.wait(3000);
testLog.info( 'Step 5: Open Account Settings');
WorkspaceSelectors.dropdownTrigger().click();
cy.wait(1000);
AccountSelectors.settingsButton().click();
AvatarSelectors.accountSettingsDialog().should('be.visible');
testLog.info( 'Step 6: Verify emoji is displayed in fallback');
AvatarSelectors.avatarFallback().should('contain.text', testEmoji);
});
it('should display fallback character when no avatar is set', () => {
const testEmail = generateRandomEmail();
const authUtils = new AuthTestUtils();
testLog.info( 'Step 1: Visit login page');
cy.visit('/login', { failOnStatusCode: false });
cy.wait(2000);
testLog.info( 'Step 2: Sign in with test account (no avatar set)');
authUtils.signInWithTestUrl(testEmail).then(() => {
cy.url({ timeout: 30000 }).should('include', '/app');
cy.wait(3000);
testLog.info( 'Step 3: Open workspace dropdown to see avatar');
WorkspaceSelectors.dropdownTrigger().click();
cy.wait(500);
testLog.info( 'Step 4: Verify fallback is displayed in workspace dropdown avatar');
AvatarSelectors.workspaceDropdownAvatar().within(() => {
AvatarSelectors.avatarFallback().should('be.visible');
});
});
});
});
});