Files
AppFlowy-Web/cypress/e2e/page/delete-page-verify-trash.cy.ts
2025-09-06 14:57:08 +08:00

315 lines
15 KiB
TypeScript

import { v4 as uuidv4 } from 'uuid';
import { AuthTestUtils } from '../../support/auth-utils';
import { TestTool } from '../../support/page-utils';
import { PageSelectors, ModalSelectors, SidebarSelectors, waitForReactUpdate } from '../../support/selectors';
describe('Delete Page, Verify in Trash, and Restore Tests', () => {
const APPFLOWY_BASE_URL = Cypress.env('APPFLOWY_BASE_URL');
const APPFLOWY_GOTRUE_BASE_URL = Cypress.env('APPFLOWY_GOTRUE_BASE_URL');
const generateRandomEmail = () => `${uuidv4()}@appflowy.io`;
let testEmail: string;
let testPageName: string;
before(() => {
// Log environment configuration for debugging
cy.task('log', `Test Environment Configuration:
- APPFLOWY_BASE_URL: ${APPFLOWY_BASE_URL}
- APPFLOWY_GOTRUE_BASE_URL: ${APPFLOWY_GOTRUE_BASE_URL}`);
});
beforeEach(() => {
// Generate unique test data for each test
testEmail = generateRandomEmail();
testPageName = `test-page-${Date.now()}`;
});
describe('Delete Page, Verify in Trash, and Restore', () => {
it('should create a page, delete it, verify in trash, restore it, and verify it is back in sidebar', () => {
// Handle uncaught exceptions during workspace creation
cy.on('uncaught:exception', (err: Error) => {
if (err.message.includes('No workspace or service found')) {
return false;
}
return true;
});
// Step 1: Login
cy.task('log', '=== Step 1: Login ===');
cy.visit('/login', { failOnStatusCode: false });
cy.wait(2000);
const authUtils = new AuthTestUtils();
authUtils.signInWithTestUrl(testEmail).then(() => {
cy.url().should('include', '/app');
// Wait for the app to fully load
cy.task('log', '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!');
// Wait for the sidebar to be visible (indicates app is loaded)
SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 });
// Wait for at least one page to exist in the sidebar
PageSelectors.names().should('exist', { timeout: 30000 });
// Additional wait for stability
cy.wait(2000);
// Now wait for the new page button to be available
cy.task('log', 'Looking for new page button...');
PageSelectors.newPageButton()
.should('exist', { timeout: 20000 })
.then(() => {
cy.task('log', 'New page button found!');
});
// Step 2: Create a new page
cy.task('log', `=== Step 2: Creating page with title: ${testPageName} ===`);
// Click new page button
PageSelectors.newPageButton().click();
waitForReactUpdate(1000);
// Handle the new page modal
ModalSelectors.newPageModal().should('be.visible').within(() => {
// Select the first available space
ModalSelectors.spaceItemInModal().first().click();
waitForReactUpdate(500);
// Click Add button
cy.contains('button', 'Add').click();
});
// Wait for navigation to the new page
cy.wait(3000);
// Close any share/modal dialogs that might be open
cy.get('body').then(($body: JQuery<HTMLBodyElement>) => {
if ($body.find('[role="dialog"]').length > 0 || $body.find('.MuiDialog-container').length > 0) {
cy.task('log', 'Closing modal dialog');
cy.get('body').type('{esc}');
cy.wait(1000);
}
});
// Set the page title
PageSelectors.titleInput().should('exist');
cy.wait(1000);
PageSelectors.titleInput()
.first()
.should('be.visible')
.click({ force: true })
.then($el => {
if ($el && $el.length > 0) {
cy.wrap($el)
.clear({ force: true })
.type(testPageName, { force: true })
.type('{enter}');
cy.task('log', `Set page title to: ${testPageName}`);
}
});
// Wait for the title to be saved
cy.wait(2000);
// Step 3: Verify the page exists in sidebar
cy.task('log', '=== Step 3: Verifying page exists in sidebar ===');
// Expand the first space to see its pages
TestTool.expandSpace();
cy.wait(1000);
// 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(', ')}`);
// Check if our page exists
const pageExists = pageNames.some(name =>
name === testPageName || name === 'Untitled'
);
if (pageExists) {
cy.task('log', `✓ 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} ===`);
// Find the page we want to delete
PageSelectors.names().then($pages => {
const pageNames = Array.from($pages).map((el: Element) => el.textContent?.trim());
// Determine the actual name of the page to delete
let pageToDelete = testPageName;
if (!pageNames.includes(testPageName)) {
// If our custom name didn't save, it might be "Untitled"
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`);
}
}
// Delete the page
TestTool.deletePageByName(pageToDelete);
cy.task('log', `✓ 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 ===');
// Click on the trash button in the sidebar
cy.get('[data-testid="sidebar-trash-button"]').click();
// Wait for navigation
cy.wait(2000);
// Verify we're on the trash page
cy.url().should('include', '/app/trash');
cy.task('log', '✓ 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 ===');
// Wait for trash table to load
cy.get('[data-testid="trash-table"]', { timeout: 10000 }).should('be.visible');
// Look for our deleted page in the trash table
cy.get('[data-testid="trash-table-row"]').then($rows => {
let foundPage = false;
// Check each row for our page name
$rows.each((index, row) => {
const rowText = Cypress.$(row).text();
cy.task('log', `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}`);
}
});
// Verify we found the page
if (foundPage) {
cy.task('log', '✓✓✓ 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 ===');
cy.get('[data-testid="trash-table-row"]').first().within(() => {
// Check for restore button
cy.get('[data-testid="trash-restore-button"]').should('exist');
cy.task('log', '✓ Restore button found');
// Check for permanent delete button
cy.get('[data-testid="trash-delete-button"]').should('exist');
cy.task('log', '✓ Permanent delete button found');
});
// Step 8: Restore the deleted page
cy.task('log', '=== 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
// Click the restore button on the first row (our deleted page)
cy.get('[data-testid="trash-table-row"]').first().within(() => {
// Get the page name before restoring
cy.get('td').first().invoke('text').then((text) => {
restoredPageName = text.trim() || 'Untitled';
cy.task('log', `Restoring page: ${restoredPageName}`);
});
// Click restore button
cy.get('[data-testid="trash-restore-button"]').click();
});
// Wait for restore to complete
cy.wait(2000);
cy.task('log', '✓ Restore button clicked');
// Step 9: Verify the page is removed from trash
cy.task('log', '=== 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) => {
// Check if there are any rows left in trash
const rowsExist = $body.find('[data-testid="trash-table-row"]').length > 0;
if (!rowsExist) {
cy.task('log', '✓ Trash is now empty - page successfully removed from trash');
} else {
// If there are still rows, verify our page is not among them
cy.get('[data-testid="trash-table-row"]').then($rows => {
let pageStillInTrash = false;
$rows.each((index, row) => {
const rowText = Cypress.$(row).text();
if (rowText.includes(restoredPageName)) {
pageStillInTrash = true;
}
});
if (pageStillInTrash) {
throw new Error(`Page "${restoredPageName}" is still in trash after restore`);
} else {
cy.task('log', `✓ Page "${restoredPageName}" successfully removed from trash`);
}
});
}
});
// Step 10: Navigate back to the main workspace
cy.task('log', '=== Step 10: Navigating back to workspace ===');
// Click on the workspace/home to go back
cy.visit(`/app`);
cy.wait(3000);
// Wait for the sidebar to load
SidebarSelectors.pageHeader().should('be.visible', { timeout: 10000 });
cy.task('log', '✓ Navigated back to workspace');
// Step 11: Verify the restored page exists in sidebar
cy.task('log', '=== Step 11: Verifying restored page exists in sidebar ===');
// Expand the space to see all pages
TestTool.expandSpace();
cy.wait(1000);
// 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(', ')}`);
// Check if our restored page exists
const pageRestored = pageNames.some(name =>
name === restoredPageName || name === testPageName || name === 'Untitled'
);
if (pageRestored) {
cy.task('log', `✓✓✓ 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! ===');
});
});
});
});