diff --git a/cypress/e2e/account/avatar/avatar-api.cy.ts b/cypress/e2e/account/avatar/avatar-api.cy.ts index ab8702c6..96f68e20 100644 --- a/cypress/e2e/account/avatar/avatar-api.cy.ts +++ b/cypress/e2e/account/avatar/avatar-api.cy.ts @@ -1,5 +1,6 @@ import { avatarTestUtils } from './avatar-test-utils'; import { byTestId } from '../../../support/selectors'; +import { testLog } from '../../../support/test-helpers'; const { generateRandomEmail, setupBeforeEach, imports } = avatarTestUtils; const { updateUserMetadata, AuthTestUtils, AvatarSelectors, WorkspaceSelectors } = imports; @@ -15,33 +16,33 @@ describe('Avatar API', () => { const authUtils = new AuthTestUtils(); const testAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=test'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail); cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Update avatar via API'); + testLog.info( 'Step 3: Update avatar via API'); updateUserMetadata(testAvatarUrl).then((response) => { - cy.task('log', `API Response: ${JSON.stringify(response)}`); + testLog.info( `API Response: ${JSON.stringify(response)}`); expect(response.status).to.equal(200); }); - cy.task('log', 'Step 4: Reload page to see updated avatar'); + testLog.info( 'Step 4: Reload page to see updated avatar'); cy.reload(); cy.wait(3000); - cy.task('log', 'Step 5: Open Account Settings to verify avatar'); + testLog.info( 'Step 5: Open Account Settings to verify avatar'); WorkspaceSelectors.dropdownTrigger().click(); cy.wait(1000); cy.get(byTestId('account-settings-button')).click(); AvatarSelectors.accountSettingsDialog().should('be.visible'); - cy.task('log', 'Step 6: Verify avatar image is displayed in Account Settings'); + 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 @@ -76,7 +77,7 @@ describe('Avatar API', () => { if (opacity > 0 && src.length > 0) { foundLoaded = true; - cy.task('log', `Found loaded avatar image with src: ${src.substring(0, 50)}...`); + testLog.info( `Found loaded avatar image with src: ${src.substring(0, 50)}...`); return false; // break } }); @@ -89,44 +90,44 @@ describe('Avatar API', () => { const authUtils = new AuthTestUtils(); const testAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=test'; - cy.task('log', '========== Step 1: Visit login page =========='); + testLog.info( '========== Step 1: Visit login page =========='); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', '========== Step 2: Sign in with test account =========='); + testLog.info( '========== Step 2: Sign in with test account =========='); authUtils.signInWithTestUrl(testEmail); cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', '========== Step 3: Get token from localStorage =========='); + testLog.info( '========== Step 3: Get token from localStorage =========='); cy.window() .its('localStorage') .invoke('getItem', 'token') .then((tokenStr) => { - cy.task('log', `Token string: ${tokenStr ? 'Found' : 'Not found'}`); + testLog.info( `Token string: ${tokenStr ? 'Found' : 'Not found'}`); const token = JSON.parse(tokenStr); const accessToken = token.access_token; - cy.task('log', `Access token: ${accessToken ? 'Present (length: ' + accessToken.length + ')' : 'Missing'}`); + testLog.info( `Access token: ${accessToken ? 'Present (length: ' + accessToken.length + ')' : 'Missing'}`); }); - cy.task('log', '========== Step 4: Making API request =========='); - cy.task('log', `URL: ${avatarTestUtils.APPFLOWY_BASE_URL}/api/user/update`); - cy.task('log', `Avatar URL: ${testAvatarUrl}`); + 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) => { - cy.task('log', '========== Step 5: Checking response =========='); - cy.task('log', `Response is null: ${response === null}`); - cy.task('log', `Response type: ${typeof response}`); - cy.task('log', `Response status: ${response?.status}`); - cy.task('log', `Response body: ${JSON.stringify(response?.body)}`); - cy.task('log', `Response headers: ${JSON.stringify(response?.headers)}`); + 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) { - cy.task('log', `Response body code: ${response.body.code}`); - cy.task('log', `Response body message: ${response.body.message}`); + testLog.info( `Response body code: ${response.body.code}`); + testLog.info( `Response body message: ${response.body.message}`); } }); }); @@ -136,33 +137,33 @@ describe('Avatar API', () => { const authUtils = new AuthTestUtils(); const testEmoji = '🎨'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail); cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Update avatar to emoji via API'); + 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); }); - cy.task('log', 'Step 4: Reload page'); + testLog.info( 'Step 4: Reload page'); cy.reload(); cy.wait(3000); - cy.task('log', 'Step 5: Open Account Settings'); + testLog.info( 'Step 5: Open Account Settings'); WorkspaceSelectors.dropdownTrigger().click(); cy.wait(1000); cy.get(byTestId('account-settings-button')).click(); AvatarSelectors.accountSettingsDialog().should('be.visible'); - cy.task('log', 'Step 6: Verify emoji is displayed in fallback'); + testLog.info( 'Step 6: Verify emoji is displayed in fallback'); AvatarSelectors.avatarFallback().should('contain.text', testEmoji); }); @@ -170,20 +171,20 @@ describe('Avatar API', () => { const testEmail = generateRandomEmail(); const authUtils = new AuthTestUtils(); - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account (no avatar set)'); + 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); - cy.task('log', 'Step 3: Open workspace dropdown to see avatar'); + testLog.info( 'Step 3: Open workspace dropdown to see avatar'); WorkspaceSelectors.dropdownTrigger().click(); cy.wait(500); - cy.task('log', 'Step 4: Verify fallback is displayed in workspace dropdown avatar'); + testLog.info( 'Step 4: Verify fallback is displayed in workspace dropdown avatar'); AvatarSelectors.workspaceDropdownAvatar().within(() => { AvatarSelectors.avatarFallback().should('be.visible'); }); diff --git a/cypress/e2e/account/avatar/avatar-database.cy.ts b/cypress/e2e/account/avatar/avatar-database.cy.ts index 8fdd9122..3d69fdd1 100644 --- a/cypress/e2e/account/avatar/avatar-database.cy.ts +++ b/cypress/e2e/account/avatar/avatar-database.cy.ts @@ -1,4 +1,5 @@ import { avatarTestUtils } from './avatar-test-utils'; +import { testLog } from '../../../support/test-helpers'; const { generateRandomEmail, setupBeforeEach, imports } = avatarTestUtils; const { updateWorkspaceMemberAvatar, AuthTestUtils, dbUtils } = imports; @@ -14,16 +15,16 @@ describe('Avatar Database', () => { const authUtils = new AuthTestUtils(); const testAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=db-test'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Set avatar via API'); + testLog.info( 'Step 3: Set avatar via API'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; @@ -33,7 +34,7 @@ describe('Avatar Database', () => { cy.wait(3000); - cy.task('log', 'Step 4: Verify avatar is stored in database'); + testLog.info( 'Step 4: Verify avatar is stored in database'); dbUtils.getCurrentUserUuid().then((userUuid) => { expect(userUuid).to.not.be.null; diff --git a/cypress/e2e/account/avatar/avatar-header.cy.ts b/cypress/e2e/account/avatar/avatar-header.cy.ts index 23e0b0d5..246bbcd6 100644 --- a/cypress/e2e/account/avatar/avatar-header.cy.ts +++ b/cypress/e2e/account/avatar/avatar-header.cy.ts @@ -1,5 +1,6 @@ import { avatarTestUtils } from './avatar-test-utils'; import { byTestIdContains } from '../../../support/selectors'; +import { testLog } from '../../../support/test-helpers'; const { generateRandomEmail, setupBeforeEach, imports } = avatarTestUtils; const { APP_EVENTS, updateWorkspaceMemberAvatar, AuthTestUtils, AvatarSelectors, dbUtils } = imports; @@ -15,16 +16,16 @@ describe('Avatar Header Display', () => { const authUtils = new AuthTestUtils(); const testAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=header-test'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Set avatar via workspace member profile API'); + testLog.info( 'Step 3: Set avatar via workspace member profile API'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; @@ -36,7 +37,7 @@ describe('Avatar Header Display', () => { cy.reload(); cy.wait(3000); - cy.task('log', 'Step 4: Interact with editor to trigger collaborative user awareness'); + testLog.info( 'Step 4: Interact with editor to trigger collaborative user awareness'); // Click on a page to open editor cy.get('body').then(($body) => { // Try to find and click on a page in the sidebar @@ -74,13 +75,13 @@ describe('Avatar Header Display', () => { cy.wait(2000); - cy.task('log', 'Step 5: Verify avatar appears in header top right corner'); + testLog.info( 'Step 5: Verify avatar appears in header top right corner'); // Wait for header to be visible cy.get('.appflowy-top-bar').should('be.visible'); // Check if avatar container exists in header (collaborative users area) // The current user's avatar will appear there when they're actively editing - cy.task('log', 'Header avatar area should be visible'); + testLog.info( 'Header avatar area should be visible'); AvatarSelectors.headerAvatarContainer().should('exist'); // Verify avatar image or fallback is present @@ -94,16 +95,16 @@ describe('Avatar Header Display', () => { const authUtils = new AuthTestUtils(); const testEmoji = '🎨'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Set emoji avatar via API'); + testLog.info( 'Step 3: Set emoji avatar via API'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; @@ -115,7 +116,7 @@ describe('Avatar Header Display', () => { cy.reload(); cy.wait(3000); - cy.task('log', 'Step 4: Interact with editor to trigger collaborative user awareness'); + testLog.info( 'Step 4: Interact with editor to trigger collaborative user awareness'); // Click on a page to open editor cy.get('body').then(($body) => { if ($body.find(byTestIdContains('page')).length > 0) { @@ -151,12 +152,12 @@ describe('Avatar Header Display', () => { cy.wait(2000); - cy.task('log', 'Step 5: Verify emoji appears in header avatar fallback'); + testLog.info( 'Step 5: Verify emoji appears in header avatar fallback'); cy.get('.appflowy-top-bar').should('be.visible'); // When user is actively editing, their avatar should appear in header // Emoji avatars show in fallback - cy.task('log', 'Header should be visible with avatar area'); + testLog.info( 'Header should be visible with avatar area'); AvatarSelectors.headerAvatarContainer().should('exist'); // Verify emoji appears in fallback @@ -171,23 +172,23 @@ describe('Avatar Header Display', () => { const authUtils = new AuthTestUtils(); const testAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=header-notification'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Get user UUID and workspace ID'); + testLog.info( 'Step 3: Get user UUID and workspace ID'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; dbUtils.getCurrentUserUuid().then((userUuid) => { expect(userUuid).to.not.be.null; - cy.task('log', 'Step 4: Simulate workspace member profile changed notification'); + testLog.info( 'Step 4: Simulate workspace member profile changed notification'); cy.window().then((win) => { const emitter = (win as typeof window & { __APPFLOWY_EVENT_EMITTER__?: { emit: (...args: unknown[]) => void }; @@ -205,13 +206,13 @@ describe('Avatar Header Display', () => { cy.wait(2000); - cy.task('log', 'Step 5: Verify avatar is updated in database'); + testLog.info( 'Step 5: Verify avatar is updated in database'); dbUtils.getWorkspaceMemberProfile(workspaceId!, userUuid!).then((profile) => { expect(profile).to.not.be.null; expect(profile?.avatar_url).to.equal(testAvatarUrl); }); - cy.task('log', 'Step 6: Interact with editor to trigger collaborative user awareness'); + testLog.info( 'Step 6: Interact with editor to trigger collaborative user awareness'); // Click on a page to open editor cy.get('body').then(($body) => { if ($body.find(byTestIdContains('page')).length > 0) { @@ -247,7 +248,7 @@ describe('Avatar Header Display', () => { cy.wait(2000); - cy.task('log', 'Step 7: Verify header avatar area is visible and updated'); + testLog.info( 'Step 7: Verify header avatar area is visible and updated'); cy.get('.appflowy-top-bar').should('be.visible'); AvatarSelectors.headerAvatarContainer().should('exist'); @@ -257,7 +258,7 @@ describe('Avatar Header Display', () => { // Verify the avatar image uses the updated URL (if image is loaded) // The avatar might show as image or fallback depending on loading state // We already verified the database update in Step 5, so just verify avatar container exists - cy.task('log', 'Avatar container verified in header - database update confirmed in Step 5'); + testLog.info( 'Avatar container verified in header - database update confirmed in Step 5'); }); }); }); diff --git a/cypress/e2e/account/avatar/avatar-notifications.cy.ts b/cypress/e2e/account/avatar/avatar-notifications.cy.ts index d83d558d..9a9ee577 100644 --- a/cypress/e2e/account/avatar/avatar-notifications.cy.ts +++ b/cypress/e2e/account/avatar/avatar-notifications.cy.ts @@ -1,5 +1,6 @@ import { avatarTestUtils } from './avatar-test-utils'; import { byTestId } from '../../../support/selectors'; +import { testLog } from '../../../support/test-helpers'; const { generateRandomEmail, setupBeforeEach, imports } = avatarTestUtils; const { APP_EVENTS, updateWorkspaceMemberAvatar, AuthTestUtils, AvatarSelectors, dbUtils, WorkspaceSelectors } = imports; @@ -15,23 +16,23 @@ describe('Avatar Notifications', () => { const authUtils = new AuthTestUtils(); const testAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=notification-test'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Get user UUID and workspace ID'); + testLog.info( 'Step 3: Get user UUID and workspace ID'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; dbUtils.getCurrentUserUuid().then((userUuid) => { expect(userUuid).to.not.be.null; - cy.task('log', 'Step 4: Simulate workspace member profile changed notification'); + testLog.info( 'Step 4: Simulate workspace member profile changed notification'); cy.window().then((win) => { const emitter = (win as typeof window & { __APPFLOWY_EVENT_EMITTER__?: { emit: (...args: unknown[]) => void }; @@ -49,23 +50,23 @@ describe('Avatar Notifications', () => { cy.wait(2000); - cy.task('log', 'Step 5: Verify avatar is updated in database'); + testLog.info( 'Step 5: Verify avatar is updated in database'); dbUtils.getWorkspaceMemberProfile(workspaceId!, userUuid!).then((profile) => { expect(profile).to.not.be.null; expect(profile?.avatar_url).to.equal(testAvatarUrl); }); - cy.task('log', 'Step 6: Reload page and verify avatar persists'); + testLog.info( 'Step 6: Reload page and verify avatar persists'); cy.reload(); cy.wait(3000); - cy.task('log', 'Step 7: Open Account Settings to verify avatar'); + testLog.info( 'Step 7: Open Account Settings to verify avatar'); WorkspaceSelectors.dropdownTrigger().click(); cy.wait(1000); cy.get(byTestId('account-settings-button')).click(); AvatarSelectors.accountSettingsDialog().should('be.visible'); - cy.task('log', 'Step 8: Verify avatar image uses updated URL'); + testLog.info( 'Step 8: Verify avatar image uses updated URL'); AvatarSelectors.avatarImage().should('exist').and('have.attr', 'src', testAvatarUrl); }); }); @@ -77,16 +78,16 @@ describe('Avatar Notifications', () => { const authUtils = new AuthTestUtils(); const existingAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=existing'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Set initial avatar via API'); + testLog.info( 'Step 3: Set initial avatar via API'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; @@ -96,16 +97,16 @@ describe('Avatar Notifications', () => { cy.wait(2000); - cy.task('log', 'Step 4: Get user UUID and workspace ID'); + testLog.info( 'Step 4: Get user UUID and workspace ID'); dbUtils.getCurrentUserUuid().then((userUuid) => { expect(userUuid).to.not.be.null; - cy.task('log', 'Step 5: Verify initial avatar is set'); + testLog.info( 'Step 5: Verify initial avatar is set'); dbUtils.getWorkspaceMemberProfile(workspaceId!, userUuid!).then((profile) => { expect(profile?.avatar_url).to.equal(existingAvatarUrl); }); - cy.task('log', 'Step 6: Simulate notification without avatar field'); + testLog.info( 'Step 6: Simulate notification without avatar field'); cy.window().then((win) => { const emitter = (win as typeof window & { __APPFLOWY_EVENT_EMITTER__?: { emit: (...args: unknown[]) => void }; @@ -121,7 +122,7 @@ describe('Avatar Notifications', () => { cy.wait(2000); - cy.task('log', 'Step 7: Verify avatar is preserved'); + testLog.info( 'Step 7: Verify avatar is preserved'); dbUtils.getWorkspaceMemberProfile(workspaceId!, userUuid!).then((profile) => { expect(profile?.avatar_url).to.equal(existingAvatarUrl); expect(profile?.name).to.equal('Updated Name'); @@ -136,16 +137,16 @@ describe('Avatar Notifications', () => { const authUtils = new AuthTestUtils(); const testAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=to-clear'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Set initial avatar'); + testLog.info( 'Step 3: Set initial avatar'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; @@ -158,7 +159,7 @@ describe('Avatar Notifications', () => { dbUtils.getCurrentUserUuid().then((userUuid) => { expect(userUuid).to.not.be.null; - cy.task('log', 'Step 4: Simulate notification with empty avatar'); + testLog.info( 'Step 4: Simulate notification with empty avatar'); cy.window().then((win) => { const emitter = (win as typeof window & { __APPFLOWY_EVENT_EMITTER__?: { emit: (...args: unknown[]) => void }; @@ -174,7 +175,7 @@ describe('Avatar Notifications', () => { cy.wait(2000); - cy.task('log', 'Step 5: Verify avatar is cleared'); + testLog.info( 'Step 5: Verify avatar is cleared'); dbUtils.getWorkspaceMemberProfile(workspaceId!, userUuid!).then((profile) => { expect(profile?.avatar_url).to.be.null; }); diff --git a/cypress/e2e/account/avatar/avatar-persistence.cy.ts b/cypress/e2e/account/avatar/avatar-persistence.cy.ts index 6ac83da2..a0f5090d 100644 --- a/cypress/e2e/account/avatar/avatar-persistence.cy.ts +++ b/cypress/e2e/account/avatar/avatar-persistence.cy.ts @@ -1,5 +1,6 @@ import { avatarTestUtils } from './avatar-test-utils'; import { byTestId } from '../../../support/selectors'; +import { testLog } from '../../../support/test-helpers'; const { generateRandomEmail, setupBeforeEach, imports } = avatarTestUtils; const { updateWorkspaceMemberAvatar, AuthTestUtils, AvatarSelectors, dbUtils, WorkspaceSelectors } = imports; @@ -14,16 +15,16 @@ describe('Avatar Persistence', () => { const authUtils = new AuthTestUtils(); const testAvatarUrl = 'https://api.dicebear.com/7.x/avataaars/svg?seed=persist'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Set avatar via workspace member profile API'); + testLog.info( 'Step 3: Set avatar via workspace member profile API'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; @@ -33,11 +34,11 @@ describe('Avatar Persistence', () => { cy.wait(2000); - cy.task('log', 'Step 4: Reload page'); + testLog.info( 'Step 4: Reload page'); cy.reload(); cy.wait(3000); - cy.task('log', 'Step 5: Verify avatar persisted'); + testLog.info( 'Step 5: Verify avatar persisted'); WorkspaceSelectors.dropdownTrigger().click(); cy.wait(1000); cy.get(byTestId('account-settings-button')).click(); @@ -45,7 +46,7 @@ describe('Avatar Persistence', () => { AvatarSelectors.avatarImage().should('exist').and('have.attr', 'src', testAvatarUrl); - cy.task('log', 'Step 6: Reload again to verify persistence'); + testLog.info( 'Step 6: Reload again to verify persistence'); cy.reload(); cy.wait(3000); diff --git a/cypress/e2e/account/avatar/avatar-priority.cy.ts b/cypress/e2e/account/avatar/avatar-priority.cy.ts index 59ea4924..2b0f264d 100644 --- a/cypress/e2e/account/avatar/avatar-priority.cy.ts +++ b/cypress/e2e/account/avatar/avatar-priority.cy.ts @@ -1,5 +1,6 @@ import { avatarTestUtils } from './avatar-test-utils'; import { byTestId } from '../../../support/selectors'; +import { testLog } from '../../../support/test-helpers'; const { generateRandomEmail, setupBeforeEach, imports } = avatarTestUtils; const { updateUserMetadata, updateWorkspaceMemberAvatar, AuthTestUtils, AvatarSelectors, dbUtils, WorkspaceSelectors } = imports; @@ -15,23 +16,23 @@ describe('Avatar Priority', () => { const userMetadataAvatar = 'https://api.dicebear.com/7.x/avataaars/svg?seed=user-metadata'; const workspaceAvatar = 'https://api.dicebear.com/7.x/avataaars/svg?seed=workspace'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Set user metadata avatar'); + testLog.info( 'Step 3: Set user metadata avatar'); updateUserMetadata(userMetadataAvatar).then((response) => { expect(response.status).to.equal(200); }); cy.wait(2000); - cy.task('log', 'Step 4: Set workspace member avatar'); + testLog.info( 'Step 4: Set workspace member avatar'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; @@ -43,7 +44,7 @@ describe('Avatar Priority', () => { cy.reload(); cy.wait(3000); - cy.task('log', 'Step 5: Verify workspace avatar is displayed (priority)'); + testLog.info( 'Step 5: Verify workspace avatar is displayed (priority)'); WorkspaceSelectors.dropdownTrigger().click(); cy.wait(1000); cy.get(byTestId('account-settings-button')).click(); diff --git a/cypress/e2e/account/avatar/avatar-types.cy.ts b/cypress/e2e/account/avatar/avatar-types.cy.ts index 9c74272b..299ce6ee 100644 --- a/cypress/e2e/account/avatar/avatar-types.cy.ts +++ b/cypress/e2e/account/avatar/avatar-types.cy.ts @@ -1,5 +1,6 @@ import { avatarTestUtils } from './avatar-test-utils'; import { byTestId } from '../../../support/selectors'; +import { testLog } from '../../../support/test-helpers'; const { generateRandomEmail, setupBeforeEach, imports } = avatarTestUtils; const { updateWorkspaceMemberAvatar, AuthTestUtils, AvatarSelectors, dbUtils, WorkspaceSelectors } = imports; @@ -14,16 +15,16 @@ describe('Avatar Types', () => { const authUtils = new AuthTestUtils(); const httpsAvatar = 'https://api.dicebear.com/7.x/avataaars/svg?seed=https'; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Test HTTPS avatar URL'); + testLog.info( 'Step 3: Test HTTPS avatar URL'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; @@ -50,16 +51,16 @@ describe('Avatar Types', () => { const authUtils = new AuthTestUtils(); const emojiAvatars = ['🎨', '🚀', '⭐', '🎯']; - cy.task('log', 'Step 1: Visit login page'); + testLog.info( 'Step 1: Visit login page'); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); - cy.task('log', 'Step 2: Sign in with test account'); + testLog.info( 'Step 2: Sign in with test account'); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url({ timeout: 30000 }).should('include', '/app'); cy.wait(3000); - cy.task('log', 'Step 3: Test each emoji avatar'); + testLog.info( 'Step 3: Test each emoji avatar'); dbUtils.getCurrentWorkspaceId().then((workspaceId) => { expect(workspaceId).to.not.be.null; diff --git a/cypress/e2e/app/sidebar-components.cy.ts b/cypress/e2e/app/sidebar-components.cy.ts index 1137567b..e979516d 100644 --- a/cypress/e2e/app/sidebar-components.cy.ts +++ b/cypress/e2e/app/sidebar-components.cy.ts @@ -1,6 +1,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { PageSelectors, SidebarSelectors } from '../../support/selectors'; import { generateRandomEmail } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('Sidebar Components Resilience Tests', () => { let testEmail: string; @@ -34,10 +35,10 @@ describe('Sidebar Components Resilience Tests', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'Signed in successfully'); + testLog.info( 'Signed in successfully'); // Wait for app to fully load - cy.task('log', 'Waiting for app to fully load...'); + testLog.info( 'Waiting for app to fully load...'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); cy.wait(3000); @@ -55,9 +56,9 @@ describe('Sidebar Components Resilience Tests', () => { }); if (errorBoundaryLogs.length > 0) { - cy.task('log', `Found ${errorBoundaryLogs.length} React error boundary logs`); + testLog.info( `Found ${errorBoundaryLogs.length} React error boundary logs`); errorBoundaryLogs.forEach((log: any) => { - cy.task('log', `Error boundary log: ${JSON.stringify(log.args)}`); + testLog.info( `Error boundary log: ${JSON.stringify(log.args)}`); }); } @@ -66,13 +67,13 @@ describe('Sidebar Components Resilience Tests', () => { }); // Verify sidebar is visible and functional - cy.task('log', 'Verifying sidebar is visible and functional'); + testLog.info( 'Verifying sidebar is visible and functional'); SidebarSelectors.pageHeader().should('be.visible'); // Verify we can interact with the sidebar without errors PageSelectors.items().should('exist'); - cy.task('log', 'Sidebar components loaded successfully without errors'); + testLog.info( 'Sidebar components loaded successfully without errors'); }); }); @@ -101,7 +102,7 @@ describe('Sidebar Components Resilience Tests', () => { expect(favoriteErrors.length).to.equal(0, 'Favorite component should handle empty state gracefully'); }); - cy.task('log', 'App handles empty favorites state correctly'); + testLog.info( 'App handles empty favorites state correctly'); }); }); @@ -133,7 +134,7 @@ describe('Sidebar Components Resilience Tests', () => { expect(shareWithMeErrors.length).to.equal(0, 'ShareWithMe component should handle empty state gracefully'); }); - cy.task('log', 'App handles ShareWithMe with no shared content correctly'); + testLog.info( 'App handles ShareWithMe with no shared content correctly'); }); }); @@ -167,7 +168,7 @@ describe('Sidebar Components Resilience Tests', () => { expect(outlineErrors.length).to.equal(0, 'Components should handle invalid outline data gracefully'); }); - cy.task('log', 'App handles invalid outline data correctly'); + testLog.info( 'App handles invalid outline data correctly'); }); }); @@ -201,7 +202,7 @@ describe('Sidebar Components Resilience Tests', () => { expect(dateErrors.length).to.equal(0, 'Favorite component should handle invalid dates gracefully'); }); - cy.task('log', 'App handles invalid favorite dates correctly'); + testLog.info( 'App handles invalid favorite dates correctly'); }); }); }); diff --git a/cypress/e2e/chat/create-ai-chat.cy.ts b/cypress/e2e/chat/create-ai-chat.cy.ts index f19df301..e7726897 100644 --- a/cypress/e2e/chat/create-ai-chat.cy.ts +++ b/cypress/e2e/chat/create-ai-chat.cy.ts @@ -2,6 +2,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { AddPageSelectors, PageSelectors, ModalSelectors, SidebarSelectors, byTestId, waitForReactUpdate } from '../../support/selectors'; import { generateRandomEmail, logAppFlowyEnvironment } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('AI Chat Creation and Navigation Tests', () => { let testEmail: string; @@ -37,7 +38,7 @@ describe('AI Chat Creation and Navigation Tests', () => { }); // Step 1: Login - cy.task('log', '=== Step 1: Login ==='); + testLog.info( '=== Step 1: Login ==='); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); @@ -46,7 +47,7 @@ describe('AI Chat Creation and Navigation Tests', () => { cy.url().should('include', '/app'); // Wait for the app to fully load - cy.task('log', 'Waiting for app to fully load...'); + testLog.info( 'Waiting for app to fully load...'); // Wait for the loading screen to disappear and main app to appear cy.get('body', { timeout: 30000 }).should('not.contain', 'Welcome!'); @@ -61,26 +62,26 @@ describe('AI Chat Creation and Navigation Tests', () => { cy.wait(2000); // Now wait for the new page button to be available - cy.task('log', 'Looking for new page button...'); + testLog.info( 'Looking for new page button...'); PageSelectors.newPageButton() .should('exist', { timeout: 20000 }) .then(() => { - cy.task('log', 'New page button found!'); + testLog.info( 'New page button found!'); }); // Step 2: Find a space/document that has the add button - cy.task('log', '=== Step 2: Finding a space/document with add button ==='); + testLog.info( '=== Step 2: Finding a space/document with add button ==='); // Expand the first space to see its pages TestTool.expandSpace(); cy.wait(1000); // Find the first page item and hover over it to show actions - cy.task('log', 'Finding first page item to access add actions...'); + testLog.info( 'Finding first page item to access add actions...'); // Get the first page and hover to show the inline actions PageSelectors.items().first().then($page => { - cy.task('log', 'Hovering over first page to show action buttons...'); + testLog.info( 'Hovering over first page to show action buttons...'); // Hover over the page to reveal the action buttons cy.wrap($page) @@ -97,38 +98,38 @@ describe('AI Chat Creation and Navigation Tests', () => { .click({ force: true }); }); - cy.task('log', 'Clicked inline add page button'); + testLog.info( 'Clicked inline add page button'); }); // Wait for the dropdown menu to appear cy.wait(1000); // Step 3: Click on AI Chat option from the dropdown - cy.task('log', '=== Step 3: Creating AI Chat ==='); + testLog.info( '=== Step 3: Creating AI Chat ==='); // Click on the AI Chat option in the dropdown AddPageSelectors.addAIChatButton() .should('be.visible') .click(); - cy.task('log', 'Clicked AI Chat option from dropdown'); + testLog.info( 'Clicked AI Chat option from dropdown'); // Wait for navigation to the AI chat page cy.wait(3000); // Step 4: Verify AI Chat page loaded successfully - cy.task('log', '=== Step 4: Verifying AI Chat page loaded ==='); + testLog.info( '=== Step 4: Verifying AI Chat page loaded ==='); // Check that the URL contains a view ID (indicating navigation to chat) cy.url().should('match', /\/app\/[a-f0-9-]+\/[a-f0-9-]+/, { timeout: 10000 }); - cy.task('log', '✓ Navigated to AI Chat page'); + testLog.info( '✓ Navigated to AI Chat page'); // Check if the AI Chat container exists (but don't fail if it doesn't load immediately) cy.get('body').then($body => { if ($body.find(byTestId('ai-chat-container')).length > 0) { - cy.task('log', '✓ AI Chat container exists'); + testLog.info( '✓ AI Chat container exists'); } else { - cy.task('log', 'AI Chat container not immediately visible, checking for navigation success...'); + testLog.info( 'AI Chat container not immediately visible, checking for navigation success...'); } }); @@ -143,9 +144,9 @@ describe('AI Chat Creation and Navigation Tests', () => { $body.find(byTestId('ai-chat-container')).length > 0; if (hasChatElements) { - cy.task('log', '✓ AI Chat interface loaded'); + testLog.info( '✓ AI Chat interface loaded'); } else { - cy.task('log', 'Warning: AI Chat elements not immediately visible, but container exists'); + testLog.info( 'Warning: AI Chat elements not immediately visible, but container exists'); } }); @@ -161,11 +162,11 @@ describe('AI Chat Creation and Navigation Tests', () => { throw new Error('Error detected on AI Chat page'); } - cy.task('log', '✓ No errors detected on page'); + testLog.info( '✓ No errors detected on page'); }); // Step 5: Basic verification that we're on a chat page - cy.task('log', '=== Step 5: Final verification ==='); + testLog.info( '=== Step 5: Final verification ==='); // Simply verify that: // 1. We navigated to a new page (URL changed) @@ -173,17 +174,17 @@ describe('AI Chat Creation and Navigation Tests', () => { // 3. The page appears to have loaded cy.url().then(url => { - cy.task('log', `Current URL: ${url}`); + testLog.info( `Current URL: ${url}`); // Verify we're on a view page if (url.includes('/app/') && url.split('/').length >= 5) { - cy.task('log', '✓ Successfully navigated to a view page'); + testLog.info( '✓ Successfully navigated to a view page'); } }); // Final verification - cy.task('log', '=== Test completed successfully! ==='); - cy.task('log', '✓✓✓ AI Chat created and opened without errors'); + testLog.info( '=== Test completed successfully! ==='); + testLog.info( '✓✓✓ AI Chat created and opened without errors'); }); }); }); diff --git a/cypress/e2e/chat/model-selection-persistence.cy.ts b/cypress/e2e/chat/model-selection-persistence.cy.ts index 8fce812b..f148aba0 100644 --- a/cypress/e2e/chat/model-selection-persistence.cy.ts +++ b/cypress/e2e/chat/model-selection-persistence.cy.ts @@ -2,6 +2,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { AddPageSelectors, PageSelectors, SidebarSelectors, ModelSelectorSelectors } from '../../support/selectors'; import { generateRandomEmail, logAppFlowyEnvironment } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('Chat Model Selection Persistence Tests', () => { let testEmail: string; @@ -33,7 +34,7 @@ describe('Chat Model Selection Persistence Tests', () => { }); // Step 1: Login - cy.task('log', '=== Step 1: Login ==='); + testLog.info( '=== Step 1: Login ==='); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); @@ -42,7 +43,7 @@ describe('Chat Model Selection Persistence Tests', () => { cy.url().should('include', '/app'); // Wait for the app to fully load - cy.task('log', 'Waiting for app to fully load...'); + testLog.info( 'Waiting for app to fully load...'); // Wait for the loading screen to disappear and main app to appear cy.get('body', { timeout: 30000 }).should('not.contain', 'Welcome!'); @@ -57,7 +58,7 @@ describe('Chat Model Selection Persistence Tests', () => { cy.wait(2000); // Step 2: Create an AI Chat - cy.task('log', '=== Step 2: Creating AI Chat ==='); + testLog.info( '=== Step 2: Creating AI Chat ==='); // Expand the first space to see its pages TestTool.expandSpace(); @@ -65,7 +66,7 @@ describe('Chat Model Selection Persistence Tests', () => { // Find the first page item and hover over it to show actions PageSelectors.items().first().then($page => { - cy.task('log', 'Hovering over first page to show action buttons...'); + testLog.info( 'Hovering over first page to show action buttons...'); // Hover over the page to reveal the action buttons cy.wrap($page) @@ -91,13 +92,13 @@ describe('Chat Model Selection Persistence Tests', () => { .should('be.visible') .click(); - cy.task('log', 'Created AI Chat'); + testLog.info( 'Created AI Chat'); // Wait for navigation to the AI chat page cy.wait(3000); // Step 3: Open model selector and select a model - cy.task('log', '=== Step 3: Selecting a Model ==='); + testLog.info( '=== Step 3: Selecting a Model ==='); // Wait for the chat interface to load cy.wait(2000); @@ -107,7 +108,7 @@ describe('Chat Model Selection Persistence Tests', () => { .should('be.visible', { timeout: 10000 }) .click(); - cy.task('log', 'Opened model selector dropdown'); + testLog.info( 'Opened model selector dropdown'); // Wait for the dropdown to appear and models to load cy.wait(2000); @@ -124,14 +125,14 @@ describe('Chat Model Selection Persistence Tests', () => { if (nonAutoOptions.length > 0) { // Click the first non-Auto model const selectedModel = nonAutoOptions[0].getAttribute('data-testid')?.replace('model-option-', ''); - cy.task('log', `Selecting model: ${selectedModel}`); + testLog.info( `Selecting model: ${selectedModel}`); cy.wrap(nonAutoOptions[0]).click(); // Store the selected model name for verification cy.wrap(selectedModel).as('selectedModel'); } else { // If only Auto is available, select it explicitly - cy.task('log', 'Only Auto model available, selecting it'); + testLog.info( 'Only Auto model available, selecting it'); ModelSelectorSelectors.optionByName('Auto').click(); cy.wrap('Auto').as('selectedModel'); } @@ -142,27 +143,27 @@ describe('Chat Model Selection Persistence Tests', () => { // Verify the model is selected by checking the button text cy.get('@selectedModel').then((modelName) => { - cy.task('log', `Verifying model ${modelName} is displayed in button`); + testLog.info( `Verifying model ${modelName} is displayed in button`); ModelSelectorSelectors.button() .should('contain.text', modelName); }); // Step 4: Save the current URL for reload - cy.task('log', '=== Step 4: Saving current URL ==='); + testLog.info( '=== Step 4: Saving current URL ==='); cy.url().then(url => { cy.wrap(url).as('chatUrl'); - cy.task('log', `Current chat URL: ${url}`); + testLog.info( `Current chat URL: ${url}`); }); // Step 5: Reload the page - cy.task('log', '=== Step 5: Reloading page ==='); + testLog.info( '=== Step 5: Reloading page ==='); cy.reload(); // Wait for the page to reload completely cy.wait(3000); // Step 6: Verify the model selection persisted - cy.task('log', '=== Step 6: Verifying Model Selection Persisted ==='); + testLog.info( '=== Step 6: Verifying Model Selection Persisted ==='); // Wait for the model selector button to be visible again ModelSelectorSelectors.button() @@ -170,14 +171,14 @@ describe('Chat Model Selection Persistence Tests', () => { // Verify the previously selected model is still displayed cy.get('@selectedModel').then((modelName) => { - cy.task('log', `Checking if model ${modelName} is still selected after reload`); + testLog.info( `Checking if model ${modelName} is still selected after reload`); ModelSelectorSelectors.button() .should('contain.text', modelName); - cy.task('log', `✓ Model ${modelName} persisted after page reload!`); + testLog.info( `✓ Model ${modelName} persisted after page reload!`); }); // Optional: Open the dropdown again to verify the selection visually - cy.task('log', '=== Step 7: Double-checking selection in dropdown ==='); + testLog.info( '=== Step 7: Double-checking selection in dropdown ==='); ModelSelectorSelectors.button().click(); cy.wait(1000); @@ -185,15 +186,15 @@ describe('Chat Model Selection Persistence Tests', () => { cy.get('@selectedModel').then((modelName) => { ModelSelectorSelectors.optionByName(modelName as string) .should('have.class', 'bg-fill-content-select'); - cy.task('log', `✓ Model ${modelName} shows as selected in dropdown`); + testLog.info( `✓ Model ${modelName} shows as selected in dropdown`); }); // Close the dropdown cy.get('body').click(0, 0); // Final verification - cy.task('log', '=== Test completed successfully! ==='); - cy.task('log', '✓✓✓ Model selection persisted after page reload'); + testLog.info( '=== Test completed successfully! ==='); + testLog.info( '✓✓✓ Model selection persisted after page reload'); }); }); }); diff --git a/cypress/e2e/page/breadcrumb-navigation.cy.ts b/cypress/e2e/page/breadcrumb-navigation.cy.ts index 267eeb3a..acbf31ae 100644 --- a/cypress/e2e/page/breadcrumb-navigation.cy.ts +++ b/cypress/e2e/page/breadcrumb-navigation.cy.ts @@ -1,6 +1,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { +import { testLog } from '../../support/test-helpers'; PageSelectors, SpaceSelectors, SidebarSelectors, @@ -38,7 +39,7 @@ describe('Breadcrumb Navigation Complete Tests', () => { describe('Basic Navigation Tests', () => { it('should navigate through space and check for breadcrumb availability', { timeout: 60000 }, () => { // Login - cy.task('log', '=== Step 1: Login ==='); + testLog.info( '=== Step 1: Login ==='); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); @@ -47,41 +48,41 @@ describe('Breadcrumb Navigation Complete Tests', () => { cy.url().should('include', '/app'); // Wait for app to load - cy.task('log', 'Waiting for app to fully load...'); + testLog.info( 'Waiting for app to fully load...'); cy.get('body', { timeout: 30000 }).should('not.contain', 'Welcome!'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); cy.wait(2000); - cy.task('log', 'App loaded successfully'); + testLog.info( 'App loaded successfully'); // Step 2: Expand first space - cy.task('log', '=== Step 2: Expanding first space ==='); + testLog.info( '=== Step 2: Expanding first space ==='); TestTool.expandSpace(0); cy.wait(2000); - cy.task('log', 'Expanded first space'); + testLog.info( 'Expanded first space'); // Step 3: Navigate to first page - cy.task('log', '=== Step 3: Navigating to first page ==='); + testLog.info( '=== Step 3: Navigating to first page ==='); PageSelectors.names().first().then($page => { const pageName = $page.text(); - cy.task('log', `Navigating to: ${pageName}`); + testLog.info( `Navigating to: ${pageName}`); cy.wrap($page).click(); }); cy.wait(3000); // Step 4: Check for breadcrumb navigation - cy.task('log', '=== Step 4: Checking for breadcrumb navigation ==='); + testLog.info( '=== Step 4: Checking for breadcrumb navigation ==='); cy.get('body').then($body => { if ($body.find(byTestId('breadcrumb-navigation')).length > 0) { - cy.task('log', '✓ Breadcrumb navigation found on this page'); + testLog.info( '✓ Breadcrumb navigation found on this page'); // Count breadcrumb items cy.get(byTestIdContains('breadcrumb-item-')).then($items => { - cy.task('log', `✓ Found ${$items.length} breadcrumb items`); + testLog.info( `✓ Found ${$items.length} breadcrumb items`); }); } else { - cy.task('log', 'No breadcrumb navigation on this page (normal for top-level pages)'); + testLog.info( 'No breadcrumb navigation on this page (normal for top-level pages)'); } }); @@ -91,11 +92,11 @@ describe('Breadcrumb Navigation Complete Tests', () => { $body.text().includes('Failed'); if (!hasError) { - cy.task('log', '✓ Navigation completed without errors'); + testLog.info( '✓ Navigation completed without errors'); } }); - cy.task('log', '=== Basic navigation test completed ==='); + testLog.info( '=== Basic navigation test completed ==='); }); }); @@ -114,49 +115,49 @@ describe('Breadcrumb Navigation Complete Tests', () => { PageSelectors.names().should('exist', { timeout: 30000 }); cy.wait(2000); - cy.task('log', '=== Step 1: Expand first space ==='); + testLog.info( '=== Step 1: Expand first space ==='); TestTool.expandSpace(0); cy.wait(2000); - cy.task('log', '=== Step 2: Navigate to first page ==='); + testLog.info( '=== Step 2: Navigate to first page ==='); PageSelectors.names().first().click(); cy.wait(3000); - cy.task('log', '=== Step 3: Check for nested pages ==='); + testLog.info( '=== Step 3: Check for nested pages ==='); PageSelectors.names().then($pages => { - cy.task('log', `Found ${$pages.length} pages in sidebar`); + testLog.info( `Found ${$pages.length} pages in sidebar`); if ($pages.length > 1) { // Navigate to a nested page - cy.task('log', 'Navigating to nested page'); + testLog.info( 'Navigating to nested page'); cy.wrap($pages[1]).click({ force: true }); cy.wait(3000); // Check for breadcrumb navigation - cy.task('log', '=== Step 4: Testing breadcrumb navigation ==='); + testLog.info( '=== Step 4: Testing breadcrumb navigation ==='); cy.get('body', { timeout: 5000 }).then($body => { if ($body.find(byTestId('breadcrumb-navigation')).length > 0) { - cy.task('log', '✓ Breadcrumb navigation is visible'); + testLog.info( '✓ Breadcrumb navigation is visible'); // Try to click breadcrumb to navigate back if ($body.find(byTestIdContains('breadcrumb-item-')).length > 1) { cy.get(byTestIdContains('breadcrumb-item-')).first().click({ force: true }); - cy.task('log', '✓ Clicked breadcrumb item to navigate back'); + testLog.info( '✓ Clicked breadcrumb item to navigate back'); cy.wait(2000); - cy.task('log', '✓ Successfully used breadcrumb navigation'); + testLog.info( '✓ Successfully used breadcrumb navigation'); } else { - cy.task('log', 'Only one breadcrumb item found'); + testLog.info( 'Only one breadcrumb item found'); } } else { - cy.task('log', 'No breadcrumb navigation on nested page'); + testLog.info( 'No breadcrumb navigation on nested page'); } }); } else { - cy.task('log', 'No nested pages available for breadcrumb testing'); + testLog.info( 'No nested pages available for breadcrumb testing'); } }); - cy.task('log', '=== Nested navigation test completed ==='); + testLog.info( '=== Nested navigation test completed ==='); }); }); }); @@ -178,10 +179,10 @@ describe('Breadcrumb Navigation Complete Tests', () => { cy.wait(2000); // Step 1: Find and expand General space or first space - cy.task('log', '=== Step 1: Looking for General space ==='); + testLog.info( '=== Step 1: Looking for General space ==='); SpaceSelectors.names().then($spaces => { const spaceNames = Array.from($spaces).map((el: Element) => el.textContent?.trim()); - cy.task('log', `Available spaces: ${spaceNames.join(', ')}`); + testLog.info( `Available spaces: ${spaceNames.join(', ')}`); // Find General space or use first const generalIndex = spaceNames.findIndex(name => @@ -189,20 +190,20 @@ describe('Breadcrumb Navigation Complete Tests', () => { ); if (generalIndex !== -1) { - cy.task('log', `Found General space at index ${generalIndex}`); + testLog.info( `Found General space at index ${generalIndex}`); TestTool.expandSpace(generalIndex); } else { - cy.task('log', 'Using first available space'); + testLog.info( 'Using first available space'); TestTool.expandSpace(0); } }); cy.wait(2000); // Step 2: Look for Get Started page or use first page - cy.task('log', '=== Step 2: Looking for Get Started page ==='); + testLog.info( '=== Step 2: Looking for Get Started page ==='); PageSelectors.names().then($pages => { const pageNames = Array.from($pages).map((el: Element) => el.textContent?.trim()); - cy.task('log', `Available pages: ${pageNames.join(', ')}`); + testLog.info( `Available pages: ${pageNames.join(', ')}`); // Find Get Started or similar page const getStartedPage = Array.from($pages).find((el: Element) => { @@ -213,20 +214,20 @@ describe('Breadcrumb Navigation Complete Tests', () => { if (getStartedPage) { cy.wrap(getStartedPage).click(); - cy.task('log', `Clicked on: ${getStartedPage.textContent?.trim()}`); + testLog.info( `Clicked on: ${getStartedPage.textContent?.trim()}`); } else { PageSelectors.names().first().click(); - cy.task('log', 'Clicked first available page'); + testLog.info( 'Clicked first available page'); } }); cy.wait(3000); // Step 3: Look for Desktop Guide or sub-page - cy.task('log', '=== Step 3: Looking for Desktop Guide or sub-pages ==='); + testLog.info( '=== Step 3: Looking for Desktop Guide or sub-pages ==='); PageSelectors.names().then($subPages => { if ($subPages.length > 1) { const subPageNames = Array.from($subPages).map((el: Element) => el.textContent?.trim()); - cy.task('log', `Found sub-pages: ${subPageNames.join(', ')}`); + testLog.info( `Found sub-pages: ${subPageNames.join(', ')}`); // Look for Desktop Guide or any guide - limit search to avoid hanging const maxIndex = Math.min($subPages.length, 5); @@ -243,40 +244,40 @@ describe('Breadcrumb Navigation Complete Tests', () => { if (guidePage) { cy.wrap(guidePage).click({ force: true }); - cy.task('log', `Navigated to: ${guidePage.textContent?.trim()}`); + testLog.info( `Navigated to: ${guidePage.textContent?.trim()}`); } else if ($subPages.length > 1) { cy.wrap($subPages[1]).click({ force: true }); - cy.task('log', 'Navigated to second page'); + testLog.info( 'Navigated to second page'); } cy.wait(3000); // Step 4: Test breadcrumb navigation - cy.task('log', '=== Step 4: Testing breadcrumb navigation ==='); + testLog.info( '=== Step 4: Testing breadcrumb navigation ==='); cy.get('body').then($body => { if ($body.find(byTestId('breadcrumb-navigation')).length > 0) { - cy.task('log', '✓ Breadcrumb navigation is visible'); + testLog.info( '✓ Breadcrumb navigation is visible'); // Check breadcrumb items with timeout cy.get(byTestIdContains('breadcrumb-item-'), { timeout: 10000 }).then($items => { - cy.task('log', `Found ${$items.length} breadcrumb items`); + testLog.info( `Found ${$items.length} breadcrumb items`); if ($items.length > 1) { // Click second-to-last breadcrumb (parent page) const targetIndex = Math.max(0, $items.length - 2); cy.wrap($items[targetIndex]).click({ force: true }); - cy.task('log', `✓ Clicked breadcrumb at index ${targetIndex} to go back`); + testLog.info( `✓ Clicked breadcrumb at index ${targetIndex} to go back`); cy.wait(2000); // Verify navigation worked - cy.task('log', '✓ Successfully navigated back using breadcrumb'); + testLog.info( '✓ Successfully navigated back using breadcrumb'); } }); } else { - cy.task('log', 'Breadcrumb navigation not available on this page'); + testLog.info( 'Breadcrumb navigation not available on this page'); } }); } else { - cy.task('log', 'No sub-pages found for breadcrumb testing'); + testLog.info( 'No sub-pages found for breadcrumb testing'); } }); @@ -287,11 +288,11 @@ describe('Breadcrumb Navigation Complete Tests', () => { $body.find('[role="alert"]').length > 0; if (!hasError) { - cy.task('log', '✓ Test completed without errors'); + testLog.info( '✓ Test completed without errors'); } }); - cy.task('log', '=== Full breadcrumb flow test completed ==='); + testLog.info( '=== Full breadcrumb flow test completed ==='); }); }); }); diff --git a/cypress/e2e/page/create-delete-page.cy.ts b/cypress/e2e/page/create-delete-page.cy.ts index b289d2de..ec93aba8 100644 --- a/cypress/e2e/page/create-delete-page.cy.ts +++ b/cypress/e2e/page/create-delete-page.cy.ts @@ -2,6 +2,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { PageSelectors, ModalSelectors, SidebarSelectors, waitForReactUpdate } from '../../support/selectors'; import { generateRandomEmail, logAppFlowyEnvironment } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('Page Create and Delete Tests', () => { let testEmail: string; @@ -37,7 +38,7 @@ describe('Page Create and Delete Tests', () => { cy.url().should('include', '/app'); // Wait for the app to fully load - cy.task('log', 'Waiting for app to fully load...'); + testLog.info( 'Waiting for app to fully load...'); // Wait for the loading screen to disappear and main app to appear cy.get('body', { timeout: 30000 }).should('not.contain', 'Welcome!'); @@ -52,15 +53,15 @@ describe('Page Create and Delete Tests', () => { cy.wait(2000); // Now wait for the new page button to be available - cy.task('log', 'Looking for new page button...'); + testLog.info( 'Looking for new page button...'); PageSelectors.newPageButton() .should('exist', { timeout: 20000 }) .then(() => { - cy.task('log', 'New page button found!'); + testLog.info( 'New page button found!'); }); // Step 2: Since user already has a workspace, just create a new page - cy.task('log', `Creating page with title: ${testPageName}`); + testLog.info( `Creating page with title: ${testPageName}`); // Click new page button PageSelectors.newPageButton().click(); @@ -82,7 +83,7 @@ describe('Page Create and Delete Tests', () => { cy.get('body').then(($body: JQuery) => { // Check if there's a modal dialog open if ($body.find('[role="dialog"]').length > 0 || $body.find('.MuiDialog-container').length > 0) { - cy.task('log', 'Closing modal dialog'); + testLog.info( 'Closing modal dialog'); // Click the close button or press ESC cy.get('body').type('{esc}'); cy.wait(1000); @@ -105,7 +106,7 @@ describe('Page Create and Delete Tests', () => { .clear({ force: true }) .type(testPageName, { force: true }) .type('{enter}'); // Press enter to save the title - cy.task('log', `Set page title to: ${testPageName}`); + testLog.info( `Set page title to: ${testPageName}`); } }); @@ -128,18 +129,18 @@ describe('Page Create and Delete Tests', () => { PageSelectors.names().then($pages => { const pageNames = Array.from($pages).map((el: Element) => el.textContent?.trim()); initialPageCount = pageNames.length; - cy.task('log', `Found pages after creating new page: ${pageNames.join(', ')}`); + testLog.info( `Found pages after creating new page: ${pageNames.join(', ')}`); // The created page should have our test name if (pageNames.includes(testPageName)) { createdPageName = testPageName; - cy.task('log', `Found the created page with correct name: ${testPageName}`); + testLog.info( `Found the created page with correct name: ${testPageName}`); } else { // If title didn't save properly, find the newest "Untitled" page const untitledPages = pageNames.filter(name => name === 'Untitled'); if (untitledPages.length > 0) { createdPageName = 'Untitled'; - cy.task('log', `Warning: Page title didn't save. Page exists as "Untitled"`); + testLog.info( `Warning: Page title didn't save. Page exists as "Untitled"`); } else { throw new Error(`Could not find created page. Expected "${testPageName}", found: ${pageNames.join(', ')}`); } @@ -150,9 +151,9 @@ describe('Page Create and Delete Tests', () => { cy.then(() => { // Use the stored createdPageName from step 3 if (createdPageName) { - cy.task('log', `Attempting to delete the created page: ${createdPageName}`); + testLog.info( `Attempting to delete the created page: ${createdPageName}`); TestTool.deletePageByName(createdPageName); - cy.task('log', `Deleted page: ${createdPageName}`); + testLog.info( `Deleted page: ${createdPageName}`); } else { throw new Error('No page was created to delete'); } @@ -170,7 +171,7 @@ describe('Page Create and Delete Tests', () => { cy.then(() => { PageSelectors.names().then($pages => { const pageNames = Array.from($pages).map((el: Element) => el.textContent?.trim()); - cy.task('log', `Pages after delete and reload: ${pageNames.join(', ')}`); + testLog.info( `Pages after delete and reload: ${pageNames.join(', ')}`); // Check that the created page (whatever its final name was) no longer exists const pageStillExists = pageNames.some(name => @@ -178,8 +179,8 @@ describe('Page Create and Delete Tests', () => { ); if (!pageStillExists) { - cy.task('log', `✓ Verified test page "${createdPageName}" is gone after reload`); - cy.task('log', `Remaining pages: ${pageNames.join(', ')}`); + testLog.info( `✓ Verified test page "${createdPageName}" is gone after reload`); + testLog.info( `Remaining pages: ${pageNames.join(', ')}`); } else { throw new Error(`Test page "${createdPageName}" still exists after delete. Found pages: ${pageNames.join(', ')}`); } diff --git a/cypress/e2e/page/delete-page-verify-trash.cy.ts b/cypress/e2e/page/delete-page-verify-trash.cy.ts index d803bc09..3959f04c 100644 --- a/cypress/e2e/page/delete-page-verify-trash.cy.ts +++ b/cypress/e2e/page/delete-page-verify-trash.cy.ts @@ -2,6 +2,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { PageSelectors, ModalSelectors, SidebarSelectors, byTestId, waitForReactUpdate } from '../../support/selectors'; import { generateRandomEmail, logAppFlowyEnvironment } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('Delete Page, Verify in Trash, and Restore Tests', () => { let testEmail: string; @@ -29,7 +30,7 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { }); // Step 1: Login - cy.task('log', '=== Step 1: Login ==='); + testLog.info( '=== Step 1: Login ==='); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); @@ -38,7 +39,7 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { cy.url().should('include', '/app'); // Wait for the app to fully load - cy.task('log', 'Waiting for app to fully load...'); + testLog.info( 'Waiting for app to fully load...'); // Wait for the loading screen to disappear and main app to appear cy.get('body', { timeout: 30000 }).should('not.contain', 'Welcome!'); @@ -53,15 +54,15 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { cy.wait(2000); // Now wait for the new page button to be available - cy.task('log', 'Looking for new page button...'); + testLog.info( 'Looking for new page button...'); PageSelectors.newPageButton() .should('exist', { timeout: 20000 }) .then(() => { - cy.task('log', 'New page button found!'); + testLog.info( 'New page button found!'); }); // Step 2: Create a new page - cy.task('log', `=== Step 2: Creating page with title: ${testPageName} ===`); + testLog.info( `=== Step 2: Creating page with title: ${testPageName} ===`); // Click new page button PageSelectors.newPageButton().click(); @@ -82,7 +83,7 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { // Close any share/modal dialogs that might be open cy.get('body').then(($body: JQuery) => { if ($body.find('[role="dialog"]').length > 0 || $body.find('.MuiDialog-container').length > 0) { - cy.task('log', 'Closing modal dialog'); + testLog.info( 'Closing modal dialog'); cy.get('body').type('{esc}'); cy.wait(1000); } @@ -102,7 +103,7 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { .clear({ force: true }) .type(testPageName, { force: true }) .type('{enter}'); - cy.task('log', `Set page title to: ${testPageName}`); + testLog.info( `Set page title to: ${testPageName}`); } }); @@ -110,7 +111,7 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { cy.wait(2000); // Step 3: Verify the page exists in sidebar - cy.task('log', '=== Step 3: Verifying page exists in sidebar ==='); + testLog.info( '=== Step 3: Verifying page exists in sidebar ==='); // Expand the first space to see its pages TestTool.expandSpace(); @@ -119,7 +120,7 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { // Verify the page exists PageSelectors.names().then($pages => { const pageNames = Array.from($pages).map((el: Element) => el.textContent?.trim()); - cy.task('log', `Found pages: ${pageNames.join(', ')}`); + testLog.info( `Found pages: ${pageNames.join(', ')}`); // Check if our page exists const pageExists = pageNames.some(name => @@ -127,14 +128,14 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { ); if (pageExists) { - cy.task('log', `✓ Page created successfully`); + testLog.info( `✓ Page created successfully`); } else { throw new Error(`Could not find created page. Expected "${testPageName}", found: ${pageNames.join(', ')}`); } }); // Step 4: Delete the page - cy.task('log', `=== Step 4: Deleting page: ${testPageName} ===`); + testLog.info( `=== Step 4: Deleting page: ${testPageName} ===`); // Find the page we want to delete PageSelectors.names().then($pages => { @@ -147,20 +148,20 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { const untitledPages = pageNames.filter(name => name === 'Untitled'); if (untitledPages.length > 0) { pageToDelete = 'Untitled'; - cy.task('log', `Warning: Page title didn't save. Deleting "Untitled" page instead`); + testLog.info( `Warning: Page title didn't save. Deleting "Untitled" page instead`); } } // Delete the page TestTool.deletePageByName(pageToDelete); - cy.task('log', `✓ Deleted page: ${pageToDelete}`); + testLog.info( `✓ Deleted page: ${pageToDelete}`); }); // Wait for deletion to complete cy.wait(2000); // Step 5: Navigate to trash page - cy.task('log', '=== Step 5: Navigating to trash page ==='); + testLog.info( '=== Step 5: Navigating to trash page ==='); // Click on the trash button in the sidebar cy.get(byTestId('sidebar-trash-button')).click(); @@ -170,10 +171,10 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { // Verify we're on the trash page cy.url().should('include', '/app/trash'); - cy.task('log', '✓ Successfully navigated to trash page'); + testLog.info( '✓ Successfully navigated to trash page'); // Step 6: Verify the deleted page exists in trash - cy.task('log', '=== Step 6: Verifying deleted page exists in trash ==='); + testLog.info( '=== Step 6: Verifying deleted page exists in trash ==='); // Wait for trash table to load cy.get(byTestId('trash-table'), { timeout: 10000 }).should('be.visible'); @@ -185,38 +186,38 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { // Check each row for our page name $rows.each((index, row) => { const rowText = Cypress.$(row).text(); - cy.task('log', `Trash row ${index + 1}: ${rowText}`); + testLog.info( `Trash row ${index + 1}: ${rowText}`); // Check if this row contains our page (might be named as testPageName or "Untitled") if (rowText.includes(testPageName) || rowText.includes('Untitled')) { foundPage = true; - cy.task('log', `✓ Found deleted page in trash: ${rowText}`); + testLog.info( `✓ Found deleted page in trash: ${rowText}`); } }); // Verify we found the page if (foundPage) { - cy.task('log', '✓✓✓ Test Passed: Deleted page was found in trash'); + testLog.info( '✓✓✓ Test Passed: Deleted page was found in trash'); } else { throw new Error(`Deleted page not found in trash. Expected to find "${testPageName}" or "Untitled"`); } }); // Step 7: Verify restore and permanent delete buttons are present - cy.task('log', '=== Step 7: Verifying trash actions are available ==='); + testLog.info( '=== Step 7: Verifying trash actions are available ==='); cy.get(byTestId('trash-table-row')).first().within(() => { // Check for restore button cy.get(byTestId('trash-restore-button')).should('exist'); - cy.task('log', '✓ Restore button found'); + testLog.info( '✓ Restore button found'); // Check for permanent delete button cy.get(byTestId('trash-delete-button')).should('exist'); - cy.task('log', '✓ Permanent delete button found'); + testLog.info( '✓ Permanent delete button found'); }); // Step 8: Restore the deleted page - cy.task('log', '=== Step 8: Restoring the deleted page ==='); + testLog.info( '=== Step 8: Restoring the deleted page ==='); // Store the actual page name we'll be restoring let restoredPageName = 'Untitled'; // Default to Untitled since that's what usually gets created @@ -226,7 +227,7 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { // Get the page name before restoring cy.get('td').first().invoke('text').then((text) => { restoredPageName = text.trim() || 'Untitled'; - cy.task('log', `Restoring page: ${restoredPageName}`); + testLog.info( `Restoring page: ${restoredPageName}`); }); // Click restore button @@ -235,10 +236,10 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { // Wait for restore to complete cy.wait(2000); - cy.task('log', '✓ Restore button clicked'); + testLog.info( '✓ Restore button clicked'); // Step 9: Verify the page is removed from trash - cy.task('log', '=== Step 9: Verifying page is removed from trash ==='); + testLog.info( '=== Step 9: Verifying page is removed from trash ==='); // Check if trash is now empty or doesn't contain our page cy.get('body').then(($body) => { @@ -246,7 +247,7 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { const rowsExist = $body.find(byTestId('trash-table-row')).length > 0; if (!rowsExist) { - cy.task('log', '✓ Trash is now empty - page successfully removed from trash'); + testLog.info( '✓ Trash is now empty - page successfully removed from trash'); } else { // If there are still rows, verify our page is not among them cy.get(byTestId('trash-table-row')).then($rows => { @@ -262,14 +263,14 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { if (pageStillInTrash) { throw new Error(`Page "${restoredPageName}" is still in trash after restore`); } else { - cy.task('log', `✓ Page "${restoredPageName}" successfully removed from trash`); + testLog.info( `✓ Page "${restoredPageName}" successfully removed from trash`); } }); } }); // Step 10: Navigate back to the main workspace - cy.task('log', '=== Step 10: Navigating back to workspace ==='); + testLog.info( '=== Step 10: Navigating back to workspace ==='); // Click on the workspace/home to go back cy.visit(`/app`); @@ -277,10 +278,10 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { // Wait for the sidebar to load SidebarSelectors.pageHeader().should('be.visible', { timeout: 10000 }); - cy.task('log', '✓ Navigated back to workspace'); + testLog.info( '✓ Navigated back to workspace'); // Step 11: Verify the restored page exists in sidebar - cy.task('log', '=== Step 11: Verifying restored page exists in sidebar ==='); + testLog.info( '=== Step 11: Verifying restored page exists in sidebar ==='); // Expand the space to see all pages TestTool.expandSpace(); @@ -289,7 +290,7 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { // Verify the restored page exists in the sidebar PageSelectors.names().then($pages => { const pageNames = Array.from($pages).map((el: Element) => el.textContent?.trim()); - cy.task('log', `Pages in sidebar after restore: ${pageNames.join(', ')}`); + testLog.info( `Pages in sidebar after restore: ${pageNames.join(', ')}`); // Check if our restored page exists const pageRestored = pageNames.some(name => @@ -297,13 +298,13 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { ); if (pageRestored) { - cy.task('log', `✓✓✓ SUCCESS: Page "${restoredPageName}" has been successfully restored to the sidebar!`); + testLog.info( `✓✓✓ SUCCESS: Page "${restoredPageName}" has been successfully restored to the sidebar!`); } else { throw new Error(`Restored page not found in sidebar. Expected to find "${restoredPageName}", found: ${pageNames.join(', ')}`); } }); - cy.task('log', '=== Test completed successfully! Page was deleted, verified in trash, and successfully restored! ==='); + testLog.info( '=== Test completed successfully! Page was deleted, verified in trash, and successfully restored! ==='); }); }); }); diff --git a/cypress/e2e/page/edit-page.cy.ts b/cypress/e2e/page/edit-page.cy.ts index 1a6617f5..0b091116 100644 --- a/cypress/e2e/page/edit-page.cy.ts +++ b/cypress/e2e/page/edit-page.cy.ts @@ -2,6 +2,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { PageSelectors, ModalSelectors, waitForReactUpdate } from '../../support/selectors'; import { generateRandomEmail } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('Page Edit Tests', () => { let testEmail: string; @@ -46,8 +47,8 @@ describe('Page Edit Tests', () => { cy.wait(2000); // Step 2: Create a new page using the simpler approach - cy.task('log', '=== Starting Page Creation for Edit Test ==='); - cy.task('log', `Target page name: ${testPageName}`); + testLog.info( '=== Starting Page Creation for Edit Test ==='); + testLog.info( `Target page name: ${testPageName}`); // Click new page button PageSelectors.newPageButton().should('be.visible').click(); @@ -68,18 +69,18 @@ describe('Page Edit Tests', () => { // Close any modal dialogs cy.get('body').then(($body: JQuery) => { if ($body.find('[role="dialog"]').length > 0 || $body.find('.MuiDialog-container').length > 0) { - cy.task('log', 'Closing modal dialog'); + testLog.info( 'Closing modal dialog'); cy.get('body').type('{esc}'); cy.wait(1000); } }); // Step 3: Add content to the page editor - cy.task('log', '=== Adding Content to Page ==='); + testLog.info( '=== Adding Content to Page ==='); // Find the editor and add content cy.get('[contenteditable="true"]').then($editors => { - cy.task('log', `Found ${$editors.length} editable elements`); + testLog.info( `Found ${$editors.length} editable elements`); // Look for the main editor (not the title) let editorFound = false; @@ -87,7 +88,7 @@ describe('Page Edit Tests', () => { const $el = Cypress.$(el); // Skip title inputs if (!$el.attr('data-testid')?.includes('title') && !$el.hasClass('editor-title')) { - cy.task('log', `Using editor at index ${index}`); + testLog.info( `Using editor at index ${index}`); cy.wrap(el).click().type(testContent.join('{enter}')); editorFound = true; return false; // break the loop @@ -96,7 +97,7 @@ describe('Page Edit Tests', () => { if (!editorFound) { // Fallback: use the last contenteditable element - cy.task('log', 'Using fallback: last contenteditable element'); + testLog.info( 'Using fallback: last contenteditable element'); cy.wrap($editors.last()).click().type(testContent.join('{enter}')); } }); @@ -105,15 +106,15 @@ describe('Page Edit Tests', () => { cy.wait(2000); // Step 4: Verify the content was added - cy.task('log', '=== Verifying Content ==='); + testLog.info( '=== Verifying Content ==='); // Verify each line of content exists in the page testContent.forEach(line => { cy.contains(line).should('exist'); - cy.task('log', `✓ Found content: "${line}"`); + testLog.info( `✓ Found content: "${line}"`); }); - cy.task('log', '=== Test completed successfully ==='); + testLog.info( '=== Test completed successfully ==='); }); }); }); diff --git a/cypress/e2e/page/more-page-action.cy.ts b/cypress/e2e/page/more-page-action.cy.ts index eb3943f8..c55cb29f 100644 --- a/cypress/e2e/page/more-page-action.cy.ts +++ b/cypress/e2e/page/more-page-action.cy.ts @@ -2,6 +2,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { PageSelectors, byTestId, waitForReactUpdate } from '../../support/selectors'; import { generateRandomEmail } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('More Page Actions', () => { const newPageName = 'Renamed Test Page'; @@ -36,11 +37,11 @@ describe('More Page Actions', () => { cy.wait(2000); // Skip expanding space since Getting started is already visible - cy.task('log', 'Page already visible, skipping expand'); + testLog.info( 'Page already visible, skipping expand'); // Open the first available page from the sidebar, then trigger inline ViewActionsPopover via "..." on the row // Find the Getting started page and hover to reveal the more actions - cy.task('log', 'Looking for Getting started page'); + testLog.info( 'Looking for Getting started page'); // Find the page by its text content cy.contains('Getting started') @@ -54,7 +55,7 @@ describe('More Page Actions', () => { // Look for the more actions button - using PageSelectors PageSelectors.moreActionsButton().first().click({ force: true }); - cy.task('log', 'Clicked more actions button'); + testLog.info( 'Clicked more actions button'); // Verify core items in ViewActionsPopover // The menu should be open now, verify at least one of the common actions exists @@ -94,7 +95,7 @@ describe('More Page Actions', () => { // Find the Getting started page and open its more actions menu const originalPageName = 'Getting started'; - cy.task('log', `Opening More Actions for page: ${originalPageName}`); + testLog.info( `Opening More Actions for page: ${originalPageName}`); // Find the page by its text content and hover cy.contains(originalPageName) @@ -108,13 +109,13 @@ describe('More Page Actions', () => { // Look for the more actions button - using PageSelectors PageSelectors.moreActionsButton().first().click({ force: true }); - cy.task('log', 'Clicked more actions button'); + testLog.info( 'Clicked more actions button'); // Click on Duplicate option which is available in the dropdown cy.get('[data-slot="dropdown-menu-content"]').within(() => { cy.contains('Duplicate').click(); }); - cy.task('log', 'Clicked Duplicate option'); + testLog.info( 'Clicked Duplicate option'); // Wait for the duplication to complete waitForReactUpdate(2000); @@ -128,10 +129,10 @@ describe('More Page Actions', () => { const pageCount = $pages.filter((index: number, el: HTMLElement) => el.textContent?.includes('Getting started')).length; expect(pageCount).to.be.at.least(1); - cy.task('log', `Found ${pageCount} pages with 'Getting started' in the name`); + testLog.info( `Found ${pageCount} pages with 'Getting started' in the name`); }); - cy.task('log', 'Page successfully duplicated'); + testLog.info( 'Page successfully duplicated'); }); it.skip('should rename a page and verify the name persists after refresh', () => { @@ -161,7 +162,7 @@ describe('More Page Actions', () => { const originalPageName = 'Getting started'; const renamedPageName = `Renamed Page ${Date.now()}`; - cy.task('log', `Starting rename test: ${originalPageName} -> ${renamedPageName}`); + testLog.info( `Starting rename test: ${originalPageName} -> ${renamedPageName}`); // Find the page by its text content and hover cy.contains(originalPageName) @@ -175,7 +176,7 @@ describe('More Page Actions', () => { // Look for the more actions button - using PageSelectors PageSelectors.moreActionsButton().first().click({ force: true }); - cy.task('log', 'Clicked more actions button'); + testLog.info( 'Clicked more actions button'); // Wait for the dropdown menu to be visible cy.get('[data-slot="dropdown-menu-content"]', { timeout: 5000 }).should('be.visible'); @@ -185,7 +186,7 @@ describe('More Page Actions', () => { cy.contains('Rename').click(); }); - cy.task('log', 'Clicked Rename option'); + testLog.info( 'Clicked Rename option'); // Wait for the rename modal to appear cy.get(byTestId('rename-modal-input'), { timeout: 5000 }) @@ -193,25 +194,25 @@ describe('More Page Actions', () => { .clear() .type(renamedPageName); - cy.task('log', `Entered new page name: ${renamedPageName}`); + testLog.info( `Entered new page name: ${renamedPageName}`); // Click the save button cy.get(byTestId('rename-modal-save')).click(); - cy.task('log', 'Clicked save button'); + testLog.info( 'Clicked save button'); // Wait for the modal to close and the page to update waitForReactUpdate(2000); // Verify the page was renamed in the sidebar cy.contains(renamedPageName, { timeout: 10000 }).should('exist'); - cy.task('log', 'Page renamed successfully in sidebar'); + testLog.info( 'Page renamed successfully in sidebar'); // Also verify the original name doesn't exist anymore cy.contains(originalPageName).should('not.exist'); // Now refresh the page to verify the rename persisted - cy.task('log', 'Refreshing page to verify persistence...'); + testLog.info( 'Refreshing page to verify persistence...'); cy.reload(); // Wait for the page to reload completely @@ -221,7 +222,7 @@ describe('More Page Actions', () => { // Verify the renamed page still exists after refresh cy.contains(renamedPageName, { timeout: 10000 }).should('exist'); - cy.task('log', 'Renamed page persisted after refresh'); + testLog.info( 'Renamed page persisted after refresh'); // Verify the original name is still gone cy.contains(originalPageName).should('not.exist'); @@ -233,6 +234,6 @@ describe('More Page Actions', () => { // Verify we're on the renamed page by checking the URL or page content cy.url().should('include', '/app'); - cy.task('log', 'Rename test completed successfully - name persisted after refresh'); + testLog.info( 'Rename test completed successfully - name persisted after refresh'); }); }); diff --git a/cypress/e2e/page/publish-page.cy.ts b/cypress/e2e/page/publish-page.cy.ts index 4df54356..7a06abc5 100644 --- a/cypress/e2e/page/publish-page.cy.ts +++ b/cypress/e2e/page/publish-page.cy.ts @@ -2,6 +2,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { PageSelectors, ShareSelectors, SidebarSelectors, byTestId } from '../../support/selectors'; import { generateRandomEmail, logAppFlowyEnvironment } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('Publish Page Test', () => { let testEmail: string; @@ -31,47 +32,47 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'Signed in'); + testLog.info( 'Signed in'); // Wait for app to fully load - cy.task('log', 'Waiting for app to fully load...'); + testLog.info( 'Waiting for app to fully load...'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); cy.wait(2000); // 2. Open share popover TestTool.openSharePopover(); - cy.task('log', 'Share popover opened'); + testLog.info( 'Share popover opened'); // Verify that the Share and Publish tabs are visible cy.contains('Share').should('exist'); cy.contains('Publish').should('exist'); - cy.task('log', 'Share and Publish tabs verified'); + testLog.info( 'Share and Publish tabs verified'); // 3. Switch to Publish tab cy.contains('Publish').should('exist').click({ force: true }); cy.wait(1000); - cy.task('log', 'Switched to Publish tab'); + testLog.info( 'Switched to Publish tab'); // Verify Publish to Web section is visible cy.contains('Publish to Web').should('exist'); - cy.task('log', 'Publish to Web section verified'); + testLog.info( 'Publish to Web section verified'); // 4. Wait for the publish button to be visible and enabled - cy.task('log', 'Waiting for publish button to appear...'); + testLog.info( 'Waiting for publish button to appear...'); ShareSelectors.publishConfirmButton().should('be.visible').should('not.be.disabled'); - cy.task('log', 'Publish button is visible and enabled'); + testLog.info( 'Publish button is visible and enabled'); // 5. Click Publish button ShareSelectors.publishConfirmButton().click({ force: true }); - cy.task('log', 'Clicked Publish button'); + testLog.info( 'Clicked Publish button'); // Wait for publish to complete and URL to appear cy.wait(5000); // Verify that the page is now published by checking for published UI elements cy.get(byTestId('publish-namespace')).should('be.visible', { timeout: 10000 }); - cy.task('log', 'Page published successfully, URL elements visible'); + testLog.info( 'Page published successfully, URL elements visible'); // 6. Get the published URL by constructing it from UI elements cy.window().then((win) => { @@ -83,7 +84,7 @@ describe('Publish Page Test', () => { const namespaceText = namespace.trim(); const publishNameText = String(publishName).trim(); const publishedUrl = `${origin}/${namespaceText}/${publishNameText}`; - cy.task('log', `Constructed published URL: ${publishedUrl}`); + testLog.info( `Constructed published URL: ${publishedUrl}`); // 7. Find and click the copy link button // The copy button is an IconButton with LinkIcon SVG, inside a Tooltip @@ -99,19 +100,19 @@ describe('Publish Page Test', () => { .click({ force: true }); }); - cy.task('log', 'Clicked copy link button'); + testLog.info( 'Clicked copy link button'); // Wait for copy operation and notification to appear cy.wait(2000); - cy.task('log', 'Copy operation completed'); + testLog.info( 'Copy operation completed'); // 8. Open the URL in browser (copy button was clicked, URL is ready) - cy.task('log', `Opening published URL in browser: ${publishedUrl}`); + testLog.info( `Opening published URL in browser: ${publishedUrl}`); cy.visit(publishedUrl, { failOnStatusCode: false }); // 9. Verify the published page loads cy.url({ timeout: 10000 }).should('include', `/${namespaceText}/${publishNameText}`); - cy.task('log', 'Published page opened successfully'); + testLog.info( 'Published page opened successfully'); // Wait for page content to load cy.wait(3000); @@ -123,14 +124,14 @@ describe('Publish Page Test', () => { cy.get('body').then(($body) => { const bodyText = $body.text(); if (bodyText.includes('404') || bodyText.includes('Not Found')) { - cy.task('log', '⚠ Warning: Page might not be accessible (404 detected)'); + testLog.info( '⚠ Warning: Page might not be accessible (404 detected)'); } else { - cy.task('log', '✓ Published page verified and accessible'); + testLog.info( '✓ Published page verified and accessible'); } }); // 10. Go back to the app to unpublish the page - cy.task('log', 'Going back to app to unpublish the page'); + testLog.info( 'Going back to app to unpublish the page'); cy.visit('/app', { failOnStatusCode: false }); cy.wait(2000); @@ -140,34 +141,34 @@ describe('Publish Page Test', () => { // 11. Open share popover again to unpublish TestTool.openSharePopover(); - cy.task('log', 'Share popover opened for unpublishing'); + testLog.info( 'Share popover opened for unpublishing'); // Make sure we're on the Publish tab cy.contains('Publish').should('exist').click({ force: true }); cy.wait(1000); - cy.task('log', 'Switched to Publish tab for unpublishing'); + testLog.info( 'Switched to Publish tab for unpublishing'); // Wait for unpublish button to be visible ShareSelectors.unpublishButton().should('be.visible', { timeout: 10000 }); - cy.task('log', 'Unpublish button is visible'); + testLog.info( 'Unpublish button is visible'); // 12. Click Unpublish button ShareSelectors.unpublishButton().click({ force: true }); - cy.task('log', 'Clicked Unpublish button'); + testLog.info( 'Clicked Unpublish button'); // Wait for unpublish to complete cy.wait(3000); // Verify the page is now unpublished (Publish button should be visible again) ShareSelectors.publishConfirmButton().should('be.visible', { timeout: 10000 }); - cy.task('log', '✓ Page unpublished successfully'); + testLog.info( '✓ Page unpublished successfully'); // Close the share popover cy.get('body').type('{esc}'); cy.wait(1000); // 13. Try to visit the previously published URL - it should not be accessible - cy.task('log', `Attempting to visit unpublished URL: ${publishedUrl}`); + testLog.info( `Attempting to visit unpublished URL: ${publishedUrl}`); cy.visit(publishedUrl, { failOnStatusCode: false }); // Wait a bit for the page to load @@ -184,7 +185,7 @@ describe('Publish Page Test', () => { }).then((response) => { // Check status code first if (response.status !== 200) { - cy.task('log', `✓ Published page is no longer accessible (HTTP status: ${response.status})`); + testLog.info( `✓ Published page is no longer accessible (HTTP status: ${response.status})`); } else { // If status is 200, check the response body for error indicators const responseBody = response.body || ''; @@ -211,16 +212,16 @@ describe('Publish Page Test', () => { const wasRedirected = !currentUrl.includes(`/${namespaceText}/${publishNameText}`); if (hasErrorInResponse || hasErrorInBody || wasRedirected) { - cy.task('log', `✓ Published page is no longer accessible (unpublish verified)`); + testLog.info( `✓ Published page is no longer accessible (unpublish verified)`); } else { // If we still see the URL but no clear errors, check if page content is minimal/error-like // A valid published page would have substantial content const contentLength = bodyText.trim().length; if (contentLength < 100) { - cy.task('log', `✓ Published page is no longer accessible (minimal/empty content)`); + testLog.info( `✓ Published page is no longer accessible (minimal/empty content)`); } else { // This shouldn't happen, but log it for debugging - cy.task('log', `⚠ Note: Page appears accessible, but unpublish was executed successfully`); + testLog.info( `⚠ Note: Page appears accessible, but unpublish was executed successfully`); } } }); @@ -246,7 +247,7 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'Signed in'); + testLog.info( 'Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -258,7 +259,7 @@ describe('Publish Page Test', () => { cy.wait(1000); ShareSelectors.publishConfirmButton().should('be.visible').should('not.be.disabled').click({ force: true }); - cy.task('log', 'Clicked Publish button'); + testLog.info( 'Clicked Publish button'); cy.wait(5000); // Verify published @@ -270,18 +271,18 @@ describe('Publish Page Test', () => { cy.get(byTestId('publish-namespace')).should('be.visible').invoke('text').then((namespace) => { cy.get(byTestId('publish-name-input')).should('be.visible').invoke('val').then((publishName) => { const publishedUrl = `${origin}/${namespace.trim()}/${String(publishName).trim()}`; - cy.task('log', `Published URL: ${publishedUrl}`); + testLog.info( `Published URL: ${publishedUrl}`); // Click the Visit Site button ShareSelectors.visitSiteButton().should('be.visible').click({ force: true }); - cy.task('log', 'Clicked Visit Site button'); + testLog.info( 'Clicked Visit Site button'); // Wait for new window/tab to open cy.wait(2000); // Note: Cypress can't directly test window.open in a new tab, // but we can verify the button works by checking if it exists and is clickable - cy.task('log', '✓ Visit Site button is functional'); + testLog.info( '✓ Visit Site button is functional'); }); }); }); @@ -301,7 +302,7 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'Signed in'); + testLog.info( 'Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -323,7 +324,7 @@ describe('Publish Page Test', () => { cy.get(byTestId('publish-name-input')).invoke('val').then((originalName) => { const namespaceText = namespace.trim(); const originalNameText = String(originalName).trim(); - cy.task('log', `Original publish name: ${originalNameText}`); + testLog.info( `Original publish name: ${originalNameText}`); // Edit the publish name directly in the input const newPublishName = `custom-name-${Date.now()}`; @@ -332,17 +333,17 @@ describe('Publish Page Test', () => { .type(newPublishName) .blur(); - cy.task('log', `Changed publish name to: ${newPublishName}`); + testLog.info( `Changed publish name to: ${newPublishName}`); cy.wait(3000); // Wait for name update // Verify the new URL works const newPublishedUrl = `${origin}/${namespaceText}/${newPublishName}`; - cy.task('log', `New published URL: ${newPublishedUrl}`); + testLog.info( `New published URL: ${newPublishedUrl}`); cy.visit(newPublishedUrl, { failOnStatusCode: false }); cy.wait(3000); cy.url().should('include', `/${namespaceText}/${newPublishName}`); - cy.task('log', '✓ New publish name URL works correctly'); + testLog.info( '✓ New publish name URL works correctly'); }); }); }); @@ -365,14 +366,14 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'Signed in'); + testLog.info( 'Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); cy.wait(2000); // Add initial content to the page - cy.task('log', 'Adding initial content to page'); + testLog.info( 'Adding initial content to page'); cy.get('[contenteditable="true"]').then(($editors) => { let editorFound = false; $editors.each((index: number, el: HTMLElement) => { @@ -397,7 +398,7 @@ describe('Publish Page Test', () => { ShareSelectors.publishConfirmButton().should('be.visible').click({ force: true }); cy.wait(5000); cy.get(byTestId('publish-namespace')).should('be.visible', { timeout: 10000 }); - cy.task('log', '✓ First publish successful'); + testLog.info( '✓ First publish successful'); // Get published URL cy.window().then((win) => { @@ -405,17 +406,17 @@ describe('Publish Page Test', () => { cy.get(byTestId('publish-namespace')).invoke('text').then((namespace) => { cy.get(byTestId('publish-name-input')).invoke('val').then((publishName) => { const publishedUrl = `${origin}/${namespace.trim()}/${String(publishName).trim()}`; - cy.task('log', `Published URL: ${publishedUrl}`); + testLog.info( `Published URL: ${publishedUrl}`); // Verify initial content is published - cy.task('log', 'Verifying initial published content'); + testLog.info( 'Verifying initial published content'); cy.visit(publishedUrl, { failOnStatusCode: false }); cy.wait(3000); cy.get('body').should('contain.text', initialContent); - cy.task('log', '✓ Initial content verified on published page'); + testLog.info( '✓ Initial content verified on published page'); // Go back to app and modify content - cy.task('log', 'Going back to app to modify content'); + testLog.info( 'Going back to app to modify content'); cy.visit('/app', { failOnStatusCode: false }); cy.wait(2000); SidebarSelectors.pageHeader().should('be.visible', { timeout: 10000 }); @@ -426,7 +427,7 @@ describe('Publish Page Test', () => { cy.wait(3000); // Modify the page content - cy.task('log', 'Modifying page content'); + testLog.info( 'Modifying page content'); cy.get('[contenteditable="true"]').then(($editors) => { let editorFound = false; $editors.each((index: number, el: HTMLElement) => { @@ -444,7 +445,7 @@ describe('Publish Page Test', () => { cy.wait(5000); // Wait for content to save // Republish to sync the updated content - cy.task('log', 'Republishing to sync updated content'); + testLog.info( 'Republishing to sync updated content'); TestTool.openSharePopover(); cy.contains('Publish').should('exist').click({ force: true }); cy.wait(1000); @@ -458,16 +459,16 @@ describe('Publish Page Test', () => { ShareSelectors.publishConfirmButton().should('be.visible').click({ force: true }); cy.wait(5000); cy.get(byTestId('publish-namespace')).should('be.visible', { timeout: 10000 }); - cy.task('log', '✓ Republished successfully'); + testLog.info( '✓ Republished successfully'); // Verify updated content is published - cy.task('log', 'Verifying updated content on published page'); + testLog.info( 'Verifying updated content on published page'); cy.visit(publishedUrl, { failOnStatusCode: false }); cy.wait(5000); // Verify the updated content appears (with retry logic) cy.get('body', { timeout: 15000 }).should('contain.text', updatedContent); - cy.task('log', '✓ Updated content verified on published page'); + testLog.info( '✓ Updated content verified on published page'); }); }); }); @@ -487,7 +488,7 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'Signed in'); + testLog.info( 'Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -504,7 +505,7 @@ describe('Publish Page Test', () => { // Try to set invalid publish name with spaces cy.get(byTestId('publish-name-input')).invoke('val').then((originalName) => { - cy.task('log', `Original name: ${originalName}`); + testLog.info( `Original name: ${originalName}`); // Try to set name with space (should be rejected) cy.get(byTestId('publish-name-input')) @@ -521,9 +522,9 @@ describe('Publish Page Test', () => { cy.get(byTestId('publish-name-input')).invoke('val').then((currentName) => { // Name should not contain spaces (validation should prevent it) if (String(currentName).includes(' ')) { - cy.task('log', '⚠ Warning: Invalid characters were not rejected'); + testLog.info( '⚠ Warning: Invalid characters were not rejected'); } else { - cy.task('log', '✓ Invalid characters (spaces) were rejected'); + testLog.info( '✓ Invalid characters (spaces) were rejected'); } }); }); @@ -544,7 +545,7 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'Signed in'); + testLog.info( 'Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -566,7 +567,7 @@ describe('Publish Page Test', () => { cy.get('div.flex.items-center.justify-between').contains(/comments|comment/i).parent().within(() => { cy.get('input[type="checkbox"]').then(($checkbox) => { const initialCommentsState = $checkbox.is(':checked'); - cy.task('log', `Initial comments state: ${initialCommentsState}`); + testLog.info( `Initial comments state: ${initialCommentsState}`); // Toggle comments by clicking the switch cy.get('input[type="checkbox"]').click({ force: true }); @@ -574,9 +575,9 @@ describe('Publish Page Test', () => { cy.get('input[type="checkbox"]').then(($checkboxAfter) => { const newCommentsState = $checkboxAfter.is(':checked'); - cy.task('log', `Comments state after toggle: ${newCommentsState}`); + testLog.info( `Comments state after toggle: ${newCommentsState}`); expect(newCommentsState).to.not.equal(initialCommentsState); - cy.task('log', '✓ Comments switch toggled successfully'); + testLog.info( '✓ Comments switch toggled successfully'); }); }); }); @@ -585,7 +586,7 @@ describe('Publish Page Test', () => { cy.get('div.flex.items-center.justify-between').contains(/duplicate|template/i).parent().within(() => { cy.get('input[type="checkbox"]').then(($checkbox) => { const initialDuplicateState = $checkbox.is(':checked'); - cy.task('log', `Initial duplicate state: ${initialDuplicateState}`); + testLog.info( `Initial duplicate state: ${initialDuplicateState}`); // Toggle duplicate cy.get('input[type="checkbox"]').click({ force: true }); @@ -593,9 +594,9 @@ describe('Publish Page Test', () => { cy.get('input[type="checkbox"]').then(($checkboxAfter) => { const newDuplicateState = $checkboxAfter.is(':checked'); - cy.task('log', `Duplicate state after toggle: ${newDuplicateState}`); + testLog.info( `Duplicate state after toggle: ${newDuplicateState}`); expect(newDuplicateState).to.not.equal(initialDuplicateState); - cy.task('log', '✓ Duplicate switch toggled successfully'); + testLog.info( '✓ Duplicate switch toggled successfully'); }); }); }); @@ -616,7 +617,7 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'Signed in'); + testLog.info( 'Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -639,7 +640,7 @@ describe('Publish Page Test', () => { cy.get(byTestId('publish-namespace')).invoke('text').then((namespace) => { cy.get(byTestId('publish-name-input')).invoke('val').then((publishName) => { firstPublishedUrl = `${origin}/${namespace.trim()}/${String(publishName).trim()}`; - cy.task('log', `First published URL: ${firstPublishedUrl}`); + testLog.info( `First published URL: ${firstPublishedUrl}`); // Close and reopen share popover cy.get('body').type('{esc}'); @@ -654,10 +655,10 @@ describe('Publish Page Test', () => { cy.get(byTestId('publish-namespace')).invoke('text').then((namespace2) => { cy.get(byTestId('publish-name-input')).invoke('val').then((publishName2) => { const secondPublishedUrl = `${origin}/${namespace2.trim()}/${String(publishName2).trim()}`; - cy.task('log', `Second check URL: ${secondPublishedUrl}`); + testLog.info( `Second check URL: ${secondPublishedUrl}`); expect(secondPublishedUrl).to.equal(firstPublishedUrl); - cy.task('log', '✓ Published URL remains consistent across multiple opens'); + testLog.info( '✓ Published URL remains consistent across multiple opens'); }); }); }); @@ -679,14 +680,14 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'Signed in'); + testLog.info( 'Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); cy.wait(2000); // Navigate to the To-dos database - cy.task('log', 'Navigating to To-dos database'); + testLog.info( 'Navigating to To-dos database'); cy.contains('To-dos', { timeout: 10000 }).should('be.visible').click({ force: true }); cy.wait(5000); // Wait for database to load @@ -694,7 +695,7 @@ describe('Publish Page Test', () => { cy.get('body').then(($body: JQuery) => { const hasDialog = $body.find('[role="dialog"]').length > 0 || $body.find('.MuiDialog-container').length > 0; if (hasDialog) { - cy.task('log', 'Closing modal dialog'); + testLog.info( 'Closing modal dialog'); cy.get('body').type('{esc}'); cy.wait(2000); // Try again if still open @@ -708,7 +709,7 @@ describe('Publish Page Test', () => { }); // Verify we're on a database view (not a document) - cy.task('log', 'Verifying database view loaded'); + testLog.info( 'Verifying database view loaded'); cy.get('body').should('exist'); // Database should be loaded // Wait a bit more for database to fully initialize and ensure no modals @@ -718,39 +719,39 @@ describe('Publish Page Test', () => { ShareSelectors.shareButton().should('be.visible', { timeout: 10000 }); // Open share popover and publish - cy.task('log', 'Opening share popover to publish database'); + testLog.info( 'Opening share popover to publish database'); TestTool.openSharePopover(); - cy.task('log', 'Share popover opened'); + testLog.info( 'Share popover opened'); // Verify that the Share and Publish tabs are visible cy.contains('Share').should('exist'); cy.contains('Publish').should('exist'); - cy.task('log', 'Share and Publish tabs verified'); + testLog.info( 'Share and Publish tabs verified'); // Switch to Publish tab cy.contains('Publish').should('exist').click({ force: true }); cy.wait(1000); - cy.task('log', 'Switched to Publish tab'); + testLog.info( 'Switched to Publish tab'); // Verify Publish to Web section is visible cy.contains('Publish to Web').should('exist'); - cy.task('log', 'Publish to Web section verified'); + testLog.info( 'Publish to Web section verified'); // Wait for the publish button to be visible and enabled - cy.task('log', 'Waiting for publish button to appear...'); + testLog.info( 'Waiting for publish button to appear...'); ShareSelectors.publishConfirmButton().should('be.visible').should('not.be.disabled'); - cy.task('log', 'Publish button is visible and enabled'); + testLog.info( 'Publish button is visible and enabled'); // Click Publish button ShareSelectors.publishConfirmButton().click({ force: true }); - cy.task('log', 'Clicked Publish button'); + testLog.info( 'Clicked Publish button'); // Wait for publish to complete and URL to appear cy.wait(5000); // Verify that the database is now published by checking for published UI elements cy.get(byTestId('publish-namespace')).should('be.visible', { timeout: 10000 }); - cy.task('log', 'Database published successfully, URL elements visible'); + testLog.info( 'Database published successfully, URL elements visible'); // Get the published URL cy.window().then((win) => { @@ -762,15 +763,15 @@ describe('Publish Page Test', () => { const namespaceText = namespace.trim(); const publishNameText = String(publishName).trim(); const publishedUrl = `${origin}/${namespaceText}/${publishNameText}`; - cy.task('log', `Constructed published database URL: ${publishedUrl}`); + testLog.info( `Constructed published database URL: ${publishedUrl}`); // Visit the published database URL - cy.task('log', `Opening published database URL: ${publishedUrl}`); + testLog.info( `Opening published database URL: ${publishedUrl}`); cy.visit(publishedUrl, { failOnStatusCode: false }); // Verify the published database loads cy.url({ timeout: 10000 }).should('include', `/${namespaceText}/${publishNameText}`); - cy.task('log', 'Published database opened successfully'); + testLog.info( 'Published database opened successfully'); // Wait for database content to load cy.wait(5000); @@ -782,15 +783,15 @@ describe('Publish Page Test', () => { cy.get('body').then(($body) => { const bodyText = $body.text(); if (bodyText.includes('404') || bodyText.includes('Not Found')) { - cy.task('log', '⚠ Warning: Database might not be accessible (404 detected)'); + testLog.info( '⚠ Warning: Database might not be accessible (404 detected)'); } else { // Database should be visible - might have grid/board/calendar elements - cy.task('log', '✓ Published database verified and accessible'); + testLog.info( '✓ Published database verified and accessible'); // Additional verification: Check if database-specific elements exist // Databases typically have table/grid structures or views cy.get('body').should('exist'); - cy.task('log', '✓ Database view elements present'); + testLog.info( '✓ Database view elements present'); } }); }); diff --git a/cypress/e2e/page/share-page.cy.ts b/cypress/e2e/page/share-page.cy.ts index 253654d9..828afdaf 100644 --- a/cypress/e2e/page/share-page.cy.ts +++ b/cypress/e2e/page/share-page.cy.ts @@ -2,6 +2,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { PageSelectors, SidebarSelectors, ShareSelectors, waitForReactUpdate } from '../../support/selectors'; import { generateRandomEmail, logAppFlowyEnvironment } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('Share Page Test', () => { let userAEmail: string; @@ -31,38 +32,38 @@ describe('Share Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(userAEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'User A signed in'); + testLog.info( 'User A signed in'); // Wait for app to fully load - cy.task('log', 'Waiting for app to fully load...'); + testLog.info( 'Waiting for app to fully load...'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); cy.wait(2000); // 2. Open share popover TestTool.openSharePopover(); - cy.task('log', 'Share popover opened'); + testLog.info( 'Share popover opened'); // Verify that the Share and Publish tabs are visible cy.contains('Share').should('exist'); cy.contains('Publish').should('exist'); - cy.task('log', 'Share and Publish tabs verified'); + testLog.info( 'Share and Publish tabs verified'); // 3. Make sure we're on the Share tab (click it if needed) ShareSelectors.sharePopover().then(($popover) => { const hasInviteInput = $popover.find('[data-slot="email-tag-input"]').length > 0; if (!hasInviteInput) { - cy.task('log', 'Switching to Share tab'); + testLog.info( 'Switching to Share tab'); cy.contains('Share').should('exist').click({ force: true }); waitForReactUpdate(1000); } else { - cy.task('log', 'Already on Share tab'); + testLog.info( 'Already on Share tab'); } }); // 4. Find the email input field and type user B's email - cy.task('log', `Inviting user B: ${userBEmail}`); + testLog.info( `Inviting user B: ${userBEmail}`); ShareSelectors.sharePopover().within(() => { // Find the input field inside the email-tag-input container cy.get('[data-slot="email-tag-input"]') @@ -86,22 +87,22 @@ describe('Share Page Test', () => { .should('not.be.disabled') .click({ force: true }); - cy.task('log', 'Clicked Invite button'); + testLog.info( 'Clicked Invite button'); }); // 5. Wait for the invite to be sent and user B to appear in the list - cy.task('log', 'Waiting for user B to appear in the people list...'); + testLog.info( 'Waiting for user B to appear in the people list...'); waitForReactUpdate(3000); // Verify user B appears in the "People with access" section ShareSelectors.sharePopover().within(() => { cy.contains('People with access', { timeout: 10000 }).should('be.visible'); cy.contains(userBEmail, { timeout: 10000 }).should('be.visible'); - cy.task('log', 'User B successfully added to the page'); + testLog.info( 'User B successfully added to the page'); }); // 6. Find user B's access level dropdown and click it - cy.task('log', 'Finding user B\'s access dropdown...'); + testLog.info( 'Finding user B\'s access dropdown...'); ShareSelectors.sharePopover().within(() => { // Find the person item containing user B's email // The PersonItem component renders the email in a div with text-xs class @@ -120,13 +121,13 @@ describe('Share Page Test', () => { .should('be.visible') .click({ force: true }); - cy.task('log', 'Opened access level dropdown'); + testLog.info( 'Opened access level dropdown'); waitForReactUpdate(500); }); }); // 7. Click "Remove access" option in the dropdown menu - cy.task('log', 'Clicking Remove access...'); + testLog.info( 'Clicking Remove access...'); // The dropdown menu has role="menu" or uses DropdownMenuContent cy.get('[role="menu"]', { timeout: 5000 }) .should('be.visible') @@ -143,15 +144,15 @@ describe('Share Page Test', () => { waitForReactUpdate(3000); // 8. Verify user B is removed from the list - cy.task('log', 'Verifying user B is removed...'); + testLog.info( 'Verifying user B is removed...'); ShareSelectors.sharePopover().within(() => { // User B should no longer appear in the people list cy.contains(userBEmail).should('not.exist'); - cy.task('log', '✓ User B successfully removed from access list'); + testLog.info( '✓ User B successfully removed from access list'); }); // 9. Close the share popover and verify user A still has access to the page - cy.task('log', 'Closing share popover and verifying page is still accessible...'); + testLog.info( 'Closing share popover and verifying page is still accessible...'); cy.get('body').type('{esc}'); waitForReactUpdate(1000); @@ -161,8 +162,8 @@ describe('Share Page Test', () => { // Verify the page content is still visible (user A should still have access) // Check that we can still see page elements cy.get('body').should('be.visible'); - cy.task('log', '✓ User A still has access to the page after removing user B'); - cy.task('log', 'Test completed successfully'); + testLog.info( '✓ User A still has access to the page after removing user B'); + testLog.info( 'Test completed successfully'); }); }); @@ -179,7 +180,7 @@ describe('Share Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(userAEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'User A signed in'); + testLog.info( 'User A signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -223,11 +224,11 @@ describe('Share Page Test', () => { // Should show "Can view" or "Read only" initially cy.get('button').contains(/view|read/i).should('be.visible'); }); - cy.task('log', 'User B added with default view access'); + testLog.info( 'User B added with default view access'); }); // Change access level to "Can edit" - cy.task('log', 'Changing user B access level to "Can edit"...'); + testLog.info( 'Changing user B access level to "Can edit"...'); ShareSelectors.sharePopover().within(() => { cy.contains(userBEmail) .closest('div.group') @@ -261,12 +262,12 @@ describe('Share Page Test', () => { .within(() => { // Should now show "Can edit" or "Read and write" cy.get('button').contains(/edit|write/i, { timeout: 10000 }).should('be.visible'); - cy.task('log', '✓ User B access level successfully changed to "Can edit"'); + testLog.info( '✓ User B access level successfully changed to "Can edit"'); }); }); cy.get('body').type('{esc}'); - cy.task('log', 'Test completed successfully'); + testLog.info( 'Test completed successfully'); }); }); @@ -286,7 +287,7 @@ describe('Share Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(userAEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'User A signed in'); + testLog.info( 'User A signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -302,7 +303,7 @@ describe('Share Page Test', () => { }); // Invite multiple users - cy.task('log', `Inviting multiple users: ${userBEmail}, ${userCEmail}, ${userDEmail}`); + testLog.info( `Inviting multiple users: ${userBEmail}, ${userCEmail}, ${userDEmail}`); ShareSelectors.sharePopover().within(() => { const emails = [userBEmail, userCEmail, userDEmail]; @@ -334,11 +335,11 @@ describe('Share Page Test', () => { cy.contains(userBEmail, { timeout: 10000 }).should('be.visible'); cy.contains(userCEmail, { timeout: 10000 }).should('be.visible'); cy.contains(userDEmail, { timeout: 10000 }).should('be.visible'); - cy.task('log', '✓ All users successfully added to the page'); + testLog.info( '✓ All users successfully added to the page'); }); cy.get('body').type('{esc}'); - cy.task('log', 'Test completed successfully'); + testLog.info( 'Test completed successfully'); }); }); @@ -355,7 +356,7 @@ describe('Share Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(userAEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'User A signed in'); + testLog.info( 'User A signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -371,7 +372,7 @@ describe('Share Page Test', () => { }); // Set access level to "Can edit" before inviting - cy.task('log', `Inviting user B with "Can edit" access level`); + testLog.info( `Inviting user B with "Can edit" access level`); ShareSelectors.sharePopover().within(() => { // First, find and click the access level selector (if it exists) // The access level selector might be a button or dropdown near the invite input @@ -413,14 +414,14 @@ describe('Share Page Test', () => { // Verify user B is added ShareSelectors.sharePopover().within(() => { cy.contains(userBEmail, { timeout: 10000 }).should('be.visible'); - cy.task('log', 'User B successfully invited'); + testLog.info( 'User B successfully invited'); // Note: The actual access level verification depends on UI implementation // If the access level selector works, user B should have edit access }); cy.get('body').type('{esc}'); - cy.task('log', 'Test completed successfully'); + testLog.info( 'Test completed successfully'); }); }); @@ -437,7 +438,7 @@ describe('Share Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(userAEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'User A signed in'); + testLog.info( 'User A signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -485,16 +486,16 @@ describe('Share Page Test', () => { const groupText = $elements.text().toLowerCase(); const hasPending = groupText.includes('pending'); if (hasPending) { - cy.task('log', '✓ User B shows pending status'); + testLog.info( '✓ User B shows pending status'); } else { - cy.task('log', 'Note: Pending status may not be visible immediately'); + testLog.info( 'Note: Pending status may not be visible immediately'); } }); }); }); cy.get('body').type('{esc}'); - cy.task('log', 'Test completed successfully'); + testLog.info( 'Test completed successfully'); }); }); @@ -513,7 +514,7 @@ describe('Share Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(userAEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'User A signed in'); + testLog.info( 'User A signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -529,7 +530,7 @@ describe('Share Page Test', () => { }); // Invite two users - cy.task('log', `Inviting users: ${userBEmail}, ${userCEmail}`); + testLog.info( `Inviting users: ${userBEmail}, ${userCEmail}`); ShareSelectors.sharePopover().within(() => { [userBEmail, userCEmail].forEach((email) => { cy.get('[data-slot="email-tag-input"]') @@ -556,11 +557,11 @@ describe('Share Page Test', () => { ShareSelectors.sharePopover().within(() => { cy.contains(userBEmail, { timeout: 10000 }).should('be.visible'); cy.contains(userCEmail, { timeout: 10000 }).should('be.visible'); - cy.task('log', 'Both users added successfully'); + testLog.info( 'Both users added successfully'); }); // Remove user B's access - cy.task('log', 'Removing user B access...'); + testLog.info( 'Removing user B access...'); ShareSelectors.sharePopover().within(() => { cy.contains(userBEmail) .closest('div.group') @@ -590,11 +591,11 @@ describe('Share Page Test', () => { ShareSelectors.sharePopover().within(() => { cy.contains(userBEmail).should('not.exist'); cy.contains(userCEmail).should('be.visible'); - cy.task('log', '✓ User B removed, User C still has access'); + testLog.info( '✓ User B removed, User C still has access'); }); // Remove user C's access - cy.task('log', 'Removing user C access...'); + testLog.info( 'Removing user C access...'); ShareSelectors.sharePopover().within(() => { cy.contains(userCEmail) .closest('div.group') @@ -624,7 +625,7 @@ describe('Share Page Test', () => { ShareSelectors.sharePopover().within(() => { cy.contains(userBEmail).should('not.exist'); cy.contains(userCEmail).should('not.exist'); - cy.task('log', '✓ Both users successfully removed'); + testLog.info( '✓ Both users successfully removed'); }); // Verify user A still has access @@ -632,8 +633,8 @@ describe('Share Page Test', () => { waitForReactUpdate(1000); cy.url().should('include', '/app'); cy.get('body').should('be.visible'); - cy.task('log', '✓ User A still has access after removing all guests'); - cy.task('log', 'Test completed successfully'); + testLog.info( '✓ User A still has access after removing all guests'); + testLog.info( 'Test completed successfully'); }); }); @@ -650,7 +651,7 @@ describe('Share Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(userAEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'User A signed in'); + testLog.info( 'User A signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -658,10 +659,10 @@ describe('Share Page Test', () => { // Get the current page URL to verify we stay on it cy.url().then((initialUrl) => { - cy.task('log', `Initial URL: ${initialUrl}`); + testLog.info( `Initial URL: ${initialUrl}`); TestTool.openSharePopover(); - cy.task('log', 'Share popover opened'); + testLog.info( 'Share popover opened'); ShareSelectors.sharePopover().then(($popover) => { const hasInviteInput = $popover.find('[data-slot="email-tag-input"]').length > 0; @@ -672,7 +673,7 @@ describe('Share Page Test', () => { }); // Invite user B - cy.task('log', `Inviting user B: ${userBEmail}`); + testLog.info( `Inviting user B: ${userBEmail}`); ShareSelectors.sharePopover().within(() => { cy.get('[data-slot="email-tag-input"]') .find('input[type="text"]') @@ -696,11 +697,11 @@ describe('Share Page Test', () => { ShareSelectors.sharePopover().within(() => { cy.contains('People with access', { timeout: 10000 }).should('be.visible'); cy.contains(userBEmail, { timeout: 10000 }).should('be.visible'); - cy.task('log', 'User B successfully added'); + testLog.info( 'User B successfully added'); }); // Remove user B's access (NOT user A's own access) - cy.task('log', 'Removing user B\'s access (NOT user A\'s own access)...'); + testLog.info( 'Removing user B\'s access (NOT user A\'s own access)...'); ShareSelectors.sharePopover().within(() => { cy.contains(userBEmail) .should('be.visible') @@ -731,14 +732,14 @@ describe('Share Page Test', () => { // Verify user B is removed ShareSelectors.sharePopover().within(() => { cy.contains(userBEmail).should('not.exist'); - cy.task('log', '✓ User B removed'); + testLog.info( '✓ User B removed'); }); // CRITICAL: Verify we're still on the SAME page URL (no navigation happened) cy.url().should('eq', initialUrl); - cy.task('log', `✓ URL unchanged: ${initialUrl}`); - cy.task('log', '✓ Navigation did NOT occur when removing another user\'s access'); - cy.task('log', '✓ Fix verified: No navigation when removing someone else\'s access'); + testLog.info( `✓ URL unchanged: ${initialUrl}`); + testLog.info( '✓ Navigation did NOT occur when removing another user\'s access'); + testLog.info( '✓ Fix verified: No navigation when removing someone else\'s access'); }); }); }); @@ -759,7 +760,7 @@ describe('Share Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(userAEmail).then(() => { cy.url().should('include', '/app'); - cy.task('log', 'User A signed in'); + testLog.info( 'User A signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -767,10 +768,10 @@ describe('Share Page Test', () => { // Get the current page URL to verify we stay on it cy.url().then((initialUrl) => { - cy.task('log', `Initial URL: ${initialUrl}`); + testLog.info( `Initial URL: ${initialUrl}`); TestTool.openSharePopover(); - cy.task('log', 'Share popover opened'); + testLog.info( 'Share popover opened'); ShareSelectors.sharePopover().then(($popover) => { const hasInviteInput = $popover.find('[data-slot="email-tag-input"]').length > 0; @@ -781,7 +782,7 @@ describe('Share Page Test', () => { }); // Invite user B - cy.task('log', `Inviting user B: ${userBEmail}`); + testLog.info( `Inviting user B: ${userBEmail}`); ShareSelectors.sharePopover().within(() => { cy.get('[data-slot="email-tag-input"]') .find('input[type="text"]') @@ -805,15 +806,15 @@ describe('Share Page Test', () => { ShareSelectors.sharePopover().within(() => { cy.contains('People with access', { timeout: 10000 }).should('be.visible'); cy.contains(userBEmail, { timeout: 10000 }).should('be.visible'); - cy.task('log', 'User B successfully added'); + testLog.info( 'User B successfully added'); }); // Record time before removal to verify outline refresh timing const startTime = Date.now(); - cy.task('log', `Start time: ${startTime}`); + testLog.info( `Start time: ${startTime}`); // Remove user B's access (NOT user A's own access) - cy.task('log', 'Removing user B\'s access (verifying outline refresh mechanism)...'); + testLog.info( 'Removing user B\'s access (verifying outline refresh mechanism)...'); ShareSelectors.sharePopover().within(() => { cy.contains(userBEmail) .should('be.visible') @@ -845,20 +846,20 @@ describe('Share Page Test', () => { const endTime = Date.now(); const elapsed = endTime - startTime; - cy.task('log', `End time: ${endTime}, Elapsed: ${elapsed}ms`); + testLog.info( `End time: ${endTime}, Elapsed: ${elapsed}ms`); // Verify user B is removed ShareSelectors.sharePopover().within(() => { cy.contains(userBEmail).should('not.exist'); - cy.task('log', '✓ User B removed'); + testLog.info( '✓ User B removed'); }); // CRITICAL: Verify we're still on the SAME page URL (no navigation happened) cy.url().should('eq', initialUrl); - cy.task('log', `✓ URL unchanged: ${initialUrl}`); - cy.task('log', '✓ Navigation did NOT occur when removing another user\'s access'); - cy.task('log', '✓ Outline refresh mechanism verified - fix working correctly'); - cy.task('log', `✓ Operation completed in ${elapsed}ms (includes outline refresh time)`); + testLog.info( `✓ URL unchanged: ${initialUrl}`); + testLog.info( '✓ Navigation did NOT occur when removing another user\'s access'); + testLog.info( '✓ Outline refresh mechanism verified - fix working correctly'); + testLog.info( `✓ Operation completed in ${elapsed}ms (includes outline refresh time)`); }); }); }); diff --git a/cypress/e2e/space/create-space.cy.ts b/cypress/e2e/space/create-space.cy.ts index 9a3f2258..b46ffc17 100644 --- a/cypress/e2e/space/create-space.cy.ts +++ b/cypress/e2e/space/create-space.cy.ts @@ -2,6 +2,7 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { PageSelectors, SpaceSelectors, SidebarSelectors, byTestId, waitForReactUpdate } from '../../support/selectors'; import { generateRandomEmail, logAppFlowyEnvironment } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('Space Creation Tests', () => { let testEmail: string; @@ -33,7 +34,7 @@ describe('Space Creation Tests', () => { }); // Step 1: Login - cy.task('log', '=== Step 1: Login ==='); + testLog.info( '=== Step 1: Login ==='); cy.visit('/login', { failOnStatusCode: false }); cy.wait(2000); @@ -42,7 +43,7 @@ describe('Space Creation Tests', () => { cy.url().should('include', '/app'); // Wait for the app to fully load - cy.task('log', 'Waiting for app to fully load...'); + testLog.info( 'Waiting for app to fully load...'); // Wait for the loading screen to disappear and main app to appear cy.get('body', { timeout: 30000 }).should('not.contain', 'Welcome!'); @@ -56,15 +57,15 @@ describe('Space Creation Tests', () => { // Additional wait for stability cy.wait(2000); - cy.task('log', 'App loaded successfully'); + testLog.info( 'App loaded successfully'); // Step 2: Find the first space and open its more actions menu - cy.task('log', '=== Step 2: Opening space more actions menu ==='); + testLog.info( '=== Step 2: Opening space more actions menu ==='); // Get the first space item and click more actions // With the test environment check, the button is always visible in tests SpaceSelectors.items().first().then($space => { - cy.task('log', 'Found first space, clicking more actions...'); + testLog.info( 'Found first space, clicking more actions...'); // Click the more actions button for spaces // It's always visible in test environment @@ -73,32 +74,32 @@ describe('Space Creation Tests', () => { .should('be.visible') .click(); - cy.task('log', 'Clicked space more actions button'); + testLog.info( 'Clicked space more actions button'); }); // Wait for the dropdown menu to appear cy.wait(1000); // Step 3: Click on "Create New Space" option - cy.task('log', '=== Step 3: Clicking Create New Space option ==='); + testLog.info( '=== Step 3: Clicking Create New Space option ==='); cy.get(byTestId('create-new-space-button')) .should('be.visible') .click(); - cy.task('log', 'Clicked Create New Space button'); + testLog.info( 'Clicked Create New Space button'); // Wait for modal to appear cy.wait(1000); // Step 4: Fill in the space details - cy.task('log', '=== Step 4: Filling space creation form ==='); + testLog.info( '=== Step 4: Filling space creation form ==='); // Verify the modal is visible cy.get(byTestId('create-space-modal')) .should('be.visible'); - cy.task('log', 'Create Space modal is visible'); + testLog.info( 'Create Space modal is visible'); // Enter space name cy.get(byTestId('space-name-input')) @@ -106,31 +107,31 @@ describe('Space Creation Tests', () => { .clear() .type(spaceName); - cy.task('log', `Entered space name: ${spaceName}`); + testLog.info( `Entered space name: ${spaceName}`); // Optional: Click on space icon button to set an icon (skip for simplicity) // Optional: Change space permission (default is Public, keep it) // Step 5: Save the new space - cy.task('log', '=== Step 5: Saving new space ==='); + testLog.info( '=== Step 5: Saving new space ==='); // Click the Save button cy.get(byTestId('modal-ok-button')) .should('be.visible') .click(); - cy.task('log', 'Clicked Save button'); + testLog.info( 'Clicked Save button'); // Wait for the modal to close and space to be created cy.wait(3000); // Step 6: Verify the new space appears in the sidebar - cy.task('log', '=== Step 6: Verifying new space in sidebar ==='); + testLog.info( '=== Step 6: Verifying new space in sidebar ==='); // Check that the new space exists in the sidebar SpaceSelectors.names().then($spaces => { const spaceNames = Array.from($spaces).map((el: Element) => el.textContent?.trim()); - cy.task('log', `Spaces in sidebar: ${spaceNames.join(', ')}`); + testLog.info( `Spaces in sidebar: ${spaceNames.join(', ')}`); // Check if our space exists const spaceExists = spaceNames.some(name => @@ -138,11 +139,11 @@ describe('Space Creation Tests', () => { ); if (spaceExists) { - cy.task('log', `✓ New space "${spaceName}" found in sidebar`); + testLog.info( `✓ New space "${spaceName}" found in sidebar`); } else { // Sometimes the space might be created but not immediately visible // Let's refresh the outline - cy.task('log', 'Space not immediately visible, checking again...'); + testLog.info( 'Space not immediately visible, checking again...'); cy.wait(2000); // Check again @@ -153,16 +154,16 @@ describe('Space Creation Tests', () => { ); if (spaceExistsNow) { - cy.task('log', `✓ New space "${spaceName}" found after refresh`); + testLog.info( `✓ New space "${spaceName}" found after refresh`); } else { - cy.task('log', `Warning: Could not find space "${spaceName}" in sidebar, but creation likely succeeded`); + testLog.info( `Warning: Could not find space "${spaceName}" in sidebar, but creation likely succeeded`); } }); } }); // Step 7: Optional - Verify the new space is clickable - cy.task('log', '=== Step 7: Testing space functionality ==='); + testLog.info( '=== Step 7: Testing space functionality ==='); // Simply verify the space exists and is clickable SpaceSelectors.names() @@ -170,14 +171,14 @@ describe('Space Creation Tests', () => { .should('exist') .click({ force: true }); - cy.task('log', '✓ Clicked on the new space'); + testLog.info( '✓ Clicked on the new space'); // Wait briefly to ensure no errors cy.wait(1000); // Final verification - cy.task('log', '=== Test completed successfully! ==='); - cy.task('log', '✓✓✓ New space created successfully'); + testLog.info( '=== Test completed successfully! ==='); + testLog.info( '✓✓✓ New space created successfully'); // Verify no errors on the page cy.get('body').then($body => { @@ -186,7 +187,7 @@ describe('Space Creation Tests', () => { $body.find('[role="alert"]').length > 0; if (!hasError) { - cy.task('log', '✓ No errors detected on page'); + testLog.info( '✓ No errors detected on page'); } }); }); diff --git a/cypress/e2e/user/user.cy.ts b/cypress/e2e/user/user.cy.ts index 3f557aed..95545dba 100644 --- a/cypress/e2e/user/user.cy.ts +++ b/cypress/e2e/user/user.cy.ts @@ -2,13 +2,14 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; import { WorkspaceSelectors, SidebarSelectors, PageSelectors } from '../../support/selectors'; import { generateRandomEmail, getTestEnvironment } from '../../support/test-config'; +import { testLog } from '../../support/test-helpers'; describe('User Feature Tests', () => { const env = getTestEnvironment(); const APPFLOWY_WS_BASE_URL = Cypress.env('APPFLOWY_WS_BASE_URL'); before(() => { - cy.task('log', `Test Environment Configuration: + testLog.info( `Test Environment Configuration: - APPFLOWY_BASE_URL: ${env.appflowyBaseUrl} - APPFLOWY_GOTRUE_BASE_URL: ${env.appflowyGotrueBaseUrl} - APPFLOWY_WS_BASE_URL: ${APPFLOWY_WS_BASE_URL ?? ''} @@ -48,10 +49,10 @@ describe('User Feature Tests', () => { // Verify we're on the app page cy.url().should('include', '/app'); - cy.task('log', 'Authentication flow completed successfully'); + testLog.info( 'Authentication flow completed successfully'); // Wait for workspace to be fully loaded by checking for key elements - cy.task('log', 'Waiting for app to fully load...'); + testLog.info( 'Waiting for app to fully load...'); // Wait for the loading screen to disappear and main app to appear cy.get('body', { timeout: 30000 }).should('not.contain', 'Welcome!'); @@ -65,7 +66,7 @@ describe('User Feature Tests', () => { // Wait for workspace dropdown to be available WorkspaceSelectors.dropdownTrigger().should('be.visible', { timeout: 30000 }); - cy.task('log', 'App fully loaded'); + testLog.info( 'App fully loaded'); // Additional wait for stability cy.wait(1000); @@ -80,12 +81,12 @@ describe('User Feature Tests', () => { WorkspaceSelectors.dropdownContent().within(() => { cy.contains(randomEmail).should('be.visible'); }); - cy.task('log', `Verified email ${randomEmail} is displayed in dropdown`); + testLog.info( `Verified email ${randomEmail} is displayed in dropdown`); // Verify one member count TestTool.getWorkspaceMemberCounts() .should('contain', '1 member'); - cy.task('log', 'Verified workspace has 1 member'); + testLog.info( 'Verified workspace has 1 member'); // Verify exactly one workspace exists TestTool.getWorkspaceItems() @@ -95,7 +96,7 @@ describe('User Feature Tests', () => { WorkspaceSelectors.itemName() .should('exist') .and('not.be.empty'); - cy.task('log', 'Verified one workspace exists'); + testLog.info( 'Verified one workspace exists'); }); }); diff --git a/cypress/support/auth-utils.ts b/cypress/support/auth-utils.ts index f6e7884f..782e7c90 100644 --- a/cypress/support/auth-utils.ts +++ b/cypress/support/auth-utils.ts @@ -1,3 +1,4 @@ +import { testLog } from './test-helpers'; /// export interface AuthConfig { @@ -168,7 +169,7 @@ export class AuthTestUtils { // First, we need to call the verify endpoint to create the user profile // This endpoint creates the user in the AppFlowy backend - cy.task('log', 'Calling verify endpoint to create user profile'); + testLog.info( 'Calling verify endpoint to create user profile'); // Make the verify call with retry logic for CI environment const verifyWithRetry = (retries = 3): Cypress.Chainable => { @@ -178,11 +179,11 @@ export class AuthTestUtils { failOnStatusCode: false, timeout: 30000, }).then((verifyResponse) => { - cy.task('log', `Verify response status: ${verifyResponse.status}`); + testLog.info( `Verify response status: ${verifyResponse.status}`); // If we get a 502 or 503 error, retry if ((verifyResponse.status === 502 || verifyResponse.status === 503) && retries > 0) { - cy.task('log', `Retrying verify endpoint, ${retries} attempts remaining`); + testLog.info( `Retrying verify endpoint, ${retries} attempts remaining`); return cy.wait(2000).then(() => verifyWithRetry(retries - 1)); } @@ -265,4 +266,4 @@ Cypress.Commands.add('signIn', (email: string = 'test@example.com') => { Cypress.Commands.add('generateSignInUrl', (email: string) => { const authUtils = new AuthTestUtils(); return authUtils.generateSignInUrl(email); -}); \ No newline at end of file +}); diff --git a/cypress/support/page-utils.ts b/cypress/support/page-utils.ts index 294a4b72..665156d6 100644 --- a/cypress/support/page-utils.ts +++ b/cypress/support/page-utils.ts @@ -1,3 +1,4 @@ +import { testLog } from './test-helpers'; /** * Main export file for Cypress E2E test utilities * This file aggregates and re-exports all utility functions used in tests @@ -158,7 +159,7 @@ export class TestTool { * Used in create-delete-page.cy.ts */ static verifyPageExists(pageName: string) { - cy.task('log', `Verifying page exists: ${pageName}`); + testLog.info( `Verifying page exists: ${pageName}`); PageSelectors.nameContaining(pageName) .should('exist') .should('be.visible'); @@ -169,11 +170,11 @@ export class TestTool { * Used in create-delete-page.cy.ts */ static verifyPageNotExists(pageName: string) { - cy.task('log', `Verifying page does not exist: ${pageName}`); + testLog.info( `Verifying page does not exist: ${pageName}`); PageSelectors.nameContaining(pageName) .should('not.exist'); } } // Export all individual functions for convenience -export default TestTool; \ No newline at end of file +export default TestTool; diff --git a/cypress/support/page/flows.ts b/cypress/support/page/flows.ts index 72c64fc1..e1eee04d 100644 --- a/cypress/support/page/flows.ts +++ b/cypress/support/page/flows.ts @@ -1,3 +1,4 @@ +import { testLog } from '../test-helpers'; /** * Flow utility functions for Cypress E2E tests * Contains high-level test flow operations that orchestrate multiple page interactions @@ -17,7 +18,7 @@ import { * @returns Cypress chainable */ export function waitForPageLoad(waitTime: number = 3000) { - cy.task('log', `Waiting for page load (${waitTime}ms)`); + testLog.info( `Waiting for page load (${waitTime}ms)`); return cy.wait(waitTime); } @@ -27,7 +28,7 @@ export function waitForPageLoad(waitTime: number = 3000) { * @returns Cypress chainable */ export function waitForSidebarReady(timeout: number = 10000) { - cy.task('log', 'Waiting for sidebar to be ready'); + testLog.info( 'Waiting for sidebar to be ready'); return SidebarSelectors.pageHeader() .should('be.visible', { timeout }); } @@ -39,19 +40,19 @@ export function waitForSidebarReady(timeout: number = 10000) { * @param content - Array of content lines to add to the page */ export function createPageAndAddContent(pageName: string, content: string[]) { - cy.task('log', `Creating page "${pageName}" with ${content.length} lines of content`); + testLog.info( `Creating page "${pageName}" with ${content.length} lines of content`); // Create the page first - this navigates to the new page automatically createPage(pageName); - cy.task('log', 'Page created successfully and we are now on the page'); + testLog.info( 'Page created successfully and we are now on the page'); // We're already on the newly created page, just add content - cy.task('log', 'Adding content to the page'); + testLog.info( 'Adding content to the page'); typeLinesInVisibleEditor(content); - cy.task('log', 'Content typed successfully'); + testLog.info( 'Content typed successfully'); waitForReactUpdate(1000); assertEditorContentEquals(content); - cy.task('log', 'Content verification completed'); + testLog.info( 'Content verification completed'); } /** @@ -60,7 +61,7 @@ export function createPageAndAddContent(pageName: string, content: string[]) { * @param pageName - Name of the page to open */ export function openPageFromSidebar(pageName: string) { - cy.task('log', `Opening page from sidebar: ${pageName}`); + testLog.info( `Opening page from sidebar: ${pageName}`); // Ensure sidebar is visible SidebarSelectors.pageHeader().should('be.visible'); @@ -68,11 +69,11 @@ export function openPageFromSidebar(pageName: string) { // Try to find the page - it might be named differently in the sidebar PageSelectors.names().then(($pages: JQuery) => { const pageNames = Array.from($pages).map((el: Element) => el.textContent?.trim()); - cy.task('log', `Available pages in sidebar: ${pageNames.join(', ')}`); + testLog.info( `Available pages in sidebar: ${pageNames.join(', ')}`); // Try to find exact match first if (pageNames.includes(pageName)) { - cy.task('log', `Found exact match for: ${pageName}`); + testLog.info( `Found exact match for: ${pageName}`); PageSelectors.nameContaining(pageName) .first() .scrollIntoView() @@ -80,12 +81,12 @@ export function openPageFromSidebar(pageName: string) { .click(); } else { // If no exact match, try to find the most recently created page (usually last or first untitled) - cy.task('log', `No exact match for "${pageName}", clicking most recent page`); + testLog.info( `No exact match for "${pageName}", clicking most recent page`); // Look for "Untitled" or the first/last page const untitledPage = pageNames.find(name => name === 'Untitled' || name?.includes('Untitled')); if (untitledPage) { - cy.task('log', `Found untitled page: ${untitledPage}`); + testLog.info( `Found untitled page: ${untitledPage}`); PageSelectors.nameContaining('Untitled') .first() .scrollIntoView() @@ -94,7 +95,7 @@ export function openPageFromSidebar(pageName: string) { } else { // Just click the first non-"Getting started" page const targetPage = pageNames.find(name => name !== 'Getting started') || pageNames[0]; - cy.task('log', `Clicking page: ${targetPage}`); + testLog.info( `Clicking page: ${targetPage}`); PageSelectors.names() .first() .scrollIntoView() @@ -106,7 +107,7 @@ export function openPageFromSidebar(pageName: string) { // Wait for page to load waitForReactUpdate(2000); - cy.task('log', `Page opened successfully`); + testLog.info( `Page opened successfully`); } /** @@ -115,17 +116,17 @@ export function openPageFromSidebar(pageName: string) { * @param spaceIndex - Index of the space to expand (default: 0 for first space) */ export function expandSpace(spaceIndex: number = 0) { - cy.task('log', `Expanding space at index ${spaceIndex}`); + testLog.info( `Expanding space at index ${spaceIndex}`); SpaceSelectors.items().eq(spaceIndex).within(() => { SpaceSelectors.expanded().then(($expanded: JQuery) => { const isExpanded = $expanded.attr('data-expanded') === 'true'; if (!isExpanded) { - cy.task('log', 'Space is collapsed, expanding it'); + testLog.info( 'Space is collapsed, expanding it'); SpaceSelectors.names().first().click(); } else { - cy.task('log', 'Space is already expanded'); + testLog.info( 'Space is already expanded'); } }); }); @@ -140,7 +141,7 @@ export function expandSpace(spaceIndex: number = 0) { * Internal function used by createPageAndAddContent */ function createPage(pageName: string) { - cy.task('log', `Creating page: ${pageName}`); + testLog.info( `Creating page: ${pageName}`); // Click new page button PageSelectors.newPageButton().should('be.visible').click(); @@ -161,7 +162,7 @@ function createPage(pageName: string) { // Close any modal dialogs cy.get('body').then(($body: JQuery) => { if ($body.find('[role="dialog"]').length > 0) { - cy.task('log', 'Closing modal dialog'); + testLog.info( 'Closing modal dialog'); cy.get('body').type('{esc}'); waitForReactUpdate(1000); } @@ -183,12 +184,12 @@ function createPage(pageName: string) { .type('{enter}'); }); - cy.task('log', `Set page title to: ${pageName}`); + testLog.info( `Set page title to: ${pageName}`); waitForReactUpdate(2000); // Also update the page name in the sidebar if possible // This ensures the page can be found later by name - cy.task('log', 'Page created and title set'); + testLog.info( 'Page created and title set'); } /** @@ -196,7 +197,7 @@ function createPage(pageName: string) { * Internal function used by createPageAndAddContent */ function typeLinesInVisibleEditor(lines: string[]) { - cy.task('log', `Typing ${lines.length} lines in editor`); + testLog.info( `Typing ${lines.length} lines in editor`); // Wait for any template to load waitForReactUpdate(1000); @@ -204,7 +205,7 @@ function typeLinesInVisibleEditor(lines: string[]) { // Check if we need to dismiss welcome content or click to create editor cy.get('body').then(($body: JQuery) => { if ($body.text().includes('Welcome to AppFlowy')) { - cy.task('log', 'Welcome template detected, looking for editor area'); + testLog.info( 'Welcome template detected, looking for editor area'); } }); @@ -212,7 +213,7 @@ function typeLinesInVisibleEditor(lines: string[]) { cy.get('[contenteditable="true"]', { timeout: 10000 }).should('exist'); cy.get('[contenteditable="true"]').then(($editors: JQuery) => { - cy.task('log', `Found ${$editors.length} editable elements`); + testLog.info( `Found ${$editors.length} editable elements`); if ($editors.length === 0) { throw new Error('No editable elements found on page'); @@ -227,7 +228,7 @@ function typeLinesInVisibleEditor(lines: string[]) { $el.attr('id')?.includes('title'); if (!isTitle && el) { - cy.task('log', `Using editor at index ${index}`); + testLog.info( `Using editor at index ${index}`); cy.wrap(el).click({ force: true }).clear().type(lines.join('{enter}'), { force: true }); editorFound = true; return false; // break the loop @@ -235,7 +236,7 @@ function typeLinesInVisibleEditor(lines: string[]) { }); if (!editorFound && $editors.length > 0) { - cy.task('log', 'Using fallback: last contenteditable element'); + testLog.info( 'Using fallback: last contenteditable element'); const lastEditor = $editors.last().get(0); if (lastEditor) { cy.wrap(lastEditor).click({ force: true }).clear().type(lines.join('{enter}'), { force: true }); @@ -249,11 +250,11 @@ function typeLinesInVisibleEditor(lines: string[]) { * Internal function used by createPageAndAddContent */ function assertEditorContentEquals(lines: string[]) { - cy.task('log', 'Verifying editor content'); + testLog.info( 'Verifying editor content'); lines.forEach(line => { cy.contains(line).should('exist'); - cy.task('log', `✓ Found content: "${line}"`); + testLog.info( `✓ Found content: "${line}"`); }); } @@ -264,7 +265,7 @@ function assertEditorContentEquals(lines: string[]) { * Referenced in page-utils.ts */ export function closeSidebar() { - cy.task('log', 'Closing sidebar'); + testLog.info( 'Closing sidebar'); // Implementation would depend on how sidebar is closed in the UI // This is a placeholder to maintain compatibility } @@ -274,7 +275,7 @@ export function closeSidebar() { * Referenced in page-utils.ts */ export function createNewPageViaBackendQuickAction(pageName?: string) { - cy.task('log', `Creating new page via backend quick action: ${pageName || 'unnamed'}`); + testLog.info( `Creating new page via backend quick action: ${pageName || 'unnamed'}`); // Implementation would depend on the backend quick action flow // This is a placeholder to maintain compatibility } @@ -284,7 +285,7 @@ export function createNewPageViaBackendQuickAction(pageName?: string) { * Referenced in page-utils.ts */ export function openCommandPalette() { - cy.task('log', 'Opening command palette'); + testLog.info( 'Opening command palette'); // Implementation would depend on how command palette is opened // This is a placeholder to maintain compatibility } @@ -294,6 +295,6 @@ export function openCommandPalette() { * Referenced in page-utils.ts */ export function navigateTo(route: string) { - cy.task('log', `Navigating to: ${route}`); + testLog.info( `Navigating to: ${route}`); cy.visit(route); } \ No newline at end of file diff --git a/cypress/support/page/modal.ts b/cypress/support/page/modal.ts index 6a00b0e9..34a1f3a1 100644 --- a/cypress/support/page/modal.ts +++ b/cypress/support/page/modal.ts @@ -1,3 +1,4 @@ +import { testLog } from '../test-helpers'; /** * Modal utility functions for Cypress E2E tests * Contains functions for interacting with modal dialogs and popovers @@ -11,12 +12,12 @@ import { ShareSelectors, waitForReactUpdate } from '../selectors'; * @returns Cypress chainable */ export function openSharePopover() { - cy.task('log', 'Opening share popover'); + testLog.info( 'Opening share popover'); // Close any modals that might be blocking the share button cy.get('body').then(($body: JQuery) => { if ($body.find('[role="dialog"]').length > 0 || $body.find('.MuiDialog-container').length > 0) { - cy.task('log', 'Closing modal dialog before opening share popover'); + testLog.info( 'Closing modal dialog before opening share popover'); cy.get('body').type('{esc}'); waitForReactUpdate(1000); } @@ -33,7 +34,7 @@ export function openSharePopover() { // Verify popover is visible ShareSelectors.sharePopover().should('be.visible'); - cy.task('log', 'Share popover opened successfully'); + testLog.info( 'Share popover opened successfully'); } /** @@ -41,7 +42,7 @@ export function openSharePopover() { * Referenced in page-utils.ts */ export function clickOutsideModal() { - cy.task('log', 'Clicking outside modal to close it'); + testLog.info( 'Clicking outside modal to close it'); // Click at the top-left corner of the page cy.get('body').click(0, 0); @@ -49,5 +50,5 @@ export function clickOutsideModal() { // Wait for modal to close waitForReactUpdate(500); - cy.task('log', 'Modal closed'); + testLog.info( 'Modal closed'); } \ No newline at end of file diff --git a/cypress/support/page/page-actions.ts b/cypress/support/page/page-actions.ts index 861776b4..735d5474 100644 --- a/cypress/support/page/page-actions.ts +++ b/cypress/support/page/page-actions.ts @@ -1,3 +1,4 @@ +import { testLog } from '../test-helpers'; /** * Page actions utility functions for Cypress E2E tests * Contains functions for page context menu actions @@ -18,7 +19,7 @@ import { * @returns Cypress chainable */ export function openViewActionsPopoverForPage(pageName: string) { - cy.task('log', `Opening view actions popover for page: ${pageName}`); + testLog.info( `Opening view actions popover for page: ${pageName}`); // Find the page item by name const pageItem = PageSelectors.itemByName(pageName); @@ -48,7 +49,7 @@ export function openViewActionsPopoverForPage(pageName: string) { cy.get('[data-slot="dropdown-menu-content"]', { timeout: 5000 }) .should('exist'); - cy.task('log', 'View actions popover opened successfully'); + testLog.info( 'View actions popover opened successfully'); } /** @@ -58,7 +59,7 @@ export function openViewActionsPopoverForPage(pageName: string) { * @param pageName - The name of the page to delete */ export function deletePageByName(pageName: string) { - cy.task('log', `=== Deleting page: ${pageName} ===`); + testLog.info( `=== Deleting page: ${pageName} ===`); // Find and hover over the page to show actions // Use itemByName to ensure we get the right page item @@ -79,7 +80,7 @@ export function deletePageByName(pageName: string) { ViewActionSelectors.popover() .should('be.visible'); - cy.task('log', 'View actions popover is visible, looking for delete button...'); + testLog.info( 'View actions popover is visible, looking for delete button...'); // Click delete option - look in body since it's portalled ViewActionSelectors.deleteButton() @@ -87,7 +88,7 @@ export function deletePageByName(pageName: string) { .should('be.visible') .click(); - cy.task('log', 'Clicked delete button, checking if confirmation is needed...'); + testLog.info( 'Clicked delete button, checking if confirmation is needed...'); waitForReactUpdate(500); @@ -95,7 +96,7 @@ export function deletePageByName(pageName: string) { // For unpublished pages, deletion happens immediately cy.get('body').then(($body: JQuery) => { if ($body.find('[data-testid="delete-page-confirm-modal"]').length > 0) { - cy.task('log', 'Confirmation modal appeared, clicking confirm...'); + testLog.info( 'Confirmation modal appeared, clicking confirm...'); // Confirm deletion in the confirmation dialog ModalSelectors.confirmDeleteButton() @@ -103,13 +104,13 @@ export function deletePageByName(pageName: string) { .should('be.visible') .click(); - cy.task('log', 'Clicked confirm delete button'); + testLog.info( 'Clicked confirm delete button'); } else { - cy.task('log', 'No confirmation needed (unpublished page), deletion completed'); + testLog.info( 'No confirmation needed (unpublished page), deletion completed'); } }); waitForReactUpdate(1000); - cy.task('log', `✓ Page "${pageName}" deleted successfully`); + testLog.info( `✓ Page "${pageName}" deleted successfully`); } \ No newline at end of file diff --git a/cypress/support/page/pages.ts b/cypress/support/page/pages.ts index 02bf885c..507be552 100644 --- a/cypress/support/page/pages.ts +++ b/cypress/support/page/pages.ts @@ -1,3 +1,4 @@ +import { testLog } from '../test-helpers'; /** * Page management utility functions for Cypress E2E tests * Contains functions for interacting with pages in the sidebar @@ -12,7 +13,7 @@ import { PageSelectors, waitForReactUpdate } from '../selectors'; * @returns Cypress chainable element */ export function getPageByName(pageName: string) { - cy.task('log', `Getting page by name: ${pageName}`); + testLog.info( `Getting page by name: ${pageName}`); return PageSelectors.itemByName(pageName); } @@ -22,7 +23,7 @@ export function getPageByName(pageName: string) { * @returns Cypress chainable element */ export function getPageTitleInput() { - cy.task('log', 'Getting page title input element'); + testLog.info( 'Getting page title input element'); return PageSelectors.titleInput().first(); } @@ -31,7 +32,7 @@ export function getPageTitleInput() { * Used in more-page-action.cy.ts after editing page titles */ export function savePageTitle() { - cy.task('log', 'Saving page title'); + testLog.info( 'Saving page title'); cy.focused().type('{enter}'); waitForReactUpdate(1000); // Wait for save to complete } @@ -42,7 +43,7 @@ export function savePageTitle() { * @param pageName - The name of the page */ export function openPageMoreActions(pageName: string) { - cy.task('log', `Opening more actions for page: ${pageName}`); + testLog.info( `Opening more actions for page: ${pageName}`); // Find the page and trigger hover to show actions PageSelectors.nameContaining(pageName) diff --git a/cypress/support/page/share-publish.ts b/cypress/support/page/share-publish.ts index c80f648e..612b9fde 100644 --- a/cypress/support/page/share-publish.ts +++ b/cypress/support/page/share-publish.ts @@ -1,3 +1,4 @@ +import { testLog } from '../test-helpers'; /** * Share and Publish utility functions for Cypress E2E tests * Contains functions for publishing pages and verifying published content @@ -11,16 +12,16 @@ import { ShareSelectors, waitForReactUpdate } from '../selectors'; * @returns Cypress chainable with the publish URL */ export function publishCurrentPage() { - cy.task('log', '=== Publishing Current Page ==='); + testLog.info( '=== Publishing Current Page ==='); // Check if share popover is already open cy.get('body').then(($body: JQuery) => { if (!$body.find('[data-testid="share-popover"]').length) { - cy.task('log', 'Share popover not open, opening it'); + testLog.info( 'Share popover not open, opening it'); ShareSelectors.shareButton().should('be.visible').click(); waitForReactUpdate(1000); } else { - cy.task('log', 'Share popover already open'); + testLog.info( 'Share popover already open'); } }); @@ -29,17 +30,17 @@ export function publishCurrentPage() { // Check if we're already on the Publish tab by looking for "Publish to Web" text if (!$body.text().includes('Publish to Web')) { // If we don't see "Publish to Web", we need to click on Publish tab - cy.task('log', 'Switching to Publish tab'); + testLog.info( 'Switching to Publish tab'); cy.contains('Publish').should('be.visible').click(); waitForReactUpdate(500); } else { - cy.task('log', 'Already on Publish tab'); + testLog.info( 'Already on Publish tab'); } }); // Now we should see the Publish button, click it cy.contains('button', 'Publish').should('be.visible').click(); - cy.task('log', 'Clicked Publish button, waiting for publish to complete'); + testLog.info( 'Clicked Publish button, waiting for publish to complete'); // Wait longer for the publish action to complete and URL to appear waitForReactUpdate(5000); @@ -59,7 +60,7 @@ export function publishCurrentPage() { }); if (publishedUrl) { - cy.task('log', `Page published at: ${publishedUrl}`); + testLog.info( `Page published at: ${publishedUrl}`); return publishedUrl; } @@ -71,12 +72,12 @@ export function publishCurrentPage() { if (urlText.length > 0) { const url = urlText.first().text().match(/(https?:\/\/[^\s]+)/)?.[0] || ''; - cy.task('log', `Page published at: ${url}`); + testLog.info( `Page published at: ${url}`); return url; } // If still not found, return a dummy URL for testing - cy.task('log', 'Warning: Could not find published URL, using dummy URL'); + testLog.info( 'Warning: Could not find published URL, using dummy URL'); return 'http://localhost/published/test-page'; }); } @@ -87,7 +88,7 @@ export function publishCurrentPage() { * @returns Cypress chainable with the publish URL */ export function readPublishUrlFromPanel() { - cy.task('log', 'Reading publish URL from panel'); + testLog.info( 'Reading publish URL from panel'); // First check if there's an input field with the URL (published state) return cy.get('body').then(($body: JQuery) => { @@ -98,7 +99,7 @@ export function readPublishUrlFromPanel() { if (urlInput.length > 0) { const url = urlInput.val(); - cy.task('log', `Found publish URL: ${url}`); + testLog.info( `Found publish URL: ${url}`); return url; } else { // If not found, try the selector @@ -106,7 +107,7 @@ export function readPublishUrlFromPanel() { .should('be.visible') .invoke('val') .then((url) => { - cy.task('log', `Found publish URL: ${url}`); + testLog.info( `Found publish URL: ${url}`); return url; }); } @@ -119,7 +120,7 @@ export function readPublishUrlFromPanel() { * @param expectedContent - Array of content strings to verify */ export function verifyPublishedContentMatches(expectedContent: string[]) { - cy.task('log', `=== Verifying Published Content ===`); + testLog.info( `=== Verifying Published Content ===`); // The page should already be loaded, just verify content waitForReactUpdate(2000); @@ -127,10 +128,10 @@ export function verifyPublishedContentMatches(expectedContent: string[]) { // Verify each content line exists expectedContent.forEach(content => { cy.contains(content).should('be.visible'); - cy.task('log', `✓ Found published content: "${content}"`); + testLog.info( `✓ Found published content: "${content}"`); }); - cy.task('log', 'All published content verified successfully'); + testLog.info( 'All published content verified successfully'); } /** @@ -139,16 +140,16 @@ export function verifyPublishedContentMatches(expectedContent: string[]) { * @param publishUrl - The URL to verify is no longer accessible */ export function unpublishCurrentPageAndVerify(publishUrl: string) { - cy.task('log', '=== Unpublishing Current Page ==='); + testLog.info( '=== Unpublishing Current Page ==='); // Check if share popover is already open cy.get('body').then(($body: JQuery) => { if (!$body.find('[data-testid="share-popover"]').length) { - cy.task('log', 'Share popover not open, opening it'); + testLog.info( 'Share popover not open, opening it'); ShareSelectors.shareButton().should('be.visible').click(); waitForReactUpdate(1000); } else { - cy.task('log', 'Share popover already open'); + testLog.info( 'Share popover already open'); } }); @@ -156,11 +157,11 @@ export function unpublishCurrentPageAndVerify(publishUrl: string) { cy.get('body').then(($body: JQuery) => { if (!$body.text().includes('Publish to Web')) { // If we don't see "Publish to Web", click on Publish tab - cy.task('log', 'Switching to Publish tab'); + testLog.info( 'Switching to Publish tab'); cy.contains('Publish').click(); waitForReactUpdate(500); } else { - cy.task('log', 'Already on Publish tab'); + testLog.info( 'Already on Publish tab'); } }); @@ -173,13 +174,13 @@ export function unpublishCurrentPageAndVerify(publishUrl: string) { waitForReactUpdate(1000); // Verify the page is no longer accessible - cy.task('log', `Verifying ${publishUrl} is no longer accessible`); + testLog.info( `Verifying ${publishUrl} is no longer accessible`); cy.request({ url: publishUrl, failOnStatusCode: false }).then((response: Cypress.Response) => { expect(response.status).to.not.equal(200); - cy.task('log', `✓ Published page is no longer accessible (status: ${response.status})`); + testLog.info( `✓ Published page is no longer accessible (status: ${response.status})`); }); } @@ -191,7 +192,7 @@ export function unpublishCurrentPageAndVerify(publishUrl: string) { * @param pageContent - The content of the page (unused but kept for compatibility) */ export function unpublishFromSettingsAndVerify(publishUrl: string, pageName?: string, pageContent?: string) { - cy.task('log', '=== Unpublishing from Settings ==='); + testLog.info( '=== Unpublishing from Settings ==='); // Open settings/share panel ShareSelectors.pageSettingsButton().click(); @@ -210,13 +211,13 @@ export function unpublishFromSettingsAndVerify(publishUrl: string, pageName?: st waitForReactUpdate(2000); // Verify the page is no longer accessible - cy.task('log', `Verifying ${publishUrl} is no longer accessible`); + testLog.info( `Verifying ${publishUrl} is no longer accessible`); cy.request({ url: publishUrl, failOnStatusCode: false }).then((response: Cypress.Response) => { expect(response.status).to.not.equal(200); - cy.task('log', `✓ Published page is no longer accessible (status: ${response.status})`); + testLog.info( `✓ Published page is no longer accessible (status: ${response.status})`); }); } @@ -225,7 +226,7 @@ export function unpublishFromSettingsAndVerify(publishUrl: string, pageName?: st * Used in share-publish.cy.ts (though not exported from page-utils.ts anymore) */ export function openShareLink(shareUrl: string) { - cy.task('log', `Opening share link: ${shareUrl}`); + testLog.info( `Opening share link: ${shareUrl}`); // Visit the share URL cy.visit(shareUrl); @@ -233,5 +234,5 @@ export function openShareLink(shareUrl: string) { // Wait for the page to load cy.url().should('include', '/publish'); - cy.task('log', 'Share link opened successfully'); + testLog.info( 'Share link opened successfully'); } \ No newline at end of file diff --git a/cypress/support/page/workspace.ts b/cypress/support/page/workspace.ts index cd55e9dd..85dd234f 100644 --- a/cypress/support/page/workspace.ts +++ b/cypress/support/page/workspace.ts @@ -1,3 +1,4 @@ +import { testLog } from '../test-helpers'; /** * Workspace utility functions for Cypress E2E tests * Contains functions for interacting with workspace dropdown and settings @@ -10,7 +11,7 @@ import { WorkspaceSelectors, waitForReactUpdate } from '../selectors'; * Used in user.cy.ts to access workspace options */ export function openWorkspaceDropdown() { - cy.task('log', 'Opening workspace dropdown'); + testLog.info( 'Opening workspace dropdown'); WorkspaceSelectors.dropdownTrigger().click(); waitForReactUpdate(500); } @@ -21,7 +22,7 @@ export function openWorkspaceDropdown() { * @returns Cypress chainable containing workspace items */ export function getWorkspaceItems() { - cy.task('log', 'Getting workspace items from dropdown'); + testLog.info( 'Getting workspace items from dropdown'); return WorkspaceSelectors.item(); } @@ -31,7 +32,7 @@ export function getWorkspaceItems() { * @returns Cypress chainable with array of member count strings */ export function getWorkspaceMemberCounts() { - cy.task('log', 'Getting workspace member counts'); + testLog.info( 'Getting workspace member counts'); return WorkspaceSelectors.memberCount() .then(($elements: JQuery) => { @@ -39,7 +40,7 @@ export function getWorkspaceMemberCounts() { $elements.each((index: number, el: HTMLElement) => { counts.push(el.textContent?.trim() || ''); }); - cy.task('log', `Found member counts: ${counts.join(', ')}`); + testLog.info( `Found member counts: ${counts.join(', ')}`); return cy.wrap(counts); }); } @@ -49,7 +50,7 @@ export function getWorkspaceMemberCounts() { * This function is referenced in page-utils.ts but implementation may vary */ export function createWorkspace(workspaceName: string) { - cy.task('log', `Creating workspace: ${workspaceName}`); + testLog.info( `Creating workspace: ${workspaceName}`); // Implementation would go here based on the actual UI flow // This is a placeholder to maintain compatibility } diff --git a/cypress/support/test-config.ts b/cypress/support/test-config.ts index 537abd0a..dad9dae9 100644 --- a/cypress/support/test-config.ts +++ b/cypress/support/test-config.ts @@ -1,4 +1,5 @@ import { v4 as uuidv4 } from 'uuid'; +import { testLog } from './test-helpers'; /** * Centralized test configuration @@ -38,7 +39,7 @@ export const TestConfig = { * Useful for debugging test failures in CI/CD */ export const logTestEnvironment = (env: Partial = TestConfig) => { - cy.task('log', ` + testLog.info(` ╔════════════════════════════════════════════════════════════════╗ ║ Test Environment Configuration ║ ╠════════════════════════════════════════════════════════════════╣