diff --git a/cypress/e2e/database/checkbox-column.cy.ts b/cypress/e2e/database/checkbox-column.cy.ts index 5f3aaade..8d354146 100644 --- a/cypress/e2e/database/checkbox-column.cy.ts +++ b/cypress/e2e/database/checkbox-column.cy.ts @@ -1,10 +1,10 @@ +import { AuthTestUtils } from '../../support/auth-utils'; import { AddPageSelectors, - DatabaseGridSelectors, CheckboxSelectors, + DatabaseGridSelectors, waitForReactUpdate } from '../../support/selectors'; -import { AuthTestUtils } from '../../support/auth-utils'; import { generateRandomEmail } from '../../support/test-config'; describe('Checkbox Column Type', () => { @@ -69,6 +69,7 @@ describe('Checkbox Column Type', () => { cy.log('[STEP 10] No checkbox cells found, cell interaction test completed'); } }); + }); cy.log('[STEP 12] Test completed successfully'); }); diff --git a/cypress/e2e/page/delete-page-verify-trash.cy.ts b/cypress/e2e/page/delete-page-verify-trash.cy.ts index 195d4bd5..95f214d9 100644 --- a/cypress/e2e/page/delete-page-verify-trash.cy.ts +++ b/cypress/e2e/page/delete-page-verify-trash.cy.ts @@ -1,6 +1,6 @@ import { AuthTestUtils } from '../../support/auth-utils'; import { TestTool } from '../../support/page-utils'; -import { PageSelectors, ModalSelectors, SidebarSelectors, TrashSelectors, waitForReactUpdate } from '../../support/selectors'; +import { ModalSelectors, PageSelectors, SidebarSelectors, TrashSelectors, waitForReactUpdate } from '../../support/selectors'; import { generateRandomEmail, logAppFlowyEnvironment } from '../../support/test-config'; import { testLog } from '../../support/test-helpers'; @@ -30,44 +30,44 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { }); // Step 1: Login - testLog.info( '=== Step 1: Login ==='); + testLog.info('=== 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 - testLog.info( '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!'); - + // 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 - testLog.info( 'Looking for new page button...'); + testLog.info('Looking for new page button...'); PageSelectors.newPageButton() .should('exist', { timeout: 20000 }) .then(() => { - testLog.info( 'New page button found!'); + testLog.info('New page button found!'); }); // Step 2: Create a new page - testLog.info( `=== Step 2: Creating page with title: ${testPageName} ===`); - + testLog.info(`=== 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 @@ -76,23 +76,23 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { // 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) => { if ($body.find('[role="dialog"]').length > 0 || $body.find('.MuiDialog-container').length > 0) { - testLog.info( 'Closing modal dialog'); + testLog.info('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') @@ -103,16 +103,16 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { .clear({ force: true }) .type(testPageName, { force: true }) .type('{enter}'); - testLog.info( `Set page title to: ${testPageName}`); + testLog.info(`Set page title to: ${testPageName}`); } }); - + // Wait for the title to be saved cy.wait(2000); // Step 3: Verify the page exists in sidebar - testLog.info( '=== 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(); cy.wait(1000); @@ -120,27 +120,27 @@ 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()); - testLog.info( `Found pages: ${pageNames.join(', ')}`); - + testLog.info(`Found pages: ${pageNames.join(', ')}`); + // Check if our page exists - const pageExists = pageNames.some(name => + const pageExists = pageNames.some(name => name === testPageName || name === 'Untitled' ); - + if (pageExists) { - testLog.info( `✓ 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 - testLog.info( `=== Step 4: Deleting page: ${testPageName} ===`); - + testLog.info(`=== 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)) { @@ -148,159 +148,167 @@ describe('Delete Page, Verify in Trash, and Restore Tests', () => { const untitledPages = pageNames.filter(name => name === 'Untitled'); if (untitledPages.length > 0) { pageToDelete = 'Untitled'; - testLog.info( `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); - testLog.info( `✓ Deleted page: ${pageToDelete}`); + testLog.info(`✓ Deleted page: ${pageToDelete}`); }); // Wait for deletion to complete cy.wait(2000); // Step 5: Navigate to trash page - testLog.info( '=== Step 5: Navigating to trash page ==='); - + testLog.info('=== Step 5: Navigating to trash page ==='); + // Click on the trash button in the sidebar TrashSelectors.sidebarTrashButton().click(); - + // Wait for navigation cy.wait(2000); - + // Verify we're on the trash page cy.url().should('include', '/app/trash'); - testLog.info( '✓ Successfully navigated to trash page'); + testLog.info('✓ Successfully navigated to trash page'); // Step 6: Verify the deleted page exists in trash - testLog.info( '=== Step 6: Verifying deleted page exists in trash ==='); - + testLog.info('=== Step 6: Verifying deleted page exists in trash ==='); + // Wait for trash table to load TrashSelectors.table().should('be.visible'); - + // Look for our deleted page in the trash table TrashSelectors.rows().then($rows => { let foundPage = false; - + // Check each row for our page name $rows.each((index, row) => { const rowText = Cypress.$(row).text(); - testLog.info( `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; - testLog.info( `✓ Found deleted page in trash: ${rowText}`); + testLog.info(`✓ Found deleted page in trash: ${rowText}`); } }); - + // Verify we found the page if (foundPage) { - testLog.info( '✓✓✓ 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 - testLog.info( '=== Step 7: Verifying trash actions are available ==='); - + testLog.info('=== Step 7: Verifying trash actions are available ==='); + TrashSelectors.rows().first().within(() => { // Check for restore button TrashSelectors.restoreButton().should('exist'); - testLog.info( '✓ Restore button found'); - + testLog.info('✓ Restore button found'); + // Check for permanent delete button TrashSelectors.deleteButton().should('exist'); - testLog.info( '✓ Permanent delete button found'); + testLog.info('✓ Permanent delete button found'); }); // Step 8: Restore the deleted page - testLog.info( '=== 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 - + // Click the restore button on the first row (our deleted page) TrashSelectors.rows().first().within(() => { // Get the page name before restoring cy.get('td').first().invoke('text').then((text) => { restoredPageName = text.trim() || 'Untitled'; - testLog.info( `Restoring page: ${restoredPageName}`); + testLog.info(`Restoring page: ${restoredPageName}`); }); - + // Click restore button TrashSelectors.restoreButton().click(); }); - + // Wait for restore to complete cy.wait(2000); - testLog.info( '✓ Restore button clicked'); + testLog.info('✓ Restore button clicked'); // Step 9: Verify the page is removed from trash - testLog.info( '=== Step 9: Verifying page is removed from trash ==='); - + testLog.info('=== Step 9: Verifying page is removed from trash ==='); + + // Wait a bit for the UI to update after restore + cy.wait(2000); + // Check if trash is now empty or doesn't contain our page - TrashSelectors.rows().then($rows => { - const rowsExist = $rows.length > 0; - + // Use a more defensive approach - check if rows exist first + cy.get('body').then($body => { + // Check if trash table rows exist + const rowsExist = $body.find('[data-testid="trash-table-row"]').length > 0; + if (!rowsExist) { - testLog.info( '✓ Trash is now empty - page successfully removed from trash'); + testLog.info('✓ Trash is now empty - page successfully removed from trash'); } else { - let pageStillInTrash = false; - - $rows.each((index, row) => { - const rowText = Cypress.$(row).text(); - if (rowText.includes(restoredPageName)) { - pageStillInTrash = true; + // Rows still exist, check if our page is among them + TrashSelectors.rows().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 { + testLog.info(`✓ Page "${restoredPageName}" successfully removed from trash`); } }); - - if (pageStillInTrash) { - throw new Error(`Page "${restoredPageName}" is still in trash after restore`); - } else { - testLog.info( `✓ Page "${restoredPageName}" successfully removed from trash`); - } } }); // Step 10: Navigate back to the main workspace - testLog.info( '=== 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`); cy.wait(3000); - + // Wait for the sidebar to load SidebarSelectors.pageHeader().should('be.visible', { timeout: 10000 }); - testLog.info( '✓ Navigated back to workspace'); + testLog.info('✓ Navigated back to workspace'); // Step 11: Verify the restored page exists in sidebar - testLog.info( '=== 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(); 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()); - testLog.info( `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 => + const pageRestored = pageNames.some(name => name === restoredPageName || name === testPageName || name === 'Untitled' ); - + if (pageRestored) { - testLog.info( `✓✓✓ 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(', ')}`); } }); - - testLog.info( '=== 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/publish-page.cy.ts b/cypress/e2e/page/publish-page.cy.ts index 05eeb57e..972255e1 100644 --- a/cypress/e2e/page/publish-page.cy.ts +++ b/cypress/e2e/page/publish-page.cy.ts @@ -15,12 +15,26 @@ describe('Publish Page Test', () => { beforeEach(() => { testEmail = generateRandomEmail(); + + // Handle uncaught exceptions + cy.on('uncaught:exception', (err: Error) => { + if (err.message.includes('No workspace or service found') || + err.message.includes('createThemeNoVars_default is not a function') || + err.message.includes('View not found') || + err.message.includes('Failed to execute \'writeText\' on \'Clipboard\': Document is not focused') || + err.name === 'NotAllowedError') { + return false; + } + return true; + }); }); it('publish page, copy URL, open in browser, unpublish, and verify inaccessible', () => { // Handle uncaught exceptions during workspace creation cy.on('uncaught:exception', (err: Error) => { - if (err.message.includes('No workspace or service found')) { + if (err.message.includes('No workspace or service found') || + err.message.includes('createThemeNoVars_default is not a function') || + err.message.includes('View not found')) { return false; } return true; @@ -32,66 +46,66 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - testLog.info( 'Signed in'); + testLog.info('Signed in'); // Wait for app to fully load - testLog.info( '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(); - testLog.info( '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'); - testLog.info( '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); - testLog.info( 'Switched to Publish tab'); + testLog.info('Switched to Publish tab'); // Verify Publish to Web section is visible cy.contains('Publish to Web').should('exist'); - testLog.info( 'Publish to Web section verified'); + testLog.info('Publish to Web section verified'); // 4. Wait for the publish button to be visible and enabled - testLog.info( 'Waiting for publish button to appear...'); + testLog.info('Waiting for publish button to appear...'); ShareSelectors.publishConfirmButton().should('be.visible').should('not.be.disabled'); - testLog.info( 'Publish button is visible and enabled'); + testLog.info('Publish button is visible and enabled'); // 5. Click Publish button ShareSelectors.publishConfirmButton().click({ force: true }); - testLog.info( '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(ShareSelectors.publishNamespace()).should('be.visible', { timeout: 10000 }); - testLog.info( 'Page published successfully, URL elements visible'); + ShareSelectors.publishNamespace().should('be.visible', { timeout: 10000 }); + testLog.info('Page published successfully, URL elements visible'); // 6. Get the published URL by constructing it from UI elements cy.window().then((win) => { const origin = win.location.origin; // Get namespace and publish name from the UI - cy.get(ShareSelectors.publishNamespace()).should('be.visible').invoke('text').then((namespace) => { - cy.get(ShareSelectors.publishNameInput()).should('be.visible').invoke('val').then((publishName) => { + ShareSelectors.publishNamespace().should('be.visible').invoke('text').then((namespace) => { + ShareSelectors.publishNameInput().should('be.visible').invoke('val').then((publishName) => { const namespaceText = namespace.trim(); const publishNameText = String(publishName).trim(); const publishedUrl = `${origin}/${namespaceText}/${publishNameText}`; - testLog.info( `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 // Located in a div with class "p-1 text-text-primary" next to the URL container ShareSelectors.sharePopover().within(() => { // Find the parent container that holds both URL inputs and copy button - cy.get(ShareSelectors.publishNameInput()) + ShareSelectors.publishNameInput() .closest('div.flex.w-full.items-center.overflow-hidden') .find('div.p-1.text-text-primary') .should('be.visible') @@ -100,19 +114,19 @@ describe('Publish Page Test', () => { .click({ force: true }); }); - testLog.info( 'Clicked copy link button'); + testLog.info('Clicked copy link button'); // Wait for copy operation and notification to appear cy.wait(2000); - testLog.info( 'Copy operation completed'); + testLog.info('Copy operation completed'); // 8. Open the URL in browser (copy button was clicked, URL is ready) - testLog.info( `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}`); - testLog.info( 'Published page opened successfully'); + testLog.info('Published page opened successfully'); // Wait for page content to load cy.wait(3000); @@ -124,14 +138,14 @@ describe('Publish Page Test', () => { cy.get('body').then(($body) => { const bodyText = $body.text(); if (bodyText.includes('404') || bodyText.includes('Not Found')) { - testLog.info( '⚠ Warning: Page might not be accessible (404 detected)'); + testLog.info('⚠ Warning: Page might not be accessible (404 detected)'); } else { - testLog.info( '✓ Published page verified and accessible'); + testLog.info('✓ Published page verified and accessible'); } }); // 10. Go back to the app to unpublish the page - testLog.info( '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); @@ -141,34 +155,34 @@ describe('Publish Page Test', () => { // 11. Open share popover again to unpublish TestTool.openSharePopover(); - testLog.info( '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); - testLog.info( '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 }); - testLog.info( 'Unpublish button is visible'); + testLog.info('Unpublish button is visible'); // 12. Click Unpublish button ShareSelectors.unpublishButton().click({ force: true }); - testLog.info( '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 }); - testLog.info( '✓ 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 - testLog.info( `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 @@ -185,7 +199,7 @@ describe('Publish Page Test', () => { }).then((response) => { // Check status code first if (response.status !== 200) { - testLog.info( `✓ 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 || ''; @@ -212,16 +226,16 @@ describe('Publish Page Test', () => { const wasRedirected = !currentUrl.includes(`/${namespaceText}/${publishNameText}`); if (hasErrorInResponse || hasErrorInBody || wasRedirected) { - testLog.info( `✓ 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) { - testLog.info( `✓ 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 - testLog.info( `⚠ Note: Page appears accessible, but unpublish was executed successfully`); + testLog.info(`⚠ Note: Page appears accessible, but unpublish was executed successfully`); } } }); @@ -236,7 +250,9 @@ describe('Publish Page Test', () => { it('publish page and use Visit Site button to open URL', () => { cy.on('uncaught:exception', (err: Error) => { - if (err.message.includes('No workspace or service found')) { + if (err.message.includes('No workspace or service found') || + err.message.includes('createThemeNoVars_default is not a function') || + err.message.includes('View not found')) { return false; } return true; @@ -247,7 +263,7 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - testLog.info( 'Signed in'); + testLog.info('Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -259,30 +275,30 @@ describe('Publish Page Test', () => { cy.wait(1000); ShareSelectors.publishConfirmButton().should('be.visible').should('not.be.disabled').click({ force: true }); - testLog.info( 'Clicked Publish button'); + testLog.info('Clicked Publish button'); cy.wait(5000); // Verify published - cy.get(ShareSelectors.publishNamespace()).should('be.visible', { timeout: 10000 }); + ShareSelectors.publishNamespace().should('be.visible', { timeout: 10000 }); // Get the published URL cy.window().then((win) => { const origin = win.location.origin; - cy.get(ShareSelectors.publishNamespace()).should('be.visible').invoke('text').then((namespace) => { - cy.get(ShareSelectors.publishNameInput()).should('be.visible').invoke('val').then((publishName) => { + ShareSelectors.publishNamespace().should('be.visible').invoke('text').then((namespace) => { + ShareSelectors.publishNameInput().should('be.visible').invoke('val').then((publishName) => { const publishedUrl = `${origin}/${namespace.trim()}/${String(publishName).trim()}`; - testLog.info( `Published URL: ${publishedUrl}`); + testLog.info(`Published URL: ${publishedUrl}`); // Click the Visit Site button ShareSelectors.visitSiteButton().should('be.visible').click({ force: true }); - testLog.info( '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 - testLog.info( '✓ Visit Site button is functional'); + testLog.info('✓ Visit Site button is functional'); }); }); }); @@ -291,7 +307,9 @@ describe('Publish Page Test', () => { it('publish page, edit publish name, and verify new URL works', () => { cy.on('uncaught:exception', (err: Error) => { - if (err.message.includes('No workspace or service found')) { + if (err.message.includes('No workspace or service found') || + err.message.includes('createThemeNoVars_default is not a function') || + err.message.includes('View not found')) { return false; } return true; @@ -302,7 +320,7 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - testLog.info( 'Signed in'); + testLog.info('Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -315,35 +333,35 @@ describe('Publish Page Test', () => { ShareSelectors.publishConfirmButton().should('be.visible').click({ force: true }); cy.wait(5000); - cy.get(ShareSelectors.publishNamespace()).should('be.visible', { timeout: 10000 }); + ShareSelectors.publishNamespace().should('be.visible', { timeout: 10000 }); // Get original URL cy.window().then((win) => { const origin = win.location.origin; - cy.get(ShareSelectors.publishNamespace()).invoke('text').then((namespace) => { - cy.get(ShareSelectors.publishNameInput()).invoke('val').then((originalName) => { + ShareSelectors.publishNamespace().invoke('text').then((namespace) => { + ShareSelectors.publishNameInput().invoke('val').then((originalName) => { const namespaceText = namespace.trim(); const originalNameText = String(originalName).trim(); - testLog.info( `Original publish name: ${originalNameText}`); + testLog.info(`Original publish name: ${originalNameText}`); // Edit the publish name directly in the input const newPublishName = `custom-name-${Date.now()}`; - cy.get(ShareSelectors.publishNameInput()) + ShareSelectors.publishNameInput() .clear() .type(newPublishName) .blur(); - testLog.info( `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}`; - testLog.info( `New published URL: ${newPublishedUrl}`); + testLog.info(`New published URL: ${newPublishedUrl}`); cy.visit(newPublishedUrl, { failOnStatusCode: false }); cy.wait(3000); cy.url().should('include', `/${namespaceText}/${newPublishName}`); - testLog.info( '✓ New publish name URL works correctly'); + testLog.info('✓ New publish name URL works correctly'); }); }); }); @@ -352,7 +370,9 @@ describe('Publish Page Test', () => { it('publish, modify content, republish, and verify content changes', () => { cy.on('uncaught:exception', (err: Error) => { - if (err.message.includes('No workspace or service found')) { + if (err.message.includes('No workspace or service found') || + err.message.includes('createThemeNoVars_default is not a function') || + err.message.includes('View not found')) { return false; } return true; @@ -366,14 +386,14 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - testLog.info( '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 - testLog.info( '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,26 +417,26 @@ describe('Publish Page Test', () => { ShareSelectors.publishConfirmButton().should('be.visible').click({ force: true }); cy.wait(5000); - cy.get(ShareSelectors.publishNamespace()).should('be.visible', { timeout: 10000 }); - testLog.info( '✓ First publish successful'); + ShareSelectors.publishNamespace().should('be.visible', { timeout: 10000 }); + testLog.info('✓ First publish successful'); // Get published URL cy.window().then((win) => { const origin = win.location.origin; - cy.get(ShareSelectors.publishNamespace()).invoke('text').then((namespace) => { - cy.get(ShareSelectors.publishNameInput()).invoke('val').then((publishName) => { + ShareSelectors.publishNamespace().invoke('text').then((namespace) => { + ShareSelectors.publishNameInput().invoke('val').then((publishName) => { const publishedUrl = `${origin}/${namespace.trim()}/${String(publishName).trim()}`; - testLog.info( `Published URL: ${publishedUrl}`); + testLog.info(`Published URL: ${publishedUrl}`); // Verify initial content is published - testLog.info( '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); - testLog.info( '✓ Initial content verified on published page'); + testLog.info('✓ Initial content verified on published page'); // Go back to app and modify content - testLog.info( '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 }); @@ -427,7 +447,7 @@ describe('Publish Page Test', () => { cy.wait(3000); // Modify the page content - testLog.info( 'Modifying page content'); + testLog.info('Modifying page content'); cy.get('[contenteditable="true"]').then(($editors) => { let editorFound = false; $editors.each((index: number, el: HTMLElement) => { @@ -445,7 +465,7 @@ describe('Publish Page Test', () => { cy.wait(5000); // Wait for content to save // Republish to sync the updated content - testLog.info( '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,17 +478,17 @@ describe('Publish Page Test', () => { // Republish with updated content ShareSelectors.publishConfirmButton().should('be.visible').click({ force: true }); cy.wait(5000); - cy.get(ShareSelectors.publishNamespace()).should('be.visible', { timeout: 10000 }); - testLog.info( '✓ Republished successfully'); + ShareSelectors.publishNamespace().should('be.visible', { timeout: 10000 }); + testLog.info('✓ Republished successfully'); // Verify updated content is published - testLog.info( '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); - testLog.info( '✓ Updated content verified on published page'); + testLog.info('✓ Updated content verified on published page'); }); }); }); @@ -477,7 +497,9 @@ describe('Publish Page Test', () => { it('test publish name validation - invalid characters', () => { cy.on('uncaught:exception', (err: Error) => { - if (err.message.includes('No workspace or service found')) { + if (err.message.includes('No workspace or service found') || + err.message.includes('createThemeNoVars_default is not a function') || + err.message.includes('View not found')) { return false; } return true; @@ -488,7 +510,7 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - testLog.info( 'Signed in'); + testLog.info('Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -501,14 +523,14 @@ describe('Publish Page Test', () => { ShareSelectors.publishConfirmButton().should('be.visible').click({ force: true }); cy.wait(5000); - cy.get(ShareSelectors.publishNamespace()).should('be.visible', { timeout: 10000 }); + ShareSelectors.publishNamespace().should('be.visible', { timeout: 10000 }); // Try to set invalid publish name with spaces - cy.get(ShareSelectors.publishNameInput()).invoke('val').then((originalName) => { - testLog.info( `Original name: ${originalName}`); + ShareSelectors.publishNameInput().invoke('val').then((originalName) => { + testLog.info(`Original name: ${originalName}`); // Try to set name with space (should be rejected) - cy.get(ShareSelectors.publishNameInput()) + ShareSelectors.publishNameInput() .clear() .type('invalid name with spaces') .blur(); @@ -519,12 +541,12 @@ describe('Publish Page Test', () => { cy.get('body').then(($body) => { const bodyText = $body.text(); // The name should either revert or show an error - cy.get(ShareSelectors.publishNameInput()).invoke('val').then((currentName) => { + ShareSelectors.publishNameInput().invoke('val').then((currentName) => { // Name should not contain spaces (validation should prevent it) if (String(currentName).includes(' ')) { - testLog.info( '⚠ Warning: Invalid characters were not rejected'); + testLog.info('⚠ Warning: Invalid characters were not rejected'); } else { - testLog.info( '✓ Invalid characters (spaces) were rejected'); + testLog.info('✓ Invalid characters (spaces) were rejected'); } }); }); @@ -534,7 +556,9 @@ describe('Publish Page Test', () => { it('test publish settings - toggle comments and duplicate switches', () => { cy.on('uncaught:exception', (err: Error) => { - if (err.message.includes('No workspace or service found')) { + if (err.message.includes('No workspace or service found') || + err.message.includes('createThemeNoVars_default is not a function') || + err.message.includes('View not found')) { return false; } return true; @@ -545,7 +569,7 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - testLog.info( 'Signed in'); + testLog.info('Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -558,7 +582,7 @@ describe('Publish Page Test', () => { ShareSelectors.publishConfirmButton().should('be.visible').click({ force: true }); cy.wait(5000); - cy.get(ShareSelectors.publishNamespace()).should('be.visible', { timeout: 10000 }); + ShareSelectors.publishNamespace().should('be.visible', { timeout: 10000 }); // Test comments switch - find by looking for Switch components in the published panel ShareSelectors.sharePopover().within(() => { @@ -566,18 +590,18 @@ describe('Publish Page Test', () => { // Look for the container divs that have the text labels 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'); - testLog.info( `Initial comments state: ${initialCommentsState}`); + const initialCommentsState = ($checkbox[0] as HTMLInputElement).checked; + testLog.info(`Initial comments state: ${initialCommentsState}`); // Toggle comments by clicking the switch cy.get('input[type="checkbox"]').click({ force: true }); cy.wait(2000); cy.get('input[type="checkbox"]').then(($checkboxAfter) => { - const newCommentsState = $checkboxAfter.is(':checked'); - testLog.info( `Comments state after toggle: ${newCommentsState}`); + const newCommentsState = ($checkboxAfter[0] as HTMLInputElement).checked; + testLog.info(`Comments state after toggle: ${newCommentsState}`); expect(newCommentsState).to.not.equal(initialCommentsState); - testLog.info( '✓ Comments switch toggled successfully'); + testLog.info('✓ Comments switch toggled successfully'); }); }); }); @@ -585,18 +609,18 @@ describe('Publish Page Test', () => { // Test duplicate switch 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'); - testLog.info( `Initial duplicate state: ${initialDuplicateState}`); + const initialDuplicateState = ($checkbox[0] as HTMLInputElement).checked; + testLog.info(`Initial duplicate state: ${initialDuplicateState}`); // Toggle duplicate cy.get('input[type="checkbox"]').click({ force: true }); cy.wait(2000); cy.get('input[type="checkbox"]').then(($checkboxAfter) => { - const newDuplicateState = $checkboxAfter.is(':checked'); - testLog.info( `Duplicate state after toggle: ${newDuplicateState}`); + const newDuplicateState = ($checkboxAfter[0] as HTMLInputElement).checked; + testLog.info(`Duplicate state after toggle: ${newDuplicateState}`); expect(newDuplicateState).to.not.equal(initialDuplicateState); - testLog.info( '✓ Duplicate switch toggled successfully'); + testLog.info('✓ Duplicate switch toggled successfully'); }); }); }); @@ -606,7 +630,9 @@ describe('Publish Page Test', () => { it('publish page multiple times - verify URL remains consistent', () => { cy.on('uncaught:exception', (err: Error) => { - if (err.message.includes('No workspace or service found')) { + if (err.message.includes('No workspace or service found') || + err.message.includes('createThemeNoVars_default is not a function') || + err.message.includes('View not found')) { return false; } return true; @@ -617,7 +643,7 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - testLog.info( 'Signed in'); + testLog.info('Signed in'); SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 }); PageSelectors.names().should('exist', { timeout: 30000 }); @@ -632,15 +658,15 @@ describe('Publish Page Test', () => { ShareSelectors.publishConfirmButton().should('be.visible').click({ force: true }); cy.wait(5000); - cy.get(ShareSelectors.publishNamespace()).should('be.visible', { timeout: 10000 }); + ShareSelectors.publishNamespace().should('be.visible', { timeout: 10000 }); // Get first URL cy.window().then((win) => { const origin = win.location.origin; - cy.get(ShareSelectors.publishNamespace()).invoke('text').then((namespace) => { - cy.get(ShareSelectors.publishNameInput()).invoke('val').then((publishName) => { + ShareSelectors.publishNamespace().invoke('text').then((namespace) => { + ShareSelectors.publishNameInput().invoke('val').then((publishName) => { firstPublishedUrl = `${origin}/${namespace.trim()}/${String(publishName).trim()}`; - testLog.info( `First published URL: ${firstPublishedUrl}`); + testLog.info(`First published URL: ${firstPublishedUrl}`); // Close and reopen share popover cy.get('body').type('{esc}'); @@ -651,14 +677,14 @@ describe('Publish Page Test', () => { cy.contains('Publish').should('exist').click({ force: true }); cy.wait(1000); - cy.get(ShareSelectors.publishNamespace()).should('be.visible', { timeout: 10000 }); - cy.get(ShareSelectors.publishNamespace()).invoke('text').then((namespace2) => { - cy.get(ShareSelectors.publishNameInput()).invoke('val').then((publishName2) => { + ShareSelectors.publishNamespace().should('be.visible', { timeout: 10000 }); + ShareSelectors.publishNamespace().invoke('text').then((namespace2) => { + ShareSelectors.publishNameInput().invoke('val').then((publishName2) => { const secondPublishedUrl = `${origin}/${namespace2.trim()}/${String(publishName2).trim()}`; - testLog.info( `Second check URL: ${secondPublishedUrl}`); + testLog.info(`Second check URL: ${secondPublishedUrl}`); expect(secondPublishedUrl).to.equal(firstPublishedUrl); - testLog.info( '✓ Published URL remains consistent across multiple opens'); + testLog.info('✓ Published URL remains consistent across multiple opens'); }); }); }); @@ -669,7 +695,9 @@ describe('Publish Page Test', () => { it('publish database (To-dos) and visit published link', () => { cy.on('uncaught:exception', (err: Error) => { - if (err.message.includes('No workspace or service found')) { + if (err.message.includes('No workspace or service found') || + err.message.includes('createThemeNoVars_default is not a function') || + err.message.includes('View not found')) { return false; } return true; @@ -680,14 +708,14 @@ describe('Publish Page Test', () => { const authUtils = new AuthTestUtils(); authUtils.signInWithTestUrl(testEmail).then(() => { cy.url().should('include', '/app'); - testLog.info( '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 - testLog.info( '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 @@ -695,7 +723,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) { - testLog.info( 'Closing modal dialog'); + testLog.info('Closing modal dialog'); cy.get('body').type('{esc}'); cy.wait(2000); // Try again if still open @@ -709,7 +737,7 @@ describe('Publish Page Test', () => { }); // Verify we're on a database view (not a document) - testLog.info( '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 @@ -719,59 +747,59 @@ describe('Publish Page Test', () => { ShareSelectors.shareButton().should('be.visible', { timeout: 10000 }); // Open share popover and publish - testLog.info( 'Opening share popover to publish database'); + testLog.info('Opening share popover to publish database'); TestTool.openSharePopover(); - testLog.info( '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'); - testLog.info( '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); - testLog.info( 'Switched to Publish tab'); + testLog.info('Switched to Publish tab'); // Verify Publish to Web section is visible cy.contains('Publish to Web').should('exist'); - testLog.info( 'Publish to Web section verified'); + testLog.info('Publish to Web section verified'); // Wait for the publish button to be visible and enabled - testLog.info( 'Waiting for publish button to appear...'); + testLog.info('Waiting for publish button to appear...'); ShareSelectors.publishConfirmButton().should('be.visible').should('not.be.disabled'); - testLog.info( 'Publish button is visible and enabled'); + testLog.info('Publish button is visible and enabled'); // Click Publish button ShareSelectors.publishConfirmButton().click({ force: true }); - testLog.info( '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(ShareSelectors.publishNamespace()).should('be.visible', { timeout: 10000 }); - testLog.info( 'Database published successfully, URL elements visible'); + ShareSelectors.publishNamespace().should('be.visible', { timeout: 10000 }); + testLog.info('Database published successfully, URL elements visible'); // Get the published URL cy.window().then((win) => { const origin = win.location.origin; // Get namespace and publish name from the UI - cy.get(ShareSelectors.publishNamespace()).should('be.visible').invoke('text').then((namespace) => { - cy.get(ShareSelectors.publishNameInput()).should('be.visible').invoke('val').then((publishName) => { + ShareSelectors.publishNamespace().should('be.visible').invoke('text').then((namespace) => { + ShareSelectors.publishNameInput().should('be.visible').invoke('val').then((publishName) => { const namespaceText = namespace.trim(); const publishNameText = String(publishName).trim(); const publishedUrl = `${origin}/${namespaceText}/${publishNameText}`; - testLog.info( `Constructed published database URL: ${publishedUrl}`); + testLog.info(`Constructed published database URL: ${publishedUrl}`); // Visit the published database URL - testLog.info( `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}`); - testLog.info( 'Published database opened successfully'); + testLog.info('Published database opened successfully'); // Wait for database content to load cy.wait(5000); @@ -783,15 +811,15 @@ describe('Publish Page Test', () => { cy.get('body').then(($body) => { const bodyText = $body.text(); if (bodyText.includes('404') || bodyText.includes('Not Found')) { - testLog.info( '⚠ 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 - testLog.info( '✓ 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'); - testLog.info( '✓ Database view elements present'); + testLog.info('✓ Database view elements present'); } }); });