-
Notifications
You must be signed in to change notification settings - Fork 3
E2e tests #189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
E2e tests #189
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
658ddac
feat: add E2E test infrastructure with Cypress
nomadbitcoin 771f70d
smoke tests
nomadbitcoin 78ed951
fix: correct baseUrl for E2E tests in CI environment
nomadbitcoin 2da0645
e2e connectWallet command and test
nomadbitcoin c054f76
fix character lenght
nomadbitcoin e28d665
excessive comments removed
nomadbitcoin a98ca17
helper function to generate testIds and keep code clean
nomadbitcoin 2b1ea96
reduced waits and video removed from cypress
nomadbitcoin 27820e7
Remove comments
laurogripa File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| name: E2E Test Suite | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: | ||
| - main | ||
| - test-setup | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| e2e-tests: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 20 | ||
|
|
||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '20' | ||
|
|
||
| - name: Enable Corepack | ||
| run: corepack enable | ||
|
|
||
| - name: Prepare Yarn 4.1.1 | ||
| run: corepack prepare yarn@4.1.1 --activate | ||
|
|
||
| - name: Install dependencies | ||
| run: yarn install --immutable | ||
|
|
||
| - name: Verify Cypress installation | ||
| run: npx cypress verify | ||
|
|
||
| - name: Setup Chopsticks config | ||
| run: | | ||
| cat > config/kusama.yml << 'EOF' | ||
| endpoint: wss://rpc.ibp.network/kusama | ||
| mock-signature-host: true | ||
| db: ./db.sqlite | ||
| runtime-log-level: 0 | ||
| EOF | ||
|
|
||
| - name: Start Chopsticks (background) | ||
| run: | | ||
| echo "Starting Chopsticks blockchain fork..." | ||
| nohup yarn chopsticks > chopsticks.log 2>&1 & | ||
| CHOPSTICKS_PID=$! | ||
| echo "CHOPSTICKS_PID=$CHOPSTICKS_PID" >> $GITHUB_ENV | ||
| echo "Chopsticks started with PID: $CHOPSTICKS_PID" | ||
| sleep 5 | ||
| echo "Chopsticks output:" | ||
| cat chopsticks.log || true | ||
|
|
||
| - name: Wait for Chopsticks to be ready | ||
| run: | | ||
| echo "Waiting for Chopsticks to respond on http://localhost:8000..." | ||
| timeout 120 bash -c 'until curl -sf http://localhost:8000 > /dev/null 2>&1; do sleep 2; done' || { | ||
| echo "Chopsticks did not start within 120 seconds" | ||
| echo "Chopsticks log:" | ||
| cat chopsticks.log || true | ||
| exit 1 | ||
| } | ||
| echo "✓ Chopsticks is running and responding" | ||
|
|
||
| - name: Start dev server (background) | ||
| run: | | ||
| echo "Starting React dev server..." | ||
| nohup yarn start > dev-server.log 2>&1 & | ||
| DEV_SERVER_PID=$! | ||
| echo "DEV_SERVER_PID=$DEV_SERVER_PID" >> $GITHUB_ENV | ||
| echo "Dev server started with PID: $DEV_SERVER_PID" | ||
| env: | ||
| NODE_OPTIONS: --openssl-legacy-provider | ||
| CI: true | ||
|
|
||
| - name: Wait for dev server to be ready | ||
| run: | | ||
| echo "Waiting for dev server to respond on http://localhost:3000..." | ||
| timeout 120 bash -c 'until curl -sf http://localhost:3000 > /dev/null 2>&1; do sleep 3; done' || { | ||
| echo "Dev server did not start within 120 seconds" | ||
| exit 1 | ||
| } | ||
| echo "✓ Dev server is running and responding" | ||
|
|
||
| - name: Run Cypress E2E tests | ||
| run: | | ||
| echo "Running E2E test suite..." | ||
| npx cypress run --config video=true --reporter json --reporter-options "output=cypress/results/results.json" | ||
|
|
||
| - name: Upload test artifacts on failure | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: cypress-artifacts | ||
| path: | | ||
| cypress/screenshots/ | ||
| cypress/videos/ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can remove videos and add the chopsticks logs to the artifacts |
||
| cypress/results/ | ||
| retention-days: 7 | ||
|
|
||
| - name: Upload test results | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: test-results | ||
| path: cypress/results/ | ||
| retention-days: 30 | ||
|
|
||
| - name: Generate test summary | ||
| if: always() | ||
| run: | | ||
| echo "## E2E Test Results" >> $GITHUB_STEP_SUMMARY | ||
| if [ -f cypress/results/results.json ]; then | ||
| echo "✓ Tests completed - see artifacts for details" >> $GITHUB_STEP_SUMMARY | ||
| else | ||
| echo "⚠ No test results generated" >> $GITHUB_STEP_SUMMARY | ||
| fi | ||
|
|
||
| - name: Cleanup | ||
| if: always() | ||
| run: | | ||
| echo "Cleaning up background processes..." | ||
| if [ ! -z "$CHOPSTICKS_PID" ]; then | ||
| kill $CHOPSTICKS_PID 2>/dev/null || true | ||
| fi | ||
| if [ ! -z "$DEV_SERVER_PID" ]; then | ||
| kill $DEV_SERVER_PID 2>/dev/null || true | ||
| fi | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { defineConfig } from 'cypress'; | ||
|
|
||
| export default defineConfig({ | ||
| e2e: { | ||
| baseUrl: 'http://localhost:3000', | ||
| setupNodeEvents(on, config) { | ||
| return config; | ||
| }, | ||
| specPattern: 'cypress/e2e/*.cy.{js,jsx,ts,tsx}', | ||
| supportFile: 'cypress/support/e2e.ts', | ||
|
|
||
| viewportWidth: 1280, | ||
| viewportHeight: 720, | ||
|
|
||
| video: false, | ||
| screenshotOnRunFailure: true, | ||
| screenshotsFolder: 'cypress/screenshots', | ||
|
|
||
| retries: { | ||
| runMode: 2, | ||
| openMode: 0, | ||
| }, | ||
|
|
||
| defaultCommandTimeout: 10000, | ||
| requestTimeout: 15000, | ||
| responseTimeout: 15000, | ||
|
|
||
| env: { | ||
| chopsticks_url: 'ws://localhost:8000', | ||
| }, | ||
| }, | ||
| }); |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,227 @@ | ||
| describe('Primary Routes Smoke Tests', () => { | ||
| const primaryRoutes = [ | ||
| { path: '/', name: 'Landing Page', hasCustomNav: true }, | ||
| { path: '/welcome', name: 'Welcome Page', hasCustomNav: false }, | ||
| { path: '/journey', name: 'Journey Page', hasCustomNav: false }, | ||
| { path: '/guide', name: 'Cyborg Guide Page', hasCustomNav: false }, | ||
| { path: '/wiki', name: 'Wiki Page', hasCustomNav: false }, | ||
| { path: '/gilbertogil', name: 'Gilberto Gil Page', hasCustomNav: false }, | ||
| { path: '/futurivel', name: 'Futurivel Page', hasCustomNav: false } | ||
| ] | ||
|
|
||
| primaryRoutes.forEach(({ path, name, hasCustomNav }) => { | ||
| it(`should load ${name} (${path})`, () => { | ||
| cy.visit(path, { timeout: 5000 }) | ||
|
|
||
| cy.get('body').should('be.visible') | ||
|
|
||
| if (!hasCustomNav) { | ||
| cy.get('nav').should('exist') | ||
| } | ||
|
|
||
| cy.window().then((win) => { | ||
| expect(win.document.readyState).to.equal('complete') | ||
| }) | ||
| }) | ||
| }) | ||
|
|
||
| it('should render all primary routes without crashes', () => { | ||
| cy.visit('/') | ||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.visit('/welcome') | ||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.visit('/journey') | ||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.visit('/guide') | ||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.visit('/wiki') | ||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.visit('/gilbertogil') | ||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.visit('/futurivel') | ||
| cy.get('body').should('be.visible') | ||
| }) | ||
| }) | ||
|
|
||
| describe('Explore Routes Smoke Tests', () => { | ||
| const exploreRoutes = [ | ||
| { path: '/explore/bidders', name: 'Bidders' }, | ||
| { path: '/explore/candidates', name: 'Candidates' }, | ||
| { path: '/explore/members', name: 'Members' }, | ||
| { path: '/explore/payouts', name: 'Payouts' }, | ||
| { path: '/explore/suspended', name: 'Suspended' } | ||
| ] | ||
|
|
||
| beforeEach(() => { | ||
| cy.visit('/') | ||
| }) | ||
|
|
||
| exploreRoutes.forEach(({ path, name }) => { | ||
| it(`should load ${name} page (${path})`, () => { | ||
| cy.visit(`${path}?rpc=ws://localhost:8000`) | ||
|
|
||
| cy.get('body').should('be.visible') | ||
| cy.get('nav').should('exist') | ||
| cy.url().should('include', path) | ||
| }) | ||
| }) | ||
|
|
||
| it('should redirect /explore to /explore/bidders', () => { | ||
| cy.visit('/explore?rpc=ws://localhost:8000') | ||
|
|
||
| cy.url().should('include', '/explore/bidders') | ||
| cy.get('body').should('be.visible') | ||
| }) | ||
|
|
||
| it('should navigate between explore sub-routes', () => { | ||
| cy.visit('/explore/bidders?rpc=ws://localhost:8000') | ||
| cy.url().should('include', '/explore/bidders') | ||
|
|
||
| cy.visit('/explore/members?rpc=ws://localhost:8000') | ||
| cy.url().should('include', '/explore/members') | ||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.visit('/explore/candidates?rpc=ws://localhost:8000') | ||
| cy.url().should('include', '/explore/candidates') | ||
| cy.get('body').should('be.visible') | ||
| }) | ||
|
|
||
| it('should maintain query parameter during navigation', () => { | ||
| cy.visit('/explore/bidders?rpc=ws://localhost:8000') | ||
|
|
||
| cy.url().should('include', 'rpc=ws://localhost:8000') | ||
| }) | ||
| }) | ||
|
|
||
| describe('Proof of Ink Routes Smoke Tests', () => { | ||
| const poiRoutes = [ | ||
| { path: '/explore/poi/examples', name: 'POI Examples' }, | ||
| { path: '/explore/poi/rules', name: 'POI Rules' }, | ||
| { path: '/explore/poi/gallery', name: 'POI Gallery' }, | ||
| { path: '/explore/poi/next-head', name: 'POI Next Head' } | ||
| ] | ||
|
|
||
| beforeEach(() => { | ||
| cy.visit('/?rpc=ws://localhost:8000') | ||
| }) | ||
|
|
||
| poiRoutes.forEach(({ path, name }) => { | ||
| it(`should load ${name} page (${path})`, () => { | ||
| cy.visit(`${path}?rpc=ws://localhost:8000`) | ||
|
|
||
| cy.get('body').should('be.visible') | ||
| cy.get('nav').should('exist') | ||
| cy.url().should('include', path) | ||
| }) | ||
| }) | ||
|
|
||
| it('should redirect /explore/poi to /explore/poi/examples', () => { | ||
| cy.visit('/explore/poi?rpc=ws://localhost:8000') | ||
|
|
||
| cy.url().should('include', '/explore/poi/examples') | ||
| cy.get('body').should('be.visible') | ||
| }) | ||
|
|
||
| it('should navigate between POI sub-routes', () => { | ||
| cy.visit('/explore/poi/examples?rpc=ws://localhost:8000') | ||
| cy.url().should('include', '/explore/poi/examples') | ||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.visit('/explore/poi/rules?rpc=ws://localhost:8000') | ||
| cy.url().should('include', '/explore/poi/rules') | ||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.visit('/explore/poi/gallery?rpc=ws://localhost:8000') | ||
| cy.url().should('include', '/explore/poi/gallery') | ||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.visit('/explore/poi/next-head?rpc=ws://localhost:8000') | ||
| cy.url().should('include', '/explore/poi/next-head') | ||
| cy.get('body').should('be.visible') | ||
| }) | ||
|
|
||
| it('should maintain query parameter in POI routes', () => { | ||
| cy.visit('/explore/poi/examples?rpc=ws://localhost:8000') | ||
|
|
||
| cy.url().should('include', 'rpc=ws://localhost:8000') | ||
|
|
||
| cy.visit('/explore/poi/gallery?rpc=ws://localhost:8000') | ||
| cy.url().should('include', 'rpc=ws://localhost:8000') | ||
| }) | ||
| }) | ||
|
|
||
| describe('API Connection Smoke Tests', () => { | ||
| it('should connect to Chopsticks successfully', () => { | ||
| cy.visit('/explore/members?rpc=ws://localhost:8000') | ||
|
|
||
| cy.get('body').should('be.visible') | ||
|
|
||
| cy.get('body').then(($body) => { | ||
| const bodyText = $body.text() | ||
|
|
||
| expect(bodyText.length).to.be.greaterThan(10) | ||
| expect(bodyText).to.not.equal('Loading...') | ||
| }) | ||
|
|
||
| cy.get('body').should('not.contain', 'Failed to connect') | ||
| cy.get('body').should('not.contain', 'API error') | ||
| }) | ||
|
|
||
| it('should display chain information', () => { | ||
| cy.visit('/explore/members?rpc=ws://localhost:8000') | ||
|
|
||
| cy.get('body').should('be.visible') | ||
| cy.get('nav').should('exist') | ||
| cy.url().should('include', 'explore') | ||
| }) | ||
|
|
||
| it('should initialize API without crashing', () => { | ||
| cy.visit('/explore/members?rpc=ws://localhost:8000') | ||
|
|
||
| cy.wait(5000) | ||
|
|
||
| cy.get('body').should('be.visible') | ||
| cy.get('nav').should('exist') | ||
| }) | ||
| }) | ||
|
|
||
| describe('Query Parameter Preservation Smoke Tests', () => { | ||
| const testRpc = 'ws://localhost:8000' | ||
|
|
||
| it('should preserve ?rpc parameter on initial load', () => { | ||
| cy.visit(`/?rpc=${testRpc}`) | ||
|
|
||
| cy.url().should('include', `rpc=${testRpc}`) | ||
| cy.get('body').should('be.visible') | ||
| }) | ||
|
|
||
| it('should preserve ?rpc parameter through redirects', () => { | ||
| cy.visit(`/explore?rpc=${testRpc}`) | ||
|
|
||
| cy.url().should('include', '/explore/bidders') | ||
| cy.url().should('include', `rpc=${testRpc}`) | ||
|
|
||
| cy.visit(`/explore/poi?rpc=${testRpc}`) | ||
|
|
||
| cy.url().should('include', '/explore/poi/examples') | ||
| cy.url().should('include', `rpc=${testRpc}`) | ||
| }) | ||
|
|
||
| it('should preserve params in deeply nested routes', () => { | ||
| cy.visit(`/explore/poi/gallery?rpc=${testRpc}`) | ||
|
|
||
| cy.url().should('include', '/explore/poi/gallery') | ||
| cy.url().should('include', `rpc=${testRpc}`) | ||
|
|
||
| cy.visit(`/explore/poi/rules?rpc=${testRpc}`) | ||
|
|
||
| cy.url().should('include', '/explore/poi/rules') | ||
| cy.url().should('include', `rpc=${testRpc}`) | ||
| }) | ||
| }) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious: why such a long sleep here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of failures due to timeout, even with only "sleep 3."