chore: add login logout test

This commit is contained in:
nathan
2025-10-02 15:40:14 +08:00
parent 2985406a2d
commit c6a92d96e7
3 changed files with 262 additions and 1 deletions

View File

@@ -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');
});
});
});
});

View File

@@ -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);
}

View File

@@ -231,7 +231,7 @@ export function Workspaces() {
<div className={'flex-1 text-left'}>{t('web.accountSettings')}</div>
</DropdownMenuItem>
</AccountSettings>
<DropdownMenuItem onSelect={() => setOpenLogoutConfirm(true)}>
<DropdownMenuItem data-testid="logout-menu-item" onSelect={() => setOpenLogoutConfirm(true)}>
<LogoutIcon />
{t('button.logout')}
</DropdownMenuItem>