diff --git a/.github/workflows/endform-experiment.yml b/.github/workflows/endform-experiment.yml new file mode 100644 index 000000000000..2e04c3d0ecc5 --- /dev/null +++ b/.github/workflows/endform-experiment.yml @@ -0,0 +1,140 @@ +name: 'Endform: Incremental experiment' + +on: + push: + branches: [augustoccesar/endform-integration] + +permissions: + contents: read + +jobs: + baseline: + if: github.repository == 'endformdev/n8n' + name: '${{ matrix.runner }}: incremental suite' + runs-on: ubuntu-latest + timeout-minutes: 45 + strategy: + fail-fast: false + max-parallel: 1 + matrix: + runner: [playwright, endform] + env: + NODE_OPTIONS: '--max-old-space-size=6144' + N8N_BASE_URL: http://localhost:5680 + RESET_E2E_DB: 'true' + N8N_SKIP_LICENSES: 'true' + PLAYWRIGHT_BROWSERS_PATH: '${{ github.workspace }}/packages/testing/playwright/.playwright-browsers' + steps: + - name: Checkout + uses: useblacksmith/checkout@bcec731f1eb1367240608d1c889a75b96db6ec53 # v1.5.0 + + - name: Install dependencies + uses: ./.github/actions/setup-nodejs + with: + build-command: '' + + - name: Build + run: pnpm build > build.log 2>&1 + + - name: Record versions + working-directory: packages/testing/playwright + run: | + git rev-parse HEAD + node --version + pnpm --version + pnpm exec playwright --version + pnpm exec endform --version + + - name: Check types and lint + working-directory: packages/testing/playwright + run: | + pnpm typecheck > typecheck.log 2>&1 + pnpm lint > lint.log 2>&1 + + - name: Install Chromium + if: matrix.runner == 'playwright' + working-directory: packages/testing/playwright + run: pnpm exec playwright install chromium --with-deps + + - name: Install resource monitor + run: | + sudo apt-get update -qq + sudo apt-get install -y sysstat procps iproute2 + + - name: Run incremental suite + working-directory: packages/testing/playwright + env: + TEST_RUNNER: '${{ matrix.runner }}' + E2E_NAVIGATION_TIMEOUT: '30000' + E2E_REMOTE_RUNNER: "${{ matrix.runner == 'endform' && 'true' || 'false' }}" + ENDFORM_API_KEY: "${{ matrix.runner == 'endform' && secrets.ENDFORM_API_KEY || '' }}" + run: | + export TZ=UTC + { + echo "Logical CPUs: $(nproc)" + free -m + echo 'The first vmstat sample contains averages since boot.' + } > host-resources.log + vmstat -w -t 5 >> host-resources.log & + host_monitor_pid=$! + pidstat -h -u -r -d -p ALL 5 > process-resources.log & + process_monitor_pid=$! + ( + while true; do + date -u '+%Y-%m-%dT%H:%M:%SZ' + ss -ltnp 'sport = :5680' + sleep 5 + done + ) > backend-listener.log & + listener_monitor_pid=$! + stop_monitors() { + kill "$host_monitor_pid" "$process_monitor_pid" "$listener_monitor_pid" 2>/dev/null || true + wait "$host_monitor_pid" "$process_monitor_pid" "$listener_monitor_pid" 2>/dev/null || true + } + trap stop_monitors EXIT + + args=( + test --project=e2e + tests/e2e/building-blocks/workflow-entry-points.spec.ts + tests/e2e/credentials/crud.spec.ts + tests/e2e/building-blocks/canvas-actions.spec.ts + tests/e2e/building-blocks/node-details-configuration.spec.ts + tests/e2e/workflows/editor/execution/execution.spec.ts + tests/e2e/building-blocks/user-service.spec.ts + --retries=0 + ) + if [ "$TEST_RUNNER" = endform ]; then + args+=(--organization-id=2G1ZCj7X) + else + args+=(--workers=4) + fi + pnpm exec "$TEST_RUNNER" "${args[@]}" > experiment.log 2>&1 + + - name: Show log tails + if: always() + run: | + for log in build.log packages/testing/playwright/{typecheck,lint,experiment}.log; do + if [ -f "$log" ]; then + echo "::group::$log" + tail -n 50 "$log" + echo '::endgroup::' + fi + done + + - name: Preserve evidence + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: 'endform-experiment-${{ matrix.runner }}-${{ github.run_attempt }}' + retention-days: 30 + path: | + build.log + packages/testing/playwright/experiment.log + packages/testing/playwright/typecheck.log + packages/testing/playwright/lint.log + packages/testing/playwright/host-resources.log + packages/testing/playwright/process-resources.log + packages/testing/playwright/backend-listener.log + packages/testing/playwright/test-results/ + packages/testing/playwright/test-results.json + packages/testing/playwright/playwright-report/ diff --git a/packages/testing/playwright/config/intercepts.ts b/packages/testing/playwright/config/intercepts.ts index c67979285c08..50ec5fb5090d 100644 --- a/packages/testing/playwright/config/intercepts.ts +++ b/packages/testing/playwright/config/intercepts.ts @@ -116,8 +116,9 @@ export async function setupDefaultInterceptors(target: BrowserContext) { }); // Pathname /api/health + // Use URL patterns so Playwright does not intercept unrelated asset requests. await target.route( - (url) => url.pathname.endsWith('/api/health'), + /^https?:\/\/[^/?#]+\/(?:[^?#]*\/)?api\/health(?:[?#].*)?$/, async (route: Route) => { await route.fulfill({ contentType: 'application/json', @@ -127,37 +128,34 @@ export async function setupDefaultInterceptors(target: BrowserContext) { ); // Pathname /api/versions/* - await target.route( - (url) => url.pathname.startsWith('/api/versions/'), - async (route: Route) => { - await route.fulfill({ - contentType: 'application/json', - body: JSON.stringify([ - { - name: '1.45.1', - createdAt: '2023-08-18T11:53:12.857Z', - hasSecurityIssue: null, - hasSecurityFix: null, - securityIssueFixVersion: null, - hasBreakingChange: null, - documentationUrl: 'https://docs.n8n.io/release-notes/#n8n131', - nodes: [], - description: 'Includes bug fixes', - }, - { - name: '1.0.5', - createdAt: '2023-07-24T10:54:56.097Z', - hasSecurityIssue: false, - hasSecurityFix: null, - securityIssueFixVersion: null, - hasBreakingChange: true, - documentationUrl: 'https://docs.n8n.io/release-notes/#n8n104', - nodes: [], - description: - 'Includes core functionality and bug fixes', - }, - ]), - }); - }, - ); + await target.route(/^https?:\/\/[^/?#]+\/api\/versions\//, async (route: Route) => { + await route.fulfill({ + contentType: 'application/json', + body: JSON.stringify([ + { + name: '1.45.1', + createdAt: '2023-08-18T11:53:12.857Z', + hasSecurityIssue: null, + hasSecurityFix: null, + securityIssueFixVersion: null, + hasBreakingChange: null, + documentationUrl: 'https://docs.n8n.io/release-notes/#n8n131', + nodes: [], + description: 'Includes bug fixes', + }, + { + name: '1.0.5', + createdAt: '2023-07-24T10:54:56.097Z', + hasSecurityIssue: false, + hasSecurityFix: null, + securityIssueFixVersion: null, + hasBreakingChange: true, + documentationUrl: 'https://docs.n8n.io/release-notes/#n8n104', + nodes: [], + description: + 'Includes core functionality and bug fixes', + }, + ]), + }); + }); } diff --git a/packages/testing/playwright/endform.config.ts b/packages/testing/playwright/endform.config.ts new file mode 100644 index 000000000000..5a8b07c14d23 --- /dev/null +++ b/packages/testing/playwright/endform.config.ts @@ -0,0 +1,8 @@ +/* eslint-disable import-x/no-default-export */ +import { defineEndformConfig } from 'endform'; + +export default defineEndformConfig({ + additionalFiles: ['workflows/**/*'], + concurrentTestLimits: [{ scope: 'within-suite-run', limit: 4 }], + environmentVariables: ['N8N_BASE_URL', 'RESET_E2E_DB', 'E2E_REMOTE_RUNNER'], +}); diff --git a/packages/testing/playwright/package.json b/packages/testing/playwright/package.json index bf4d533742c6..f52f30b15ea1 100644 --- a/packages/testing/playwright/package.json +++ b/packages/testing/playwright/package.json @@ -75,6 +75,7 @@ "autocannon": "^8.0.0", "axe-core": "^4.13.0", "axe-html-reporter": "^2.2.11", + "endform": "catalog:", "eslint-plugin-playwright": "catalog:e2e", "flatted": "catalog:", "generate-schema": "2.6.0", diff --git a/packages/testing/playwright/playwright.config.ts b/packages/testing/playwright/playwright.config.ts index 66f1395cedd2..6cdb881a650b 100644 --- a/packages/testing/playwright/playwright.config.ts +++ b/packages/testing/playwright/playwright.config.ts @@ -70,7 +70,7 @@ if (BACKEND_URL && !SKIP_WEB_SERVER) { } webServer.push({ command: 'pnpm --dir ../../.. start', - url: `${BACKEND_URL}/favicon.ico`, + url: `${BACKEND_URL}/healthz/readiness`, timeout: 30000, reuseExistingServer: IS_DEV ? false : true, env: { @@ -112,7 +112,7 @@ export default defineConfig({ forbidOnly: IS_CI, retries: IS_CI ? 2 : 0, workers: WORKERS, - timeout: 60000, + timeout: process.env.E2E_REMOTE_RUNNER === 'true' ? 90000 : 60000, expect: { timeout: EXPECT_TIMEOUT, }, @@ -130,7 +130,10 @@ export default defineConfig({ headless: process.env.SHOW_BROWSER !== 'true', viewport: MACBOOK_WINDOW_SIZE, actionTimeout: 20000, // TODO: We might need to make this dynamic for container tests if we have low resource containers etc - navigationTimeout: 10000, + navigationTimeout: Number( + process.env.E2E_NAVIGATION_TIMEOUT ?? + (process.env.E2E_REMOTE_RUNNER === 'true' ? 30000 : 10000), + ), currentsFixturesEnabled: !!process.env.CI, }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e733578946fe..97f6f4171976 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -500,6 +500,9 @@ catalogs: electron: specifier: 41.10.4 version: 41.10.4 + endform: + specifier: 0.76.0 + version: 0.76.0 eslint: specifier: 9.29.0 version: 9.29.0 @@ -7647,6 +7650,9 @@ importers: axe-html-reporter: specifier: ^2.2.11 version: 2.2.11(axe-core@4.13.0) + endform: + specifier: 'catalog:' + version: 0.76.0 eslint-plugin-playwright: specifier: catalog:e2e version: 2.2.2(eslint@9.29.0(jiti@2.6.1)(supports-color@8.1.1)) @@ -17582,6 +17588,40 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + endform-darwin-arm64@0.76.0: + resolution: {integrity: sha512-uspKKhg6K9u01d3gETnrENNB42s+Oj0JmAgDuA/Ij+/H3g4rvLaShAwIUUf6MQgUwg7oHJGppYz0bgzlrLFcag==} + cpu: [arm64] + os: [darwin] + + endform-darwin-x86@0.76.0: + resolution: {integrity: sha512-eGS8aCru2i7EVdCYmwcz414LPGstzo9bN2/C7cy56QIaIHi71HCccVMIAHWXxgCQcE2/8WoAtcB6OVUB450Rqg==} + cpu: [x64] + os: [darwin] + + endform-linux-arm64@0.76.0: + resolution: {integrity: sha512-V0naw5K33GmQW7yDsZvqCvTi9n5jT1gJpCeYGu70tB7NiOZVGFuOlepqSOZ0ZBzE0KJ2t85eMZj8ocdSuVVQJA==} + cpu: [arm64] + os: [linux] + + endform-linux-x86@0.76.0: + resolution: {integrity: sha512-y4qt59+1rG/H1F7tZZ90fiTPbujHwVzpZE5UynpY/ffxxBkN2mU40y4BricSKX/mfyG0vey8Vp12A+u3evNrYQ==} + cpu: [x64] + os: [linux] + + endform-win32-arm64@0.76.0: + resolution: {integrity: sha512-x+YXkZqmeCW9gcABctk7L7chYYoXA55+XOF56K6iEsiZh/dLpGF6ALpGmVmIlGo9kC5ADnvSqsKlxTO/kJZ+tQ==} + cpu: [arm64] + os: [win32] + + endform-win32-x86@0.76.0: + resolution: {integrity: sha512-yCYi3kheL57pURP56QYl508oI1QK7rDWh1a7jy+YZ749MWKo4XlqJx/I04tMzYKAWYkKtfC2frTjoOY63m1CIQ==} + cpu: [x64] + os: [win32] + + endform@0.76.0: + resolution: {integrity: sha512-qh5PWJ2CuFVEGObkt8OBvk5vmUNo0ET3MWst3GXLqgl/F6GEMW1sqgBdETmtJT8dUe4rIdVPzlpxobpq1Ch+4Q==} + hasBin: true + engine.io-client@6.6.6: resolution: {integrity: sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==} @@ -37727,6 +37767,33 @@ snapshots: dependencies: once: 1.4.0 + endform-darwin-arm64@0.76.0: + optional: true + + endform-darwin-x86@0.76.0: + optional: true + + endform-linux-arm64@0.76.0: + optional: true + + endform-linux-x86@0.76.0: + optional: true + + endform-win32-arm64@0.76.0: + optional: true + + endform-win32-x86@0.76.0: + optional: true + + endform@0.76.0: + optionalDependencies: + endform-darwin-arm64: 0.76.0 + endform-darwin-x86: 0.76.0 + endform-linux-arm64: 0.76.0 + endform-linux-x86: 0.76.0 + endform-win32-arm64: 0.76.0 + endform-win32-x86: 0.76.0 + engine.io-client@6.6.6(bufferutil@4.0.9)(supports-color@8.1.1)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 28c2ca3af7a4..19b5c69e54df 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -286,6 +286,7 @@ catalog: yaml: 2.8.3 zod: 3.25.76 zod-to-json-schema: 3.23.3 + endform: 0.76.0 catalogMode: strict