[recovery] Ignore removeChild NotFoundError from DOM extensions #1586
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
| name: CI | |
| on: | |
| pull_request: | |
| branches: [dev, master, staging, "test/**"] | |
| types: [opened, synchronize, ready_for_review] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| # Shared base URL for the production server we boot for e2e + lighthouse. | |
| LOCAL_BASE_URL: http://localhost:3000 | |
| # Extended jobs (build / e2e / lighthouse / page-visual) only run for PRs into | |
| # master, staging, or test/** branches. Jobs downstream of `build` inherit | |
| # gating automatically via `needs:` (skipped jobs propagate). | |
| # | |
| # Exception: Storybook Chromatic (`visual-tests`) runs on every non-draft PR. | |
| # TurboSnap (`onlyChanged`) keeps snapshot usage low: a PR with no affected | |
| # stories is bypassed and billed nothing, and no build is published for it. | |
| # The merge gate for visual changes is Chromatic's own "UI Tests" check (posted | |
| # by the Chromatic GitHub App), which updates live as a maintainer | |
| # accepts/rejects -- so this job itself always exits 0. | |
| # | |
| # `fetch-depth: 0` is set selectively: lint needs it for `git diff`, and the | |
| # two Chromatic jobs need it for baseline detection. Other jobs use the default | |
| # shallow clone for speed. | |
| jobs: | |
| lint: | |
| name: Lint, type-check & markdown | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - uses: ./.github/actions/setup-pnpm-node | |
| - name: ESLint | |
| run: pnpm lint | |
| - name: TypeScript | |
| run: pnpm type-check | |
| - name: Find changed English markdown | |
| id: changed | |
| run: | | |
| FILES=$(git diff --name-only --diff-filter=ACMR origin/${{ github.base_ref }}...HEAD \ | |
| | grep '^public/content/.*\.md$' \ | |
| | grep -v '^public/content/translations/' \ | |
| || true) | |
| { | |
| echo "files<<EOF" | |
| echo "$FILES" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Lint changed markdown | |
| if: steps.changed.outputs.files != '' | |
| uses: DavidAnson/markdownlint-cli2-action@v22 | |
| with: | |
| globs: ${{ steps.changed.outputs.files }} | |
| unit-tests: | |
| name: Unit tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup-pnpm-node | |
| - name: Run unit tests | |
| run: pnpm test:unit | |
| visual-tests: | |
| name: Visual regression (Chromatic) | |
| # Runs on every non-draft PR. TurboSnap (`onlyChanged`) means PRs with no | |
| # affected stories are bypassed: nothing is snapshotted, billed, or | |
| # published. The required merge gate is the Chromatic App's "UI Tests" | |
| # check, so the job itself always exits 0. | |
| if: github.event.pull_request.draft == false | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - uses: ./.github/actions/setup-pnpm-node | |
| - name: Publish to Chromatic | |
| uses: chromaui/action@v18.5.0 | |
| with: | |
| projectToken: fee8e66c9916 | |
| # Not named chromatic.config.json: that filename is auto-loaded by | |
| # both projects, and untraced without onlyChanged fails the pages job. | |
| configFile: chromatic.storybook.json | |
| # Emergency stop: set repo variable CHROMATIC_SKIP to "**". Snapshots | |
| # stop but the UI Tests check still passes, so PRs stay mergeable. | |
| # The value is a branch glob, hence a default no branch can match. | |
| skip: ${{ vars.CHROMATIC_SKIP || '__chromatic_skip_disabled__' }} | |
| exitZeroOnChanges: true | |
| onlyChanged: true | |
| # Defaults to on whenever onlyChanged is set; keep it off so debug | |
| # logs aren't published next to the public Storybook. | |
| uploadMetadata: false | |
| # Release-train builds (dev/staging/master head branches) were | |
| # already reviewed per-PR on dev; auto-accept them as baselines. | |
| autoAcceptChanges: "{dev,staging,master}" | |
| zip: true | |
| build: | |
| name: Build website (mock data) | |
| if: | | |
| github.base_ref == 'master' | |
| || github.base_ref == 'staging' | |
| || startsWith(github.base_ref, 'test/') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup-pnpm-node | |
| - name: Build (mock data, deterministic, e2e locale subset) | |
| run: pnpm build | |
| env: | |
| USE_MOCK_DATA: "true" | |
| IS_VISUAL_TEST: "true" | |
| # E2E specs reference en/es/zh/ar (404 i18n + language picker + RTL). | |
| # Page-visual + lighthouse only need en. Build all four once. | |
| NEXT_PUBLIC_BUILD_LOCALES: "en,es,zh,ar" | |
| - name: Upload build artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: next-build | |
| path: | | |
| .next | |
| !.next/cache | |
| retention-days: 1 | |
| include-hidden-files: true | |
| e2e-tests: | |
| name: E2E tests | |
| if: | | |
| github.base_ref == 'master' | |
| || github.base_ref == 'staging' | |
| || startsWith(github.base_ref, 'test/') | |
| runs-on: ubuntu-latest | |
| env: | |
| DEPLOY_PREVIEW_URL: https://deploy-preview-${{ github.event.pull_request.number }}--ethereumorg.netlify.app | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup-pnpm-node | |
| - name: Install Playwright with browser deps | |
| run: npx playwright install --with-deps | |
| - name: Wait for Netlify deploy preview to be ready | |
| id: netlify_wait | |
| uses: ./.github/actions/wait-for-pr-checks | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| pr_number: ${{ github.event.pull_request.number }} | |
| check_name: netlify/ethereumorg/deploy-preview | |
| - name: Fail if Netlify preview is not ready | |
| if: steps.netlify_wait.outputs.outcome != 'success' | |
| run: | | |
| echo "::error::Netlify deploy preview outcome: ${{ steps.netlify_wait.outputs.outcome }}" | |
| exit 1 | |
| - name: Warm up Netlify edge cache | |
| run: | | |
| for path in / /wallets/find-wallet/ /staking/ /whitepaper/ /nft/ /developers/ /start/; do | |
| curl -s -o /dev/null "${DEPLOY_PREVIEW_URL}${path}" & | |
| done | |
| wait | |
| - name: Run E2E tests | |
| run: pnpm test:e2e | |
| env: | |
| PLAYWRIGHT_TEST_BASE_URL: ${{ env.DEPLOY_PREVIEW_URL }} | |
| - name: Upload Playwright report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-report | |
| path: ./tests/__results__ | |
| retention-days: 7 | |
| lighthouse: | |
| name: Lighthouse audit | |
| needs: build | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup-pnpm-node | |
| - name: Download build artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: next-build | |
| path: .next | |
| - name: Start production server | |
| run: pnpm start & | |
| - uses: ./.github/actions/wait-for-server | |
| with: | |
| url: ${{ env.LOCAL_BASE_URL }} | |
| - name: Audit URLs using Lighthouse | |
| id: lighthouse_audit | |
| uses: treosh/lighthouse-ci-action@v11 | |
| with: | |
| urls: | | |
| ${{ env.LOCAL_BASE_URL }}/en/ | |
| ${{ env.LOCAL_BASE_URL }}/en/wallets/find-wallet/ | |
| ${{ env.LOCAL_BASE_URL }}/en/staking/ | |
| ${{ env.LOCAL_BASE_URL }}/en/whitepaper/ | |
| ${{ env.LOCAL_BASE_URL }}/en/nft/ | |
| ${{ env.LOCAL_BASE_URL }}/en/developers/docs/intro-to-ethereum/ | |
| ${{ env.LOCAL_BASE_URL }}/en/developers/tutorials/creating-a-wagmi-ui-for-your-contract/ | |
| runs: 3 | |
| uploadArtifacts: true | |
| temporaryPublicStorage: true | |
| - name: Format Lighthouse score | |
| id: format_lighthouse_score | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const manifests = ${{ steps.lighthouse_audit.outputs.manifest }}; | |
| const links = ${{ steps.lighthouse_audit.outputs.links }}; | |
| const formatResult = (res) => Math.round((res * 100)); | |
| console.log('Total manifests:', manifests.length); | |
| console.log('Manifests:', JSON.stringify(manifests, null, 2)); | |
| console.log('Links:', JSON.stringify(links, null, 2)); | |
| let comment = [ | |
| '| Page | Performance | Accessibility | Best practices | SEO | PWA |', | |
| '| --- | --- | --- | --- | --- | --- |', | |
| ]; | |
| Object.entries(links).forEach(([pageUrl, reportUrl]) => { | |
| const relevantManifests = manifests.filter(manifest => manifest.url === pageUrl); | |
| const results = relevantManifests.map(manifest => manifest.summary); | |
| const averagedResults = {}; | |
| if (results.length > 0) { | |
| Object.keys(results[0]).forEach(key => { | |
| averagedResults[key] = formatResult( | |
| results.reduce((acc, cur) => acc + cur[key], 0) / results.length | |
| ); | |
| }); | |
| const score = res => res >= 90 ? '🟢' : res >= 50 ? '🟠' : '🔴'; | |
| const urlForTable = pageUrl.includes('/en/') ? pageUrl.substring(pageUrl.indexOf('/en/')) : pageUrl; | |
| comment.push( | |
| `| [${urlForTable}](${reportUrl}) | ${score(averagedResults.performance)} ${averagedResults.performance} | ${score(averagedResults.accessibility)} ${averagedResults.accessibility} | ${score(averagedResults['best-practices'])} ${averagedResults['best-practices']} | ${score(averagedResults.seo)} ${averagedResults.seo} | ${score(averagedResults.pwa)} ${averagedResults.pwa} |` | |
| ); | |
| } else { | |
| console.error('No results found for URL:', pageUrl); | |
| } | |
| }); | |
| comment.push( | |
| ' ', | |
| '*Lighthouse scores are calculated based on the latest audit results*' | |
| ); | |
| comment = comment.join('\n'); | |
| core.setOutput("comment", comment); | |
| - name: Add Lighthouse stats as comment | |
| uses: marocchino/sticky-pull-request-comment@v2 | |
| with: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| number: ${{ github.event.pull_request.number }} | |
| header: lighthouse | |
| message: ${{ steps.format_lighthouse_score.outputs.comment }} | |
| page-visual-tests: | |
| name: Page visual snapshots (Playwright + Chromatic) | |
| needs: build | |
| runs-on: ubuntu-latest | |
| container: | |
| image: mcr.microsoft.com/playwright:v1.53.1-noble | |
| env: | |
| # Playwright image preinstalls browsers under /root; GitHub Actions | |
| # otherwise overrides HOME to /github/home and Playwright re-downloads. | |
| HOME: /root | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Mark workspace as safe for git | |
| run: git config --global --add safe.directory "$GITHUB_WORKSPACE" | |
| - uses: ./.github/actions/setup-pnpm-node | |
| - name: Download build artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: next-build | |
| path: .next | |
| - name: Run visual tests | |
| run: pnpm test:visual | |
| - name: Publish to Chromatic | |
| if: always() | |
| uses: chromaui/action@v18.5.0 | |
| with: | |
| projectToken: ${{ secrets.CHROMATIC_PAGES_TOKEN }} | |
| playwright: true | |
| exitZeroOnChanges: true | |
| zip: true |