diff --git a/cypress/e2e/auth/login-logout.cy.ts b/cypress/e2e/auth/login-logout.cy.ts new file mode 100644 index 00000000..5629e072 --- /dev/null +++ b/cypress/e2e/auth/login-logout.cy.ts @@ -0,0 +1,241 @@ +import { v4 as uuidv4 } from 'uuid'; +import { AuthTestUtils } from '../../support/auth-utils'; +import { TestTool } from '../../support/page-utils'; +import { + WorkspaceSelectors, + AuthSelectors, + waitForReactUpdate +} from '../../support/selectors'; + +describe('Login and Logout Flow', () => { + const baseUrl = Cypress.config('baseUrl') || 'http://localhost:3000'; + const gotrueUrl = Cypress.env('APPFLOWY_GOTRUE_BASE_URL') || 'http://localhost/gotrue'; + const apiUrl = Cypress.env('APPFLOWY_BASE_URL') || 'http://localhost'; + + beforeEach(() => { + // Handle uncaught exceptions + cy.on('uncaught:exception', (err) => { + if (err.message.includes('Minified React error') || + err.message.includes('View not found') || + err.message.includes('No workspace or service found')) { + return false; + } + return true; + }); + cy.viewport(1280, 720); + }); + + describe('Test Case 1: Complete Login and Logout Flow', () => { + it('should login and successfully logout with detailed verification', () => { + const testEmail = `test-${uuidv4()}@appflowy.io`; + + cy.log(`[TEST START] Complete Login and Logout Flow - Email: ${testEmail}`); + + // Step 1: Navigate to login page + cy.log('[STEP 1] Visiting login page'); + cy.visit('/login', { failOnStatusCode: false }); + cy.wait(2000); + + // Step 2: Verify login page elements before authentication + cy.log('[STEP 2] Verifying login page loaded with all elements'); + cy.contains('Welcome to AppFlowy').should('be.visible'); + AuthSelectors.emailInput().should('be.visible'); + AuthSelectors.passwordSignInButton().should('be.visible'); + + // Step 3: Use AuthTestUtils for authentication (more reliable than manual password entry) + cy.log('[STEP 3] Starting authentication process'); + const authUtils = new AuthTestUtils(); + authUtils.signInWithTestUrl(testEmail).then(() => { + cy.log('[STEP 4] Authentication successful'); + + // Step 4: Verify successful navigation to app page + cy.url({ timeout: 30000 }).should('include', '/app'); + cy.wait(3000); // Allow app to fully load + + // Step 5: Verify workspace is loaded by checking dropdown trigger + cy.log('[STEP 5] Verifying workspace loaded'); + WorkspaceSelectors.dropdownTrigger({ timeout: 15000 }) + .should('be.visible'); + + // Step 6: Open workspace dropdown + cy.log('[STEP 6] Opening workspace dropdown'); + TestTool.openWorkspaceDropdown(); + + // Step 7: Verify dropdown content and user email + cy.log('[STEP 7] Verifying dropdown content and user email'); + WorkspaceSelectors.dropdownContent() + .should('be.visible'); + cy.contains(testEmail).should('be.visible'); + + // Step 8: Click logout menu item + cy.log('[STEP 8] Clicking logout menu item'); + AuthSelectors.logoutMenuItem() + .should('be.visible') + .click(); + + waitForReactUpdate(1000); + + // Step 9: Verify logout confirmation dialog appears + cy.log('[STEP 9] Verifying logout confirmation dialog'); + AuthSelectors.logoutConfirmButton() + .should('be.visible'); + + // Step 10: Confirm logout + cy.log('[STEP 10] Confirming logout'); + AuthSelectors.logoutConfirmButton().click(); + + waitForReactUpdate(2000); + + // Step 11: Verify redirect to login page + cy.log('[STEP 11] Verifying redirect to login page'); + cy.url({ timeout: 10000 }).should('include', '/login'); + + // Step 12: Verify login page elements are visible after logout + cy.log('[STEP 12] Verifying login page elements after logout'); + cy.contains('Welcome to AppFlowy').should('be.visible'); + AuthSelectors.emailInput().should('be.visible'); + AuthSelectors.passwordSignInButton().should('be.visible'); + + cy.log('[STEP 13] Test completed successfully - Full login and logout flow verified'); + }); + }); + }); + + describe('Test Case 2: Quick Login and Logout using Test URL', () => { + it('should login with test URL and successfully logout', () => { + const testEmail = `test-${uuidv4()}@appflowy.io`; + + cy.log(`[TEST START] Quick Login and Logout using Test URL - Email: ${testEmail}`); + + // Step 1: Visit login page + cy.log('[STEP 1] Visiting login page'); + cy.visit('/login', { failOnStatusCode: false }); + cy.wait(2000); + + // Step 2: Use AuthTestUtils for quick authentication + cy.log('[STEP 2] Starting quick authentication with test URL'); + const authUtils = new AuthTestUtils(); + authUtils.signInWithTestUrl(testEmail).then(() => { + cy.log('[STEP 3] Authentication successful'); + + // Verify navigation to app page + cy.url({ timeout: 30000 }).should('include', '/app'); + cy.wait(3000); // Allow app to fully load + + // Step 4: Verify user is logged in + cy.log('[STEP 4] Verifying user is logged in'); + WorkspaceSelectors.dropdownTrigger({ timeout: 15000 }) + .should('be.visible'); + + // Step 5: Open workspace dropdown + cy.log('[STEP 5] Opening workspace dropdown'); + TestTool.openWorkspaceDropdown(); + + // Verify dropdown is open and user email is visible + WorkspaceSelectors.dropdownContent() + .should('be.visible'); + cy.contains(testEmail).should('be.visible'); + + // Step 6: Click logout menu item + cy.log('[STEP 6] Clicking logout menu item'); + AuthSelectors.logoutMenuItem() + .should('be.visible') + .click(); + + waitForReactUpdate(1000); + + // Step 7: Confirm logout + cy.log('[STEP 7] Confirming logout'); + AuthSelectors.logoutConfirmButton() + .should('be.visible') + .click(); + + waitForReactUpdate(2000); + + // Step 8: Verify redirect to login page + cy.log('[STEP 8] Verifying redirect to login page'); + cy.url({ timeout: 10000 }).should('include', '/login'); + + // Verify login page elements are visible + cy.contains('Welcome to AppFlowy').should('be.visible'); + AuthSelectors.emailInput().should('be.visible'); + + cy.log('[STEP 9] Test completed successfully - Quick login and logout verified'); + }); + }); + }); + + describe('Test Case 3: Cancel Logout Confirmation', () => { + it('should cancel logout when clicking cancel button', () => { + const testEmail = `test-${uuidv4()}@appflowy.io`; + + cy.log(`[TEST START] Cancel Logout Confirmation - Email: ${testEmail}`); + + // Step 1: Visit login page + cy.log('[STEP 1] Visiting login page'); + cy.visit('/login', { failOnStatusCode: false }); + cy.wait(2000); + + // Step 2: Use AuthTestUtils for quick authentication + cy.log('[STEP 2] Starting authentication'); + const authUtils = new AuthTestUtils(); + authUtils.signInWithTestUrl(testEmail).then(() => { + cy.log('[STEP 3] Authentication successful'); + + // Verify navigation to app page + cy.url({ timeout: 30000 }).should('include', '/app'); + cy.wait(3000); // Allow app to fully load + + // Step 4: Open workspace dropdown + cy.log('[STEP 4] Opening workspace dropdown'); + WorkspaceSelectors.dropdownTrigger({ timeout: 15000 }) + .should('be.visible'); + TestTool.openWorkspaceDropdown(); + + // Verify dropdown is open + WorkspaceSelectors.dropdownContent() + .should('be.visible'); + + // Step 5: Click logout menu item + cy.log('[STEP 5] Clicking logout menu item'); + AuthSelectors.logoutMenuItem() + .should('be.visible') + .click(); + + waitForReactUpdate(1000); + + // Step 6: Click Cancel button in logout confirmation dialog + cy.log('[STEP 6] Clicking Cancel button in logout confirmation'); + + // Find and click the Cancel button using text content + cy.contains('button', 'Cancel') + .should('be.visible') + .click(); + + waitForReactUpdate(1000); + + // Step 7: Verify user remains logged in + cy.log('[STEP 7] Verifying user remains logged in'); + + // Should still be on /app page + cy.url().should('include', '/app'); + + // Workspace dropdown should still be accessible + WorkspaceSelectors.dropdownTrigger() + .should('be.visible'); + + // Open dropdown again to verify user is still logged in + TestTool.openWorkspaceDropdown(); + WorkspaceSelectors.dropdownContent() + .should('be.visible'); + cy.contains(testEmail).should('be.visible'); + + // Close dropdown + cy.get('body').click(0, 0); // Click outside to close dropdown + waitForReactUpdate(500); + + cy.log('[STEP 8] Test completed successfully - Logout cancellation verified'); + }); + }); + }); +}); \ No newline at end of file diff --git a/cypress/support/selectors.ts b/cypress/support/selectors.ts index 60f8ff40..67290b02 100644 --- a/cypress/support/selectors.ts +++ b/cypress/support/selectors.ts @@ -404,6 +404,26 @@ export const RowControlsSelectors = { deleteRowConfirmButton: () => cy.get(byTestId('delete-row-confirm-button')), }; +/** + * Authentication-related selectors + * Used for login/logout flow testing + */ +export const AuthSelectors = { + // Login page elements + emailInput: () => cy.get(byTestId('login-email-input')), + + // Password sign-in button + passwordSignInButton: () => cy.get(byTestId('login-password-button')), + + // Password page elements + passwordInput: () => cy.get(byTestId('password-input')), + passwordSubmitButton: () => cy.get(byTestId('password-submit-button')), + + // Logout elements + logoutMenuItem: () => cy.get(byTestId('logout-menu-item')), + logoutConfirmButton: () => cy.get(byTestId('logout-confirm-button')), +}; + export function waitForReactUpdate(ms: number = 500) { return cy.wait(ms); } \ No newline at end of file diff --git a/src/components/app/workspaces/Workspaces.tsx b/src/components/app/workspaces/Workspaces.tsx index 6b03b52c..f400f1b1 100644 --- a/src/components/app/workspaces/Workspaces.tsx +++ b/src/components/app/workspaces/Workspaces.tsx @@ -231,7 +231,7 @@ export function Workspaces() {