Cypress E2E Test Fixes - CI Failure Resolution¶
Latest Update: December 27, 2024
Previous Update: December 26, 2024
Commits: abfc63e (Dec 26), c77608f, 02b903f (Dec 27)
Issues Addressed:
- All tests in 04-positions-results.cy.js and 05-integration.cy.js failing (Dec 26)
- ResizeObserver errors and click timing issues (Dec 27)
Problem Analysis¶
The CI tests were failing because:
- Mock Web3 Provider Timing: Provider was injected AFTER visiting the page, causing the dapp to load without wallet detection
- Hidden Deployment Failures:
continue-on-error: truemasked contract deployment issues - Inconsistent Test Patterns: Different tests had varying approaches to provider setup
- Missing Health Checks: No verification that Hardhat node was responding
Root Cause¶
The primary issue was the sequence of operations in tests:
// ❌ WRONG - Provider injected after page load
beforeEach(() => {
cy.visit('/fairwins')
cy.mockWeb3Provider()
})
When the page loaded, it checked for window.ethereum but found nothing, leading to "no wallet" state. By the time the mock provider was injected, the app had already initialized.
Solution Implemented¶
1. Fixed Mock Provider Injection Sequence¶
Updated cypress/support/commands.js:
// ✅ CORRECT - Use 'window:before:load' event
Cypress.Commands.add('mockWeb3Provider', (options = {}) => {
// ...
cy.on('window:before:load', (win) => {
// Create mock ethereum provider
win.ethereum = { /* ... */ }
})
})
Key change: Using cy.on('window:before:load') ensures the provider exists BEFORE the app's JavaScript executes.
2. Updated All Test Files¶
Pattern applied to all tests:
// ✅ CORRECT - Provider before visit
describe('Test Suite', () => {
beforeEach(() => {
cy.mockWeb3Provider() // First: inject provider
cy.visit('/page') // Then: visit page
})
})
Files updated:
- 02-fairwins-trading.cy.js
- 03-clearpath-governance.cy.js
- 04-positions-results.cy.js
- 05-integration.cy.js
3. Improved connectWallet Command¶
Updated to be more resilient:
Cypress.Commands.add('connectWallet', () => {
// Check if provider already exists
cy.window().then((win) => {
if (!win.ethereum) {
cy.mockWeb3Provider()
}
})
// Then click connect button
cy.contains('button', /connect wallet/i, { timeout: 10000 }).click()
cy.waitForWalletConnection()
})
This allows connectWallet() to work even if mockWeb3Provider() wasn't explicitly called.
4. Enhanced CI Workflow¶
Updated .github/workflows/frontend-testing.yml:
- name: Start Hardhat node
run: |
npm run node > /tmp/hardhat-node.log 2>&1 &
echo "HARDHAT_PID=$!" >> $GITHUB_ENV
- name: Wait for Hardhat to start
run: |
echo "Waiting for Hardhat node to be ready..."
sleep 15
# Verify node is responding
curl -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://localhost:8545
- name: Deploy contracts to local network
run: |
echo "Deploying contracts to local Hardhat network..."
npm run deploy:local
echo "Contract deployment completed"
continue-on-error: false # ✅ Now catches deployment failures
Key improvements:
- Redirect Hardhat logs to file for debugging
- Add health check with curl to verify node is responding
- Remove continue-on-error: true to catch deployment failures
- Better logging for troubleshooting
5. Added Error Logging¶
Updated cypress/support/e2e.js:
Cypress.on('uncaught:exception', (err, runnable) => {
// Log errors for debugging
console.error('Uncaught exception:', err.message)
// Ignore known Web3 errors
if (err.message.includes('MetaMask') || /* ... */) {
return false
}
return true
})
This provides better visibility into what's failing.
Testing Strategy Updates¶
Before Fix¶
- Visit page
- Inject mock provider (too late)
- Try to connect wallet
- App doesn't see provider
- Tests fail
After Fix¶
- Inject mock provider
- Visit page (provider already exists)
- App detects provider on load
- Connect wallet works
- Tests pass
Expected Improvements¶
With these fixes, the tests should now:
✅ Properly initialize wallet state - App sees provider from first load
✅ Pass wallet connection tests - Connection flow works as expected
✅ Display wallet-dependent UI - Balance, positions, etc. become visible
✅ Catch deployment failures - CI won't proceed with broken contracts
✅ Provide better debugging - Logs help identify issues quickly
Verification¶
To verify these fixes work:
# Local testing
cd frontend
npm run dev &
npm run test:e2e
# Check specific failing tests
npx cypress run --spec "cypress/e2e/04-positions-results.cy.js"
npx cypress run --spec "cypress/e2e/05-integration.cy.js"
Additional Recommendations¶
For Future Test Development¶
- Always mock provider before visit: Make it a pattern in all new tests
- Use beforeEach wisely: Set up providers in beforeEach, not in individual tests
- Check window.ethereum: Add assertions to verify provider exists
- Test with contracts: Ensure local Hardhat node has deployed contracts
For Production Resilience¶
- Seed test data: Consider adding a seed script that populates contracts with test markets/proposals
- Mock contract responses: For pure UI tests, mock contract calls without needing deployment
- Separate test levels: Unit tests (no contracts), integration tests (with contracts), E2E tests (full stack)
Known Limitations¶
Even with these fixes, some tests may still show limitations:
- Empty State Tests: Tests that expect data need contracts with seeded data
- Real Blockchain Interaction: Mock provider doesn't execute real transactions
- Gas Estimation: Not tested with mock provider
- Contract Events: May not emit properly without real deployment
Next Steps¶
- ✅ Fixed provider injection sequence - Completed (Dec 26)
- ✅ Updated all test files - Completed (Dec 26)
- ✅ Enhanced CI workflow - Completed (Dec 26)
- ✅ Added ResizeObserver error handler - Completed (Dec 27)
- ✅ Improved click stability with force clicks - Completed (Dec 27)
- ✅ Increased wait times for DOM stability - Completed (Dec 27)
- ⏳ Monitor CI results - Waiting for next run
- ⏳ Consider adding seed script - Future improvement
- ⏳ Add more resilient assertions - Future improvement
Conclusion¶
The fixes address the root cause of test failures by ensuring proper initialization order. The mock Web3 provider is now available when the application loads, allowing it to detect a connected wallet from the start. Combined with better CI visibility and error handling, these changes should significantly improve test reliability.
Files Modified¶
December 26, 2024 Fixes¶
.github/workflows/frontend-testing.yml- CI workflow improvementsfrontend/cypress/support/commands.js- Provider injection timing fixfrontend/cypress/support/e2e.js- Error loggingfrontend/cypress/e2e/02-fairwins-trading.cy.js- Provider sequence fixfrontend/cypress/e2e/03-clearpath-governance.cy.js- Provider sequence fixfrontend/cypress/e2e/04-positions-results.cy.js- Provider sequence fixfrontend/cypress/e2e/05-integration.cy.js- Provider sequence fix
December 27, 2024 Fixes¶
frontend/cypress/support/e2e.js- Added ResizeObserver error handlerfrontend/cypress/support/commands.js- Enhanced connectWallet with force clicksfrontend/cypress/e2e/04-positions-results.cy.js- All 18 tests updated with stability improvementsfrontend/cypress/e2e/02-fairwins-trading.cy.js- Wallet connection stability fixfrontend/cypress/e2e/03-clearpath-governance.cy.js- Wallet connection stability fix
December 27, 2024 Update: DOM Stability and ResizeObserver Fixes¶
New Issues Identified¶
After the December 26 fixes, CI tests revealed additional stability issues:
- ResizeObserver Loop Errors: Browser was throwing "ResizeObserver loop completed with undelivered notifications" errors
- Click Timing Issues:
cy.click()was failing because DOM was updating during click execution - Test Flakiness: Race conditions in 04-positions-results.cy.js causing all 18 tests to fail intermittently
Root Causes¶
- Uncaught Browser Exceptions: ResizeObserver errors are harmless but Cypress treats all uncaught exceptions as fatal by default
- Async DOM Updates: React components were updating the DOM during Cypress click events, causing the "page updated while this command was executing" error
- Insufficient Wait Times: 1-2 second waits were not enough for all async operations to complete
Solutions Implemented¶
1. ResizeObserver Error Handler¶
Added to cypress/support/e2e.js:
Cypress.on('uncaught:exception', (err, runnable) => {
// Log errors for debugging
console.error('Uncaught exception:', err.message)
// Ignore ResizeObserver errors (harmless browser errors)
if (err.message.includes('ResizeObserver')) {
return false
}
// Ignore Web3 provider errors during testing
if (err.message.includes('MetaMask') || /* ... */) {
return false
}
return true
})
Impact: Prevents harmless browser ResizeObserver errors from failing tests while still catching real application errors.
2. Enhanced Click Stability¶
Updated cypress/support/commands.js connectWallet command:
Cypress.Commands.add('connectWallet', () => {
// If provider not already injected, inject it
cy.window().then((win) => {
if (!win.ethereum) {
cy.mockWeb3Provider()
}
})
// Then click the connect button with stability checks
cy.contains('button', /connect wallet/i, { timeout: 10000 })
.should('be.visible')
.should('not.be.disabled')
.click({ force: true }) // Force click to avoid DOM update conflicts
// Wait for connection to complete
cy.waitForWalletConnection()
})
Key changes:
- Added .should('be.visible') to wait for element visibility
- Added .should('not.be.disabled') to ensure button is enabled
- Added { force: true } to click command to bypass actionability checks and avoid DOM update conflicts
3. Improved Test Robustness in 04-positions-results.cy.js¶
Pattern applied to all 18 tests:
// ❌ BEFORE - Unreliable
it('should display user balance when connected', () => {
cy.contains('button', /connect wallet/i, { timeout: 10000 }).click()
cy.wait(1000)
// assertions...
})
// ✅ AFTER - Robust
it('should display user balance when connected', () => {
cy.contains('button', /connect wallet/i, { timeout: 10000 })
.should('be.visible')
.should('not.be.disabled')
.click({ force: true })
cy.wait(2000) // Increased wait time
// assertions...
})
Improvements:
- Explicit stability checks: Wait for visibility and enabled state before clicking
- Force clicks: Use { force: true } to handle DOM updates during click
- Increased wait times: Changed from 1-2 seconds to 2-3 seconds for wallet connections
- Consistent pattern: Applied same approach across all tests for predictability
4. Applied Fixes to Additional Test Files¶
Updated files:
- 02-fairwins-trading.cy.js - Applied stability fixes to wallet connection test
- 03-clearpath-governance.cy.js - Applied stability fixes to DAO information test
Testing Strategy Updates¶
Click Interaction Best Practices¶
When clicking elements that might update during interaction:
// 1. Wait for element to be stable
cy.get('button')
.should('be.visible')
.should('not.be.disabled')
// 2. Use force click if DOM is dynamic
.click({ force: true })
// 3. Wait for action to complete
cy.wait(1000)
Wait Time Guidelines¶
- Initial page load: 2000ms minimum
- Wallet connection: 2000-3000ms
- DOM updates after click: 1000ms
- Complex state changes: 3000ms
Expected Improvements¶
With these fixes, tests should now:
✅ Ignore harmless browser errors - ResizeObserver errors no longer fail tests
✅ Handle dynamic DOM updates - Force clicks prevent timing conflicts
✅ Have better stability - Increased wait times allow operations to complete
✅ Be more predictable - Consistent patterns across all tests
✅ Pass reliably in CI - Reduced flakiness from race conditions
Verification¶
The fixes address three critical failure patterns:
- ResizeObserver errors → Now ignored, won't fail tests
- Click timing errors → Force clicks and stability checks prevent conflicts
- Race conditions → Increased wait times ensure operations complete
All 18 tests in 04-positions-results.cy.js now follow the same robust pattern, making them more reliable and maintainable.