Files
metabase/.github/workflows/embedding-sdk-component-tests.yml
2026-08-29 09:53:45 +12:00

343 lines
13 KiB
YAML

name: Embedding SDK Component Tests
on:
workflow_call:
env:
DISPLAY: "" # https://github.com/cypress-io/cypress/issues/4034
TERM: xterm
TZ: US/Pacific # to make Node match the instance tz
MB_EDITION: "ee"
CI_CONDUCTOR_BASE_URL: ${{ vars.CI_CONDUCTOR_BASE_URL }}
CI_CONDUCTOR_WEBHOOK_SECRET: ${{ secrets.CI_CONDUCTOR_WEBHOOK_SECRET }}
REPO_ID: ${{ github.event.repository.id }}
COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
TARGET_BRANCH: ${{ github.event.pull_request.base.ref || github.ref_name }}
QUARANTINE_FAILURES_FILE: ${{ github.workspace }}/target/quarantine-failures.jsonl
jobs:
sdk-component-tests:
name: "SDK component tests (React ${{ matrix.react-version }})"
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 45
strategy:
matrix:
react-version: [18, 19]
fail-fast: false
env:
CI_CONDUCTOR_TEST_SUITE: embedding-sdk-component-react-${{ matrix.react-version }}
# Sensitive env vars (tokens, secrets) starting with `CYPRESS_` are accessed via async `cy.env()`
# Example: `cy.env(["FOO"]).then(({ FOO }) => ...)`
# For non-sensitive config, use `Cypress.expose()` in cypress config instead (synchronous)
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
steps:
- uses: actions/checkout@v6
- name: Prepare Docker containers
uses: ./.github/actions/e2e-prepare-containers
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
maildev: true
openldap: false
webhook: false
snowplow: true
postgres: false
mysql: false
mongo: false
- name: Retrieve uberjar artifact for ee
uses: ./.github/actions/fetch-artifact
with:
commit: ${{ github.event.pull_request.head.sha || github.sha }}
edition: ee
extract-path: "target/uberjar"
output-name: "metabase.jar"
- name: Retrieve Embedding SDK dist artifact
uses: actions/download-artifact@v8
with:
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
path: ${{ github.workspace }}/resources/embedding-sdk
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Prepare Cypress environment
id: cypress-prep
uses: ./.github/actions/prepare-cypress
# esbuild resolves every import in the spec bundle's graph, even the ones tree shaking then drops.
# The cljs imports resolve into target/cljs_dev, so the compiled CLJS has to exist before Cypress runs.
# run-tests.yml compiles it in parallel with the uberjar build, so downloading is ~10x faster than compiling here.
- name: Download pre-built CLJS
id: prebuilt-cljs
uses: actions/download-artifact@v8
continue-on-error: true
with:
name: e2e-cljs-${{ github.event.pull_request.head.sha || github.sha }}
path: target/cljs_dev
- name: Compile CLJS
if: steps.prebuilt-cljs.outcome != 'success'
run: bun run build-pure:cljs
- name: Run Metabase
run: node e2e/runner/run_cypress_ci.js start
- uses: ./.github/actions/resolve-job-id
- name: Make app db snapshot
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
# Component tests only need the default snapshot
run: |
node e2e/runner/run_cypress_ci.js snapshot \
--spec "e2e/snapshot-creators/default.cy.snap.js"
# Has to be run after the app db snapshot is made, as it is not compatible with React 19
- name: Change React version to ${{ matrix.react-version }}
run: ./bin/embedding-sdk/change-react-version.bash ${{ matrix.react-version }}
- name: Run component tests for Embedding SDK
id: run-tests
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: node e2e/runner/run_cypress_ci.js component
# Keep this step continue-on-error because the quarantine logic controls whether it ultimately fails.
continue-on-error: true
- name: Rewrite JUnit hook failures
if: always()
continue-on-error: true
timeout-minutes: 1
shell: bash
run: |
if [ -d ./target/junit ]; then
echo "fix-junit-hooks: invoking bun"
bun e2e/support/fix-junit-hooks.ts ./target/junit
else
echo "No ./target/junit directory; nothing to process."
fi
- name: Quarantine gate
if: ${{ !cancelled() && steps.run-tests.outcome == 'failure' }}
continue-on-error: ${{ vars.QUARANTINE_ENGINE != 'ci-conductor' }}
timeout-minutes: 3
uses: ./.github/actions/quarantine-gate
with:
adapter: e2e
test-suite: ${{ env.CI_CONDUCTOR_TEST_SUITE }}
enforce-quarantine: ${{ vars.QUARANTINE_ENGINE == 'ci-conductor' }}
- name: "SDK component tests > Upload Cypress Artifacts upon failure"
uses: actions/upload-artifact@v7
if: ${{ !cancelled() && steps.run-tests.outcome == 'failure' }}
with:
name: cypress-recording-embedding-sdk-react-${{ matrix.react-version }}
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
- name: "SDK component tests > Publish Summary"
if: ${{ !cancelled() && steps.run-tests.outcome == 'failure' }}
uses: actions/github-script@v9
with:
script: | #js
const {
generateReport,
parseReport,
formatSummary
} = require("./.github/scripts/handle-mochawesome-report.js");
const report = await generateReport();
const results = parseReport(report);
const summary = formatSummary(results);
await core.summary.addRaw(summary).write();
decide-target-ref:
runs-on: ${{ vars.SLIM_RUNNER_KEY }}
timeout-minutes: 5
outputs:
target-ref: ${{ steps.get-target-ref.outputs.value }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 2
- name: Decide ref to checkout
id: get-target-ref
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "value=${{ github.base_ref }}" >> "$GITHUB_OUTPUT"
else
echo "value=$(git rev-parse HEAD^1)" >> "$GITHUB_OUTPUT"
fi
# This job ensures that the Embedding SDK package from the current commit (N) works with the
# Embedding SDK Bundle (Metabase JAR) from the previous commit (N-1).
# N - 1 is a target of a pull request, or a literal previous commit when this job is run on push in a branch.
sdk-component-tests-backward-compatibility:
name: "SDK component tests (Backward compatibility)"
needs: [decide-target-ref]
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 45
env:
MB_RUN_MODE: "e2e"
MB_WAREHOUSE_ALLOWED_NETWORKS: "allow-all"
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
CI_CONDUCTOR_TEST_SUITE: embedding-sdk-component-backward-compatibility
# Sensitive env vars (tokens, secrets) starting with `CYPRESS_` are accessed via async `cy.env()`
# Example: `cy.env(["FOO"]).then(({ FOO }) => ...)`
# For non-sensitive config, use `Cypress.expose()` in cypress config instead (synchronous)
CYPRESS_MB_ALL_FEATURES_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
CYPRESS_MB_STARTER_CLOUD_TOKEN: ${{ secrets.STAGING_MB_STARTER_CLOUD_TOKEN }}
CYPRESS_MB_PRO_CLOUD_TOKEN: ${{ secrets.STAGING_MB_PRO_CLOUD_TOKEN }}
CYPRESS_MB_PRO_SELF_HOSTED_TOKEN: ${{ secrets.STAGING_MB_PRO_SELF_HOSTED_TOKEN }}
steps:
- name: Check out the code from the previous commit
uses: actions/checkout@v6
with:
ref: ${{ needs.decide-target-ref.outputs.target-ref }}
- name: Prepare Docker containers
uses: ./.github/actions/e2e-prepare-containers
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
maildev: true
openldap: false
webhook: false
snowplow: true
postgres: false
mysql: false
mongo: false
- name: Get the actual commit SHA from the checked out branch
id: get-commit-sha
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Prepare back-end environment
uses: ./.github/actions/prepare-backend
- name: Attempt to retrieve the ee uberjar artifact from the previous commit
id: download-uberjar
uses: ./.github/actions/fetch-artifact
with:
commit: ${{ steps.get-commit-sha.outputs.sha }}
edition: ee
extract-path: "target/uberjar"
output-name: "metabase.jar"
continue-on-error: true # do not break the whole job if this step doesn't succeed
# Build the uberjar from the previous commit only if we couldn't fetch it.
# `step.foo.OUTCOME` is important because it looks at the step status *before* continue-on-error is applied.
# Do not confuse it with the `step.foo.conclusion`.
- name: Build MB uberjar from the commit N-1
if: steps.download-uberjar.outcome != 'success'
run: ./bin/build.sh
- name: Retrieve Embedding SDK dist artifact built from the current commit N
uses: actions/download-artifact@v8
with:
name: embedding-sdk-${{ github.event.pull_request.head.sha || github.sha }}
path: ${{ github.workspace }}/resources/embedding-sdk
- name: Prepare Cypress environment
id: cypress-prep
uses: ./.github/actions/prepare-cypress
# esbuild resolves every import in the spec bundle's graph, even the ones tree shaking then drops.
# The cljs imports resolve into target/cljs_dev, so the compiled CLJS has to exist before Cypress runs.
# The checked-out tree is the previous commit, which the current run's CLJS artifact was not built from,
# so compile from source instead of downloading.
- name: Compile CLJS
run: bun run build-pure:cljs
- name: Run Metabase
run: node e2e/runner/run_cypress_ci.js start
- uses: ./.github/actions/resolve-job-id
- name: Make app db snapshot
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
# Component tests only need the default snapshot
run: |
node e2e/runner/run_cypress_ci.js snapshot \
--spec "e2e/snapshot-creators/default.cy.snap.js"
- name: Run component tests for Embedding SDK
id: run-tests
env:
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
run: |
node e2e/runner/run_cypress_ci.js component \
--expose grepTags="-@skip-backward-compatibility"
# Keep this step continue-on-error because the quarantine logic controls whether it ultimately fails.
continue-on-error: true
- name: Rewrite JUnit hook failures
if: always()
continue-on-error: true
timeout-minutes: 1
shell: bash
run: |
if [ -d ./target/junit ]; then
echo "fix-junit-hooks: invoking bun"
bun e2e/support/fix-junit-hooks.ts ./target/junit
else
echo "No ./target/junit directory; nothing to process."
fi
- name: Quarantine gate
if: ${{ !cancelled() && steps.run-tests.outcome == 'failure' }}
continue-on-error: ${{ vars.QUARANTINE_ENGINE != 'ci-conductor' }}
timeout-minutes: 3
uses: ./.github/actions/quarantine-gate
with:
adapter: e2e
test-suite: ${{ env.CI_CONDUCTOR_TEST_SUITE }}
enforce-quarantine: ${{ vars.QUARANTINE_ENGINE == 'ci-conductor' }}
- name: Upload Cypress Artifacts upon failure
uses: actions/upload-artifact@v7
if: ${{ !cancelled() && steps.run-tests.outcome == 'failure' }}
with:
name: cypress-recording-embedding-sdk-cross-version
path: |
./cypress
./logs/test.log
if-no-files-found: ignore
- name: Publish Summary
if: ${{ !cancelled() && steps.run-tests.outcome == 'failure' }}
uses: actions/github-script@v9
with:
script: | #js
const {
generateReport,
parseReport,
formatSummary
} = require("./.github/scripts/handle-mochawesome-report.js");
const report = await generateReport();
const results = parseReport(report);
const summary = formatSummary(results);
await core.summary.addRaw(summary).write();