343 lines
13 KiB
YAML
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();
|