From 4dd11162166808443b77601b62dceae72650e7a5 Mon Sep 17 00:00:00 2001 From: Bruno Amui Date: Fri, 7 Nov 2025 23:38:17 -0300 Subject: [PATCH 1/5] fix: reduce e2e test timeouts and add detailed error logging - Reduce pageLoadTimeout from 120s to 30s for faster failures - Add defaultCommandTimeout of 10s - Update all cy.visit() calls to use 30s timeout with failOnStatusCode: false - Add detailed timing logs for page loads in both test suites - Add error logging for uncaught exceptions with context - Log selected tutorial URL in copy-for-llm test for debugging - Add visit timing information to identify slow/hanging pages This will help tests fail fast (within 30s instead of 120s) and provide better diagnostics about which resources are causing page load timeouts. --- cypress.config.cjs | 3 +- .../cypress/integration/copy-for-llm.cy.js | 78 +++++++++++++++++-- .../helpcenter-navigation-status.cy.js | 27 ++++++- 3 files changed, 98 insertions(+), 10 deletions(-) diff --git a/cypress.config.cjs b/cypress.config.cjs index 5256074d..43d64e66 100644 --- a/cypress.config.cjs +++ b/cypress.config.cjs @@ -16,6 +16,7 @@ module.exports = defineConfig({ specPattern: 'src/tests/cypress/integration/**/*.cy.{js,jsx,ts,tsx}', supportFile: 'src/tests/cypress/support/index.js', baseUrl: 'http://localhost:3030', - pageLoadTimeout: 120000, // Increase timeout to 120 seconds for Netlify preview + pageLoadTimeout: 30000, // Fail fast - max 30 seconds for page load + defaultCommandTimeout: 10000, // 10 seconds for commands }, }) diff --git a/src/tests/cypress/integration/copy-for-llm.cy.js b/src/tests/cypress/integration/copy-for-llm.cy.js index e2210fa6..8787d0b9 100644 --- a/src/tests/cypress/integration/copy-for-llm.cy.js +++ b/src/tests/cypress/integration/copy-for-llm.cy.js @@ -63,9 +63,35 @@ describe('Copy for AI Feature', () => { ? tutorialsSection.slugPrefix.slice(0, -1) : tutorialsSection.slugPrefix tutorialUrl = `${prefix}/${slug}` - cy.log(`Using tutorial: ${tutorialUrl}`) + cy.log(`📄 Selected tutorial: ${tutorialUrl}`) cy.log( - `Language slugs - EN: ${tutorialSlugs.en}, ES: ${tutorialSlugs.es}, PT: ${tutorialSlugs.pt}` + `🌐 Language slugs - EN: ${tutorialSlugs.en}, ES: ${tutorialSlugs.es}, PT: ${tutorialSlugs.pt}` + ) + + // Write to cypress log file for CI visibility + cy.writeFile( + 'cypress.log', + `Selected tutorial URL: ${tutorialUrl}\n`, + { + flag: 'a+', + } + ) + cy.writeFile('cypress.log', `EN slug: ${tutorialSlugs.en}\n`, { + flag: 'a+', + }) + cy.writeFile( + 'cypress.log', + `ES slug: ${tutorialSlugs.es || 'N/A'}\n`, + { + flag: 'a+', + } + ) + cy.writeFile( + 'cypress.log', + `PT slug: ${tutorialSlugs.pt || 'N/A'}\n\n`, + { + flag: 'a+', + } ) } else { throw new Error('No markdown tutorial found in navigation') @@ -79,11 +105,15 @@ describe('Copy for AI Feature', () => { beforeEach(() => { // Handle hydration errors globally for all tests cy.on('uncaught:exception', (err) => { + // Log all uncaught exceptions for debugging + cy.log(`âš ī¸ Uncaught exception: ${err.message}`) + // Ignore hydration-related errors if ( err.message.includes('Suspense boundary') || err.message.includes('hydrating') ) { + cy.log(`â„šī¸ (Ignoring hydration error)`) return false } return true @@ -99,14 +129,34 @@ describe('Copy for AI Feature', () => { }) it('Should find the Copy for AI button in English', () => { - cy.visit(tutorialUrl) + const startTime = Date.now() + cy.log(`🔗 Visiting: ${tutorialUrl}`) + + cy.visit(tutorialUrl, { + timeout: 30000, + failOnStatusCode: false, + }).then( + () => { + const loadTime = Date.now() - startTime + cy.log(`✅ Page loaded in ${loadTime}ms`) + }, + (err) => { + const loadTime = Date.now() - startTime + cy.log(`❌ Visit failed after ${loadTime}ms: ${err.message}`) + throw err + } + ) + cy.contains('button', 'Copy for AI', { timeout: 10000 }).should( 'be.visible' ) }) it('Should copy content to clipboard in English and verify', () => { - cy.visit(tutorialUrl) + cy.visit(tutorialUrl, { + timeout: 30000, + failOnStatusCode: false, + }) // Stub document.execCommand to capture copy operations cy.document().then((doc) => { @@ -149,7 +199,10 @@ describe('Copy for AI Feature', () => { }) it('Should switch to Spanish and find Copy for AI button', () => { - cy.visit(tutorialUrl) + cy.visit(tutorialUrl, { + timeout: 30000, + failOnStatusCode: false, + }) // Click language switcher cy.contains('button', 'EN').click() @@ -176,7 +229,10 @@ describe('Copy for AI Feature', () => { tutorialSlugs.es }` - cy.visit(spanishUrl) + cy.visit(spanishUrl, { + timeout: 30000, + failOnStatusCode: false, + }) // Wait for page to be fully loaded and hydrated cy.get('article', { timeout: 15000 }).should('be.visible') @@ -229,7 +285,10 @@ describe('Copy for AI Feature', () => { }) it('Should switch to Portuguese and find Copy for AI button', () => { - cy.visit(tutorialUrl) + cy.visit(tutorialUrl, { + timeout: 30000, + failOnStatusCode: false, + }) // Click language switcher cy.contains('button', 'EN').click() @@ -256,7 +315,10 @@ describe('Copy for AI Feature', () => { tutorialSlugs.pt }` - cy.visit(portugueseUrl) + cy.visit(portugueseUrl, { + timeout: 30000, + failOnStatusCode: false, + }) // Wait for page to be fully loaded and hydrated cy.get('article', { timeout: 15000 }).should('be.visible') diff --git a/src/tests/cypress/integration/helpcenter-navigation-status.cy.js b/src/tests/cypress/integration/helpcenter-navigation-status.cy.js index ad1cbe2d..bf8b3b6a 100644 --- a/src/tests/cypress/integration/helpcenter-navigation-status.cy.js +++ b/src/tests/cypress/integration/helpcenter-navigation-status.cy.js @@ -88,6 +88,9 @@ describe('Help Center Navigation Status Test', () => { it('should successfully load randomly selected pages from each navbar section', () => { // Handle hydration and React errors during page loads cy.on('uncaught:exception', (err) => { + // Log all uncaught exceptions for debugging + cy.task('log', ` âš ī¸ Uncaught exception: ${err.message}`) + // Ignore hydration-related errors and React minified errors if ( err.message.includes('Suspense boundary') || @@ -95,6 +98,7 @@ describe('Help Center Navigation Status Test', () => { err.message.includes('Minified React error') || err.message.includes('invariant') ) { + cy.task('log', ` â„šī¸ (Ignoring hydration/React error)`) return false } return true @@ -111,7 +115,28 @@ describe('Help Center Navigation Status Test', () => { cy.task('log', ` Page: "${page.name}"`) cy.task('log', ` URL: ${page.url}`) - cy.visit(page.url, { failOnStatusCode: false }) + const startTime = Date.now() + + cy.visit(page.url, { + timeout: 30000, + failOnStatusCode: false, + onBeforeLoad: () => { + cy.task('log', ` âąī¸ Page load started`) + }, + }).then( + () => { + const totalTime = Date.now() - startTime + cy.task('log', ` âąī¸ Visit completed in ${totalTime}ms`) + }, + (err) => { + const totalTime = Date.now() - startTime + cy.task( + 'log', + ` ❌ Visit failed after ${totalTime}ms: ${err.message}` + ) + throw err + } + ) // Verify page loads successfully cy.url().should('include', page.url.replace(baseUrl, '')) From 85384d8856f1b11885973e5c9e9b0eb770985fd7 Mon Sep 17 00:00:00 2001 From: Bruno Amui Date: Sat, 8 Nov 2025 00:06:56 -0300 Subject: [PATCH 2/5] fix: skip local build in e2e workflow - test against Netlify preview only The cypress-io/github-action was building the app locally and running tests against localhost, which was slow and unnecessary since we want to test the actual Netlify deploy preview. This change skips the build step and runs tests directly against the Netlify preview URL, making the workflow much faster and testing the actual deployed artifact. --- .github/workflows/e2e-tests.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 8eb7bb38..367cb3da 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -1,12 +1,12 @@ name: E2E Tests -on: +on: pull_request: types: [opened, synchronize, reopened] jobs: e2e-tests: runs-on: ubuntu-latest - + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -21,12 +21,13 @@ jobs: checkInterval: 10 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + - name: Run Cypress E2E tests uses: cypress-io/github-action@v6 with: wait-on: 'https://deploy-preview-${{ github.event.pull_request.number }}--leafy-mooncake-7c2e5e.netlify.app' wait-on-timeout: 120 + build: echo "Skipping build - testing against Netlify preview" env: CYPRESS_baseUrl: https://deploy-preview-${{ github.event.pull_request.number }}--leafy-mooncake-7c2e5e.netlify.app @@ -53,13 +54,13 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "**Deployment URL:** https://deploy-preview-${{ github.event.pull_request.number }}--leafy-mooncake-7c2e5e.netlify.app" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - + if [[ "${{ job.status }}" == "success" ]]; then echo "✅ All tests passed!" >> $GITHUB_STEP_SUMMARY else echo "❌ Some tests failed. Check the logs for details." >> $GITHUB_STEP_SUMMARY fi - + if [[ -f "./cypress.log" ]]; then echo "" >> $GITHUB_STEP_SUMMARY echo "### Failed Tests" >> $GITHUB_STEP_SUMMARY From 02f40904c7dd0fc7c90342c0659dc9d1e45ce8ec Mon Sep 17 00:00:00 2001 From: Bruno Amui Date: Sat, 8 Nov 2025 00:12:21 -0300 Subject: [PATCH 3/5] feat: add comprehensive network and Cypress diagnostics to CI Added detailed diagnostics to identify why tests run locally but timeout in CI: Network Diagnostics: - DNS resolution test for deploy preview - HTTP response time measurements (connect, transfer, total) - Navigation API endpoint connectivity test - All results logged to GitHub step summary Cypress Debugging: - Enabled DEBUG=cypress:server:* for detailed Cypress logs - Added ELECTRON_ENABLE_LOGGING for browser debugging - Added 'time' command prefix to measure test execution time - Kept existing detailed logging in test files This will help us understand: - Network latency from GitHub Actions to Netlify - DNS resolution issues - Whether the deploy preview is actually accessible - Timing breakdown of where delays occur - Any Cypress-specific issues in the CI environment --- .github/workflows/e2e-tests.yml | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 367cb3da..85283a30 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -22,14 +22,50 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Network diagnostics for deploy preview + run: | + echo "## 🌐 Network Diagnostics" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Testing connectivity to Netlify deploy preview..." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + DEPLOY_URL="https://deploy-preview-${{ github.event.pull_request.number }}--leafy-mooncake-7c2e5e.netlify.app" + + echo "### Deploy Preview URL" >> $GITHUB_STEP_SUMMARY + echo "\`$DEPLOY_URL\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Test DNS resolution + echo "### DNS Resolution" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + nslookup deploy-preview-${{ github.event.pull_request.number }}--leafy-mooncake-7c2e5e.netlify.app || echo "DNS lookup failed" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Test HTTP response time and headers + echo "### HTTP Response Test" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + time curl -I -L -s -w "\nHTTP Code: %{http_code}\nTime Total: %{time_total}s\nTime Connect: %{time_connect}s\nTime Start Transfer: %{time_starttransfer}s\n" "$DEPLOY_URL" || echo "HTTP request failed" + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Test navigation API endpoint + echo "### Navigation API Test" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + time curl -s -w "\nHTTP Code: %{http_code}\nTime Total: %{time_total}s\n" "$DEPLOY_URL/api/navigation" | head -20 + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + - name: Run Cypress E2E tests uses: cypress-io/github-action@v6 with: wait-on: 'https://deploy-preview-${{ github.event.pull_request.number }}--leafy-mooncake-7c2e5e.netlify.app' wait-on-timeout: 120 build: echo "Skipping build - testing against Netlify preview" + command-prefix: 'time' env: CYPRESS_baseUrl: https://deploy-preview-${{ github.event.pull_request.number }}--leafy-mooncake-7c2e5e.netlify.app + DEBUG: 'cypress:server:*' + ELECTRON_ENABLE_LOGGING: 1 - name: Upload Cypress screenshots on failure if: failure() From 946717720fa7ce704d75be3f3505aeb5f84f013c Mon Sep 17 00:00:00 2001 From: Bruno Amui Date: Sat, 8 Nov 2025 00:19:13 -0300 Subject: [PATCH 4/5] fix: remove invalid command-prefix from workflow --- .github/workflows/e2e-tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 85283a30..57104ca2 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -61,7 +61,6 @@ jobs: wait-on: 'https://deploy-preview-${{ github.event.pull_request.number }}--leafy-mooncake-7c2e5e.netlify.app' wait-on-timeout: 120 build: echo "Skipping build - testing against Netlify preview" - command-prefix: 'time' env: CYPRESS_baseUrl: https://deploy-preview-${{ github.event.pull_request.number }}--leafy-mooncake-7c2e5e.netlify.app DEBUG: 'cypress:server:*' From 107ad2fc7dd1023e4db632c8985978bb40301e48 Mon Sep 17 00:00:00 2001 From: Bruno Amui Date: Sat, 8 Nov 2025 12:45:40 -0300 Subject: [PATCH 5/5] feat: add retry logic and reduce timeouts for faster failures - Add 2 retries for failed tests in CI (runMode) - Reduce requestTimeout from 30s to 10s - Reduce responseTimeout from 30s to 10s - This will fail faster on network issues and retry intermittent failures --- cypress.config.cjs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cypress.config.cjs b/cypress.config.cjs index 43d64e66..f4e01042 100644 --- a/cypress.config.cjs +++ b/cypress.config.cjs @@ -18,5 +18,11 @@ module.exports = defineConfig({ baseUrl: 'http://localhost:3030', pageLoadTimeout: 30000, // Fail fast - max 30 seconds for page load defaultCommandTimeout: 10000, // 10 seconds for commands + requestTimeout: 10000, // 10 seconds for resource requests - fail faster on network issues + responseTimeout: 10000, // 10 seconds for response - fail faster + retries: { + runMode: 2, // Retry failed tests 2 times in CI + openMode: 0, // No retries in interactive mode + }, }, })