Compare commits
18 Commits
proj-pytho
...
e2e/run-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a199b2d71c | ||
|
|
f842041668 | ||
|
|
88f06e1976 | ||
|
|
db35f2e14a | ||
|
|
d2bae3d18a | ||
|
|
716e944681 | ||
|
|
ecedbf2a11 | ||
|
|
b3a6ec5cce | ||
|
|
6d4480bc3e | ||
|
|
94221b292e | ||
|
|
ac95fe9476 | ||
|
|
13767bc988 | ||
|
|
4eef0140f5 | ||
|
|
8188d7a9af | ||
|
|
87cc7bc096 | ||
|
|
c61d0a0cac | ||
|
|
bd80bc35df | ||
|
|
b4f5290468 |
72
.github/actions/build-e2e-matrix/action.yml
vendored
72
.github/actions/build-e2e-matrix/action.yml
vendored
@@ -1,72 +0,0 @@
|
||||
name: Build E2E Matrix
|
||||
description: Build and output a custom matrix for E2E tests.
|
||||
|
||||
outputs:
|
||||
matrix:
|
||||
description: Parameterized matrix in JSON format
|
||||
value: ${{ steps.matrix.outputs.result }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
id: matrix
|
||||
with:
|
||||
script: | # js
|
||||
const java = 21;
|
||||
|
||||
const defaultRunner = "ubuntu-22.04";
|
||||
|
||||
const defaultOptions = {
|
||||
"java-version": java,
|
||||
runner: defaultRunner,
|
||||
edition: "ee",
|
||||
context: "folder"
|
||||
};
|
||||
|
||||
const testSets = [
|
||||
["actions", {} ],
|
||||
["admin", {} ],
|
||||
["admin-2", {} ],
|
||||
["binning", {} ],
|
||||
["collections", {} ],
|
||||
["custom-column", {} ],
|
||||
["dashboard", {} ],
|
||||
["dashboard-cards", {} ],
|
||||
["dashboard-filters", {} ],
|
||||
["dashboard-filters-2", {} ],
|
||||
["embedding", {} ],
|
||||
["embedding-sdk", { context: "embedding-sdk" } ],
|
||||
["filters", {} ],
|
||||
["filters-reproductions", {}],
|
||||
["joins", {} ],
|
||||
["metrics", {} ],
|
||||
["models", {} ],
|
||||
["native", {} ],
|
||||
["native-filters", {} ],
|
||||
["onboarding", {} ],
|
||||
["organization", {} ],
|
||||
["permissions", {} ],
|
||||
["question", {} ],
|
||||
["question-reproductions", {} ],
|
||||
["search", {}],
|
||||
["sharing", {} ],
|
||||
["stats", {} ],
|
||||
["visualizations-charts", {} ],
|
||||
["visualizations-tabular", {} ],
|
||||
["oss-subset", { edition: 'oss', context: "special" } ],
|
||||
["slow", { context: "special" } ],
|
||||
["flaky", { context: "special" } ],
|
||||
["mongo", { context: "qa-database" } ],
|
||||
];
|
||||
|
||||
const config = testSets.map(([name, specialOptions]) => {
|
||||
return {
|
||||
...defaultOptions,
|
||||
...specialOptions,
|
||||
name,
|
||||
}
|
||||
});
|
||||
|
||||
const matrix = { include: config };
|
||||
return matrix;
|
||||
91
.github/workflows/e2e-matrix-builder.yml
vendored
Normal file
91
.github/workflows/e2e-matrix-builder.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
name: E2E Matrix Builder
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
chunks:
|
||||
required: true
|
||||
type: number
|
||||
default: 30
|
||||
outputs:
|
||||
matrix:
|
||||
value: ${{ jobs.build-matrix.outputs.matrix }}
|
||||
|
||||
jobs:
|
||||
build-matrix:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
matrix: ${{ steps.matrix.outputs.config }}
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20.x'
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: e2e
|
||||
- name: get test file names
|
||||
id: test-files
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::set-output name=filenames::$(find e2e/test/scenarios -name '*.cy.spec.*' | tr '\n' ',')"
|
||||
- run: yarn add underscore
|
||||
- uses: actions/github-script@v7
|
||||
id: matrix
|
||||
with:
|
||||
script: | # js
|
||||
const _ = require('underscore');
|
||||
|
||||
const java = 21;
|
||||
const defaultRunner = "ubuntu-22.04";
|
||||
|
||||
// these are special and shouldn't be chunked out arbitrarily
|
||||
const blacklist = [
|
||||
"/embedding-sdk/",
|
||||
"/cross-version/"
|
||||
];
|
||||
|
||||
const defaultOptions = {
|
||||
"java-version": java,
|
||||
runner: defaultRunner,
|
||||
specs: "./e2e/test/scenarios/**/*.cy.spec.*",
|
||||
edition: "ee",
|
||||
};
|
||||
|
||||
const specialTests = [
|
||||
{ name: "embedding-sdk", specs: "./e2e/test/scenarios/embedding-sdk/**.cy.spec.*" },
|
||||
{ name: "oss-subset", edition: 'oss', tags: "@OSS" },
|
||||
{ name: "slow", tags: "@slow" },
|
||||
{ name: "mongo", tags: "@mongo" },
|
||||
];
|
||||
|
||||
const fileNames = "${{ steps.test-files.outputs.filenames }}"
|
||||
.split(",")
|
||||
.filter(Boolean)
|
||||
.filter(testPath => !blacklist.some(blacklisted => testPath.includes(blacklisted)));
|
||||
|
||||
const fileCount = fileNames.length;
|
||||
|
||||
// subtract the special tests since they all get their own runners
|
||||
const chunkSize = fileCount / (Number('${{ inputs.chunks }}') - specialTests.length);
|
||||
|
||||
|
||||
|
||||
const chunkedFiles = _.chunk(fileNames, chunkSize);
|
||||
|
||||
const regularTests = chunkedFiles.map((files, index) => ({
|
||||
name: `e2e-group-${index + 1}`,
|
||||
specs: files.join(","),
|
||||
tags: '',
|
||||
}));
|
||||
|
||||
const testSets = [...regularTests, ...specialTests];
|
||||
|
||||
const config = testSets.map((options) => ({
|
||||
...defaultOptions,
|
||||
...options,
|
||||
}));
|
||||
|
||||
console.log("matrix config", config);
|
||||
|
||||
core.setOutput("config", JSON.stringify({ include: config }));
|
||||
161
.github/workflows/e2e-test.yml
vendored
Normal file
161
.github/workflows/e2e-test.yml
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
name: E2E Test
|
||||
run-name: E2E Test - ${{ inputs.name }}
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
name:
|
||||
required: true
|
||||
type: string
|
||||
edition: # oss | ee
|
||||
required: false
|
||||
type: string
|
||||
default: ee
|
||||
specs:
|
||||
required: false
|
||||
default: ./e2e/test/scenarios/**/*.cy.spec.*
|
||||
type: string
|
||||
tags:
|
||||
required: false
|
||||
type: string
|
||||
runner:
|
||||
required: false
|
||||
type: string
|
||||
default: ubuntu-22.04
|
||||
|
||||
jobs:
|
||||
e2e-tests:
|
||||
runs-on: ${{ inputs.runner }}
|
||||
timeout-minutes: 45
|
||||
name: e2e-tests-${{ inputs.name }}-${{ inputs.edition }}
|
||||
env:
|
||||
E2E_HOST: http://localhost:3000
|
||||
MB_EDITION: ${{ inputs.edition }}
|
||||
DISPLAY: ""
|
||||
QA_DB_ENABLED: true
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
|
||||
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
|
||||
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number || '' }}
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
MB_SNOWPLOW_AVAILABLE: true
|
||||
MB_SNOWPLOW_URL: "http://localhost:9090" # Snowplow micro
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
CYPRESS_QA_DB_MONGO: ${{ inputs.name == 'mongo' }}
|
||||
CYPRESS_IS_EMBEDDING_SDK: ${{ inputs.name == 'embedding-sdk' }}
|
||||
WITH_SNAPSHOTS: 1
|
||||
mongo: ${{ inputs.name == 'mongo' }}
|
||||
postgres: ${{ inputs.name != 'mongo' }}
|
||||
mysql: ${{ inputs.name != 'mongo' }}
|
||||
openldap: ${{ contains(inputs.specs, 'ldap') }}
|
||||
maildev: true
|
||||
webhook: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare Docker containers
|
||||
uses: ./.github/actions/e2e-prepare-containers
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
maildev: true
|
||||
openldap: ${{ env.openldap }}
|
||||
postgres: ${{ env.postgres }}
|
||||
mysql: ${{ env.mysql }}
|
||||
mongo: ${{ env.mongo }}
|
||||
webhook: ${{ env.webhook }}
|
||||
|
||||
- name: Run Embedding SDK Storybook
|
||||
if: inputs.name == 'embedding-sdk'
|
||||
uses: ./.github/actions/run-embedding-sdk-storybook
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: "temurin"
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
with:
|
||||
m2-cache-key: uberjar
|
||||
java-version: 21
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Run Snowplow micro
|
||||
uses: ./.github/actions/run-snowplow-micro
|
||||
|
||||
- name: Run Metabase EE
|
||||
if: ${{ inputs.edition == 'ee' }}
|
||||
run: yarn dev-ee &
|
||||
|
||||
- name: Run Metabase OSS
|
||||
if: ${{ inputs.edition == 'oss' }}
|
||||
run: yarn dev &
|
||||
|
||||
- name: Wait for Metabase to start
|
||||
run: while ! curl 'http://localhost:3000/api/health' | grep '{"status":"ok"}'; do sleep 1; done
|
||||
shell: bash
|
||||
|
||||
- name: Run EE Cypress tests on ${{ inputs.name }}
|
||||
if: ${{ !inputs.tags }}
|
||||
shell: bash
|
||||
run: |
|
||||
yarn test-cypress-run \
|
||||
--env grepTags="-@slow+-@mongo+-@flaky+-@OSS --@quarantine",grepOmitFiltered=true \
|
||||
--spec '${{ inputs.specs }}' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Run Tagged EE Cypress tests on ${{ inputs.name }}
|
||||
if: ${{ inputs.tags }}
|
||||
shell: bash
|
||||
run: |
|
||||
yarn test-cypress-run \
|
||||
--env grepTags="${{inputs.tags}} --@quarantine",grepOmitFiltered=true \
|
||||
--spec '${{ inputs.specs }}' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: e2e-${{ inputs.name }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-${{ inputs.name }}-${{ inputs.edition }}
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Publish Summary
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | #js
|
||||
const {
|
||||
parseReport,
|
||||
formatSummary
|
||||
} = require("./.github/scripts/handle-mochawesome-report.js");
|
||||
|
||||
const report = parseReport();
|
||||
const summary = formatSummary(report);
|
||||
|
||||
await core.summary.addRaw(summary).write();
|
||||
227
.github/workflows/e2e-tests.yml
vendored
227
.github/workflows/e2e-tests.yml
vendored
@@ -29,15 +29,9 @@ jobs:
|
||||
filters: .github/file-paths.yaml
|
||||
|
||||
e2e-matrix-builder:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
matrix: ${{ steps.e2e-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Generate matrix for E2E tests
|
||||
id: e2e-matrix
|
||||
uses: ./.github/actions/build-e2e-matrix
|
||||
uses: ./.github/workflows/e2e-matrix-builder.yml
|
||||
with:
|
||||
chunks: 50
|
||||
|
||||
# if this is a test on a release branch, we need to check the build requirements
|
||||
get-build-requirements:
|
||||
@@ -70,12 +64,12 @@ jobs:
|
||||
node_version: requirements.node,
|
||||
};
|
||||
|
||||
|
||||
build:
|
||||
needs: [files-changed, e2e-matrix-builder, get-build-requirements]
|
||||
needs: [files-changed, get-build-requirements]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
github.event.pull_request.draft == false &&
|
||||
needs.e2e-matrix-builder.result == 'success' &&
|
||||
needs.files-changed.outputs.e2e_all == 'true'
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 25
|
||||
@@ -108,179 +102,56 @@ jobs:
|
||||
name: metabase-${{ matrix.edition }}-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
|
||||
e2e-tests:
|
||||
needs: [build, e2e-matrix-builder]
|
||||
name: ${{ matrix.name }}-${{ matrix.edition }}
|
||||
uses: ./.github/workflows/e2e-test.yml
|
||||
needs: [ e2e-matrix-builder ]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.e2e-matrix-builder.outputs.matrix) }}
|
||||
secrets: inherit
|
||||
with:
|
||||
name: ${{ matrix.name }}
|
||||
edition: ${{ matrix.edition }}
|
||||
specs: ${{ matrix.specs }}
|
||||
tags: ${{ matrix.tags }}
|
||||
runner: ${{ matrix.runner }}
|
||||
|
||||
# run this separately to make it non-blocking
|
||||
flaky-e2e-tests:
|
||||
name: Flaky e2e Tests
|
||||
uses: ./.github/workflows/e2e-test.yml
|
||||
needs: build
|
||||
if: |
|
||||
!cancelled() && needs.build.result == 'success'
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 45
|
||||
name: e2e-tests-${{ matrix.name }}-${{ matrix.edition }}
|
||||
env:
|
||||
MB_EDITION: ${{ matrix.edition }}
|
||||
DISPLAY: ""
|
||||
QA_DB_ENABLED: true
|
||||
# Any env starting with `CYPRESS_` will be available to all Cypress tests via `Cypress.env()`
|
||||
# Example: you can get `CYPRESS_FOO` with `Cypress.env("FOO")`
|
||||
CYPRESS_ALL_FEATURES_TOKEN: ${{ secrets.ENTERPRISE_TOKEN }}
|
||||
CYPRESS_NO_FEATURES_TOKEN: ${{ secrets.E2E_STARTER_TOKEN }}
|
||||
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number || '' }}
|
||||
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message || github.event.head.sha }}
|
||||
MB_SNOWPLOW_AVAILABLE: true
|
||||
MB_SNOWPLOW_URL: "http://localhost:9090" # Snowplow micro
|
||||
TZ: US/Pacific # to make node match the instance tz
|
||||
CYPRESS_CI: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.e2e-matrix-builder.outputs.matrix) }}
|
||||
secrets: inherit
|
||||
with:
|
||||
name: Flaky Tests
|
||||
tags: "@flaky"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare Docker containers
|
||||
uses: ./.github/actions/e2e-prepare-containers
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
maildev: true
|
||||
openldap: ${{ startsWith(matrix.name, 'admin') }}
|
||||
postgres: ${{ matrix.name != 'mongo'}}
|
||||
mysql: ${{ matrix.name != 'mongo'}}
|
||||
mongo: ${{ matrix.name == 'mongo'}}
|
||||
webhook: true
|
||||
|
||||
- name: Retrieve uberjar artifact for ${{ matrix.edition }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: metabase-${{ matrix.edition }}-${{ github.event.pull_request.head.sha || github.sha }}-uberjar
|
||||
|
||||
- name: Get the version info
|
||||
run: |
|
||||
jar xf target/uberjar/metabase.jar version.properties
|
||||
mv version.properties resources/
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare JDK ${{ matrix.java-version }}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
distribution: "temurin"
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
id: cypress-prep
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Run Snowplow micro
|
||||
uses: ./.github/actions/run-snowplow-micro
|
||||
|
||||
- name: Run OSS-specific Cypress tests
|
||||
if: matrix.edition == 'oss' && github.event_name != 'schedule'
|
||||
run: |
|
||||
yarn run test-cypress-run \
|
||||
--env grepTags=@OSS,grepOmitFiltered=true \
|
||||
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Run slow and resource-intensive Cypress tests
|
||||
if: matrix.name == 'slow' && github.event_name != 'schedule'
|
||||
run: |
|
||||
yarn run test-cypress-run \
|
||||
--env grepTags="@slow",grepOmitFiltered=true \
|
||||
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Run Flaky Cypress tests
|
||||
if: matrix.name == 'flaky' && github.event_name != 'schedule'
|
||||
run: |
|
||||
yarn run test-cypress-run \
|
||||
--env grepTags="@flaky --@quarantine",grepOmitFiltered=true \
|
||||
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Run EE Cypress tests on ${{ matrix.name }}
|
||||
if: matrix.context == 'folder' && github.event_name != 'schedule'
|
||||
run: |
|
||||
yarn run test-cypress-run \
|
||||
--env grepTags="-@slow+-@mongo+-@flaky --@quarantine",grepOmitFiltered=true \
|
||||
--folder ${{ matrix.name }} \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
|
||||
- name: Run E2E tests that depend on Mongo
|
||||
if: matrix.name == 'mongo'
|
||||
run: |
|
||||
yarn run test-cypress-run \
|
||||
--env grepTags="@mongo --@quarantine",grepOmitFiltered=true \
|
||||
--spec './e2e/test/scenarios/**/*.cy.spec.(js|ts)' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
env:
|
||||
CYPRESS_QA_DB_MONGO: true
|
||||
|
||||
- name: Run Embedding SDK Storybook
|
||||
if: matrix.context == 'embedding-sdk' && github.event_name != 'schedule'
|
||||
uses: ./.github/actions/run-embedding-sdk-storybook
|
||||
|
||||
- name: Run E2E tests for Embedding SDK
|
||||
if: matrix.context == 'embedding-sdk' && github.event_name != 'schedule'
|
||||
run: |
|
||||
yarn run test-cypress-run \
|
||||
--env grepTags="-@slow+-@flaky --@quarantine",grepOmitFiltered=true \
|
||||
--folder 'embedding-sdk' \
|
||||
--browser ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
env:
|
||||
CYPRESS_IS_EMBEDDING_SDK: true
|
||||
shell: bash
|
||||
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit
|
||||
output-name: e2e-${{ matrix.name }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-${{ matrix.name }}-${{ matrix.edition }}
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Publish Summary
|
||||
if: failure()
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: | #js
|
||||
const {
|
||||
parseReport,
|
||||
formatSummary
|
||||
} = require("./.github/scripts/handle-mochawesome-report.js");
|
||||
|
||||
const report = parseReport();
|
||||
const summary = formatSummary(report);
|
||||
|
||||
await core.summary.addRaw(summary).write();
|
||||
|
||||
e2e-tests-skipped-stub:
|
||||
needs: [e2e-tests, e2e-matrix-builder]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.e2e-tests.result == 'skipped'
|
||||
runs-on: ${{ matrix.runner }}
|
||||
e2e-tests-result:
|
||||
needs: [e2e-tests, flaky-e2e-tests]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: e2e-tests-${{ matrix.name }}-${{ matrix.edition }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.e2e-matrix-builder.outputs.matrix) }}
|
||||
name: e2e-tests-result
|
||||
if: always() && !cancelled()
|
||||
env:
|
||||
E2E_TESTS_RESULT: ${{ needs.e2e-tests.result }}
|
||||
FLAKY_E2E_TESTS_RESULT: ${{ needs.flaky-e2e-tests.result }}
|
||||
steps:
|
||||
- run: |
|
||||
- name: Stub
|
||||
if: ${{ needs.e2e-tests.result == 'skipped' }}
|
||||
run: |
|
||||
echo "Didn't run due to conditional filtering"
|
||||
- name: Test results
|
||||
if: ${{ needs.e2e-tests.result != 'skipped' }}
|
||||
run: | # sh
|
||||
echo "E2E Tests: ${{ needs.e2e-tests.result }}"
|
||||
echo "Flaky E2E Tests: ${{ needs.flaky-e2e-tests.result }}"
|
||||
|
||||
if [ "${{ needs.e2e-tests.result }}" != "success" ]; then
|
||||
echo "E2E Tests failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pr-env:
|
||||
needs: [build]
|
||||
|
||||
@@ -5,6 +5,7 @@ const runCypress = require("./cypress-runner-run-tests");
|
||||
const { printBold } = require("./cypress-runner-utils");
|
||||
|
||||
const e2eHost = process.env["E2E_HOST"];
|
||||
const withSnapshots = process.env.WITH_SNAPSHOTS;
|
||||
|
||||
const server = CypressBackend.createServer();
|
||||
const baseUrl = e2eHost || server.host;
|
||||
@@ -21,6 +22,11 @@ const init = async () => {
|
||||
await generateSnapshots(baseUrl, cleanup);
|
||||
}
|
||||
|
||||
if (withSnapshots && e2eHost) {
|
||||
printBold("Generating snapshots");
|
||||
await generateSnapshots(baseUrl, cleanup);
|
||||
}
|
||||
|
||||
printBold("Starting Cypress");
|
||||
await runCypress(baseUrl, cleanup);
|
||||
};
|
||||
|
||||
@@ -322,7 +322,7 @@ export function interceptIfNotPreviouslyDefined({ method, url, alias } = {}) {
|
||||
export function saveQuestion(
|
||||
name,
|
||||
{ addToDashboard = false, wrapId = false, idAlias = "questionId" } = {},
|
||||
pickEntityOptions,
|
||||
pickEntityOptions = null,
|
||||
) {
|
||||
cy.intercept("POST", "/api/card").as("saveQuestion");
|
||||
cy.findByTestId("qb-header").button("Save").click();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import dayjs from "dayjs";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
|
||||
import { H } from "e2e/support";
|
||||
dayjs.extend(timezone);
|
||||
|
||||
import {
|
||||
@@ -11,13 +13,13 @@ import {
|
||||
* There's an endpoint that supports this: /api/testing/set-time.
|
||||
* These tests guarantee that this endpoint works as expected. */
|
||||
describe("server clock", () => {
|
||||
afterEach(() => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
resetServerTime();
|
||||
cy.signInAsAdmin();
|
||||
});
|
||||
|
||||
it("can advance the server clock by one day", () => {
|
||||
cy.signInAsAdmin();
|
||||
|
||||
cy.request("GET", "/api/setting/report-timezone-long").then(response => {
|
||||
cy.wrap(response.body).as("serverTimeZone");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,723 @@
|
||||
import { H } from "e2e/support";
|
||||
|
||||
import * as QSHelpers from "./shared/dashboard-filters-query-stages";
|
||||
|
||||
/**
|
||||
* Empty section title element is rendered.
|
||||
* TODO: https://github.com/metabase/metabase/issues/47218
|
||||
*/
|
||||
const NAMELESS_SECTION = "";
|
||||
|
||||
/**
|
||||
* Abbreviations used for card aliases in this test suite:
|
||||
* qbq = question-based question
|
||||
* qbm = question-based model
|
||||
* mbq = model-based question
|
||||
* mbm = model-based model
|
||||
*/
|
||||
describe("scenarios > dashboard > filters > query stages", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
QSHelpers.createBaseQuestions();
|
||||
|
||||
cy.intercept("POST", "/api/dataset").as("dataset");
|
||||
cy.intercept("GET", "/api/dashboard/**").as("getDashboard");
|
||||
cy.intercept("PUT", "/api/dashboard/**").as("updateDashboard");
|
||||
cy.intercept("POST", "/api/dashboard/*/dashcard/*/card/*/query").as(
|
||||
"dashboardData",
|
||||
);
|
||||
cy.intercept("GET", "/api/public/dashboard/*/dashcard/*/card/*").as(
|
||||
"publicDashboardData",
|
||||
);
|
||||
cy.intercept("GET", "/api/embed/dashboard/*/dashcard/*/card/*").as(
|
||||
"embeddedDashboardData",
|
||||
);
|
||||
});
|
||||
|
||||
describe("1-stage queries", () => {
|
||||
describe("Q1 - join, custom column, no aggregations, no breakouts", () => {
|
||||
beforeEach(() => {
|
||||
QSHelpers.createAndVisitDashboardWithCardMatrix(
|
||||
QSHelpers.createQ1Query,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows to map to all relevant columns", () => {
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Model", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Question-based Model",
|
||||
[
|
||||
...QSHelpers.ORDERS_DATE_COLUMNS,
|
||||
"Reviews - Product → Created At",
|
||||
],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_DATE_COLUMNS],
|
||||
["Reviews - Product → Product", QSHelpers.PRODUCTS_DATE_COLUMNS],
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Model-based Model",
|
||||
[
|
||||
...QSHelpers.ORDERS_DATE_COLUMNS,
|
||||
"Reviews - Product → Created At",
|
||||
],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_DATE_COLUMNS],
|
||||
["Reviews - Product → Product", QSHelpers.PRODUCTS_DATE_COLUMNS],
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Question-based Model",
|
||||
["Reviews - Product → Reviewer", "Reviews - Product → Body"],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_TEXT_COLUMNS],
|
||||
["Reviews - Product → Product", QSHelpers.PRODUCTS_TEXT_COLUMNS],
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Model-based Model",
|
||||
["Reviews - Product → Reviewer", "Reviews - Product → Body"],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_TEXT_COLUMNS],
|
||||
["Reviews - Product → Product", QSHelpers.PRODUCTS_TEXT_COLUMNS],
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Question",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
],
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Model",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
],
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Question-based Model",
|
||||
[
|
||||
...QSHelpers.ORDERS_NUMBER_COLUMNS,
|
||||
"Net",
|
||||
"Reviews - Product → Rating",
|
||||
],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Reviews - Product → Product",
|
||||
QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Model-based Model",
|
||||
[
|
||||
...QSHelpers.ORDERS_NUMBER_COLUMNS,
|
||||
"Net",
|
||||
"Reviews - Product → Rating",
|
||||
],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Reviews - Product → Product",
|
||||
QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Q2 - join, custom column, 2 aggregations, no breakouts", () => {
|
||||
beforeEach(() => {
|
||||
QSHelpers.createAndVisitDashboardWithCardMatrix(
|
||||
QSHelpers.createQ2Query,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows to map to all relevant columns (metabase#47184)", () => {
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Model", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(2);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(3);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(2);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(3);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Question",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Model",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count", "Sum of Total"]]],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count", "Sum of Total"]]],
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Q3 - join, custom column, no aggregations, 3 breakouts", () => {
|
||||
beforeEach(() => {
|
||||
QSHelpers.createAndVisitDashboardWithCardMatrix(
|
||||
QSHelpers.createQ3Query,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows to map to all relevant columns", () => {
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Model", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
NAMELESS_SECTION,
|
||||
["Created At: Month", "User → Created At: Year"],
|
||||
],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
NAMELESS_SECTION,
|
||||
["Created At: Month", "User → Created At: Year"],
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Product → Category"]]],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Product → Category"]]],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Question",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Model",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(2);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(3);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Q4 - join, custom column, 2 aggregations, 2 breakouts (metabase#47184)", () => {
|
||||
beforeEach(() => {
|
||||
QSHelpers.createAndVisitDashboardWithCardMatrix(
|
||||
QSHelpers.createQ4Query,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows to map to all relevant columns", () => {
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Model", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
NAMELESS_SECTION,
|
||||
["Created At: Month", "User → Created At: Year"],
|
||||
],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
NAMELESS_SECTION,
|
||||
["Created At: Month", "User → Created At: Year"],
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Product → Category"]]],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Product → Category"]]],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Question",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
["Summaries", ["Count", "Sum of Total"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Model",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
["Summaries", ["Count", "Sum of Total"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count", "Sum of Total"]]],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count", "Sum of Total"]]],
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,426 @@
|
||||
import { H } from "e2e/support";
|
||||
|
||||
import * as QSHelpers from "./shared/dashboard-filters-query-stages";
|
||||
|
||||
/**
|
||||
* Empty section title element is rendered.
|
||||
* TODO: https://github.com/metabase/metabase/issues/47218
|
||||
*/
|
||||
const NAMELESS_SECTION = "";
|
||||
|
||||
/**
|
||||
* Abbreviations used for card aliases in this test suite:
|
||||
* qbq = question-based question
|
||||
* qbm = question-based model
|
||||
* mbq = model-based question
|
||||
* mbm = model-based model
|
||||
*/
|
||||
describe("scenarios > dashboard > filters > query stages", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
QSHelpers.createBaseQuestions();
|
||||
|
||||
cy.intercept("POST", "/api/dataset").as("dataset");
|
||||
cy.intercept("GET", "/api/dashboard/**").as("getDashboard");
|
||||
cy.intercept("PUT", "/api/dashboard/**").as("updateDashboard");
|
||||
cy.intercept("POST", "/api/dashboard/*/dashcard/*/card/*/query").as(
|
||||
"dashboardData",
|
||||
);
|
||||
cy.intercept("GET", "/api/public/dashboard/*/dashcard/*/card/*").as(
|
||||
"publicDashboardData",
|
||||
);
|
||||
cy.intercept("GET", "/api/embed/dashboard/*/dashcard/*/card/*").as(
|
||||
"embeddedDashboardData",
|
||||
);
|
||||
});
|
||||
|
||||
describe("3-stage queries", () => {
|
||||
describe("Q9 - Q8 + 3rd stage with 1 aggregation", () => {
|
||||
beforeEach(() => {
|
||||
QSHelpers.createAndVisitDashboardWithCardMatrix(
|
||||
QSHelpers.createQ9Query,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows to map to all relevant columns", () => {
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_DATE_COLUMNS,
|
||||
...QSHelpers.REVIEWS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Model", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_DATE_COLUMNS,
|
||||
...QSHelpers.REVIEWS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_TEXT_COLUMNS,
|
||||
...QSHelpers.REVIEWS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
["Summaries (2)", ["Reviewer", "Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_TEXT_COLUMNS,
|
||||
...QSHelpers.REVIEWS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
["Summaries (2)", ["Reviewer", "Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Question",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_NUMBER_COLUMNS,
|
||||
...QSHelpers.REVIEWS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
["Summaries", ["Count", "Sum of Total", "5 * Count"]],
|
||||
["Summaries (2)", ["Count", "Sum of Rating"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Model",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_NUMBER_COLUMNS,
|
||||
...QSHelpers.REVIEWS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
["Summaries", ["Count", "Sum of Total", "5 * Count"]],
|
||||
["Summaries (2)", ["Count", "Sum of Rating"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count"]]],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count"]]],
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
describe("applies filter to the the dashcard and allows to drill via dashcard header", () => {
|
||||
it("1st stage explicit join", () => {
|
||||
QSHelpers.setup1stStageExplicitJoinFilter();
|
||||
QSHelpers.apply1stStageExplicitJoinFilter();
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["953"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["953"],
|
||||
});
|
||||
});
|
||||
|
||||
it("1st stage implicit join (data source)", () => {
|
||||
QSHelpers.setup1stStageImplicitJoinFromSourceFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["1,044"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["1,044"],
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: https://github.com/metabase/metabase/issues/46774
|
||||
it.skip("1st stage implicit join (joined data source)", () => {
|
||||
QSHelpers.setup1stStageImplicitJoinFromJoinFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["1,077"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["1,077"],
|
||||
});
|
||||
});
|
||||
|
||||
it("1st stage custom column", () => {
|
||||
QSHelpers.setup1stStageCustomColumnFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["688"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["688"],
|
||||
});
|
||||
});
|
||||
|
||||
it("1st stage aggregation", () => {
|
||||
QSHelpers.setup1stStageAggregationFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["3"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["3"],
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: https://github.com/metabase/metabase/issues/46774
|
||||
it.skip("1st stage breakout", () => {
|
||||
QSHelpers.setup1stStageBreakoutFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["1,077"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["1,077"],
|
||||
});
|
||||
});
|
||||
|
||||
it("2nd stage explicit join", () => {
|
||||
QSHelpers.setup2ndStageExplicitJoinFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["4"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["4"],
|
||||
});
|
||||
});
|
||||
|
||||
it("2nd stage custom column", () => {
|
||||
QSHelpers.setup2ndStageCustomColumnFilter();
|
||||
QSHelpers.apply2ndStageCustomColumnFilter();
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["31"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["31"],
|
||||
});
|
||||
});
|
||||
|
||||
it("2nd stage aggregation", () => {
|
||||
QSHelpers.setup2ndStageAggregationFilter();
|
||||
QSHelpers.apply2ndStageAggregationFilter();
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["6"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["6"],
|
||||
});
|
||||
});
|
||||
|
||||
it("2nd stage breakout", () => {
|
||||
H.editDashboard();
|
||||
|
||||
QSHelpers.getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
QSHelpers.getPopoverList().scrollTo("bottom");
|
||||
QSHelpers.getPopoverItem("Category", 2).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
QSHelpers.getPopoverList().scrollTo("bottom");
|
||||
QSHelpers.getPopoverItem("Category", 2).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByLabelText("Gadget").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["1,077"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["1,077"],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,255 @@
|
||||
import { H } from "e2e/support";
|
||||
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
|
||||
import type { Card, StructuredQuery } from "metabase-types/api";
|
||||
|
||||
import * as QSHelpers from "./shared/dashboard-filters-query-stages";
|
||||
|
||||
const { ORDERS, PRODUCTS } = SAMPLE_DATABASE;
|
||||
|
||||
describe("scenarios > dashboard > filters > query stages + temporal unit parameters", () => {
|
||||
describe("applies filter to the the dashcard and allows to drill via dashcard header", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
|
||||
cy.intercept("POST", "/api/dataset").as("dataset");
|
||||
});
|
||||
|
||||
it("1st stage explicit join + unit of time parameter", () => {
|
||||
H.createDashboard(
|
||||
{
|
||||
name: "My new dashboard",
|
||||
},
|
||||
{ wrapId: true, idAlias: "myNewDash" },
|
||||
);
|
||||
|
||||
cy.get("@myNewDash").then((dashId: number | any) => {
|
||||
cy.request("POST", "/api/activity/recents", {
|
||||
context: "selection",
|
||||
model: "dashboard",
|
||||
model_id: dashId,
|
||||
});
|
||||
});
|
||||
|
||||
H.startNewQuestion();
|
||||
|
||||
H.entityPickerModal().within(() => {
|
||||
H.entityPickerModalTab("Tables").click();
|
||||
H.entityPickerModalItem(2, "Orders").click();
|
||||
});
|
||||
|
||||
H.getNotebookStep("filter")
|
||||
.findByText("Add filters to narrow your answer")
|
||||
.click();
|
||||
H.popover().within(() => {
|
||||
cy.findByText("Orders").click();
|
||||
cy.findByText("Product").click();
|
||||
cy.findByText("Category").click();
|
||||
cy.findByLabelText("Gizmo").click();
|
||||
cy.findByLabelText("Doohickey").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
|
||||
H.getNotebookStep("summarize")
|
||||
.findByText("Pick a function or metric")
|
||||
.click();
|
||||
H.popover().findByText("Count of rows").click();
|
||||
H.getNotebookStep("summarize").icon("add").click();
|
||||
H.popover().within(() => {
|
||||
cy.findByText("Sum of ...").click();
|
||||
cy.findByText("Total").click();
|
||||
});
|
||||
|
||||
H.getNotebookStep("summarize")
|
||||
.findByText("Pick a column to group by")
|
||||
.click();
|
||||
H.popover()
|
||||
.findByLabelText("Created At")
|
||||
.findByLabelText("Temporal bucket")
|
||||
.click();
|
||||
H.popover().last().findByText("Week").click();
|
||||
H.getNotebookStep("summarize")
|
||||
.findByTestId("breakout-step")
|
||||
.icon("add")
|
||||
.click();
|
||||
H.popover().within(() => {
|
||||
cy.findByText("Orders").click();
|
||||
cy.findByText("Product").click();
|
||||
cy.findByText("Category").click();
|
||||
});
|
||||
|
||||
cy.findAllByTestId("action-buttons").last().button("Summarize").click();
|
||||
H.popover().findByText("Count of rows").click();
|
||||
H.getNotebookStep("summarize", { stage: 1 })
|
||||
.findByText("Pick a column to group by")
|
||||
.click();
|
||||
H.popover().findByLabelText("Created At: Week").click();
|
||||
|
||||
H.visualize(); // need to visualize because startNewQuestion does not set "display" property on a card
|
||||
cy.wait("@dataset");
|
||||
H.saveQuestion("test"); // added to new dash automatically
|
||||
|
||||
cy.findByLabelText("Add a filter or parameter").click();
|
||||
H.popover().findByText("Text or Category").click();
|
||||
H.getDashboardCard().findByText("Select…").click();
|
||||
cy.findAllByText("Category").first().click();
|
||||
|
||||
cy.findByLabelText("Add a filter or parameter").click();
|
||||
H.popover().findByText("Time grouping").click();
|
||||
H.getDashboardCard().findByText("Select…").click();
|
||||
H.popover().findByText("Created At: Week").click();
|
||||
|
||||
H.saveDashboard();
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByText("Gizmo").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
|
||||
H.filterWidget().eq(1).click();
|
||||
H.popover().findByText("Quarter").click();
|
||||
|
||||
H.getDashboardCard().findByText("Q1 2023").should("be.visible");
|
||||
H.getDashboardCard().findByTestId("legend-caption-title").click();
|
||||
cy.wait("@dataset");
|
||||
|
||||
// assert that new filter was applied
|
||||
cy.findByTestId("qb-filters-panel").within(() => {
|
||||
cy.findByText("Product → Category is 2 selections").should(
|
||||
"be.visible",
|
||||
);
|
||||
cy.findByText("Product → Category is Gizmo").should("be.visible");
|
||||
});
|
||||
|
||||
// assert that temporal unit parameter was applied
|
||||
cy.findByTestId("chart-container")
|
||||
.findByText("Q1 2023")
|
||||
.should("be.visible");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("pivot tables", () => {
|
||||
const QUESTION_PIVOT_INDEX = 0;
|
||||
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
QSHelpers.createBaseQuestions();
|
||||
|
||||
cy.intercept("POST", "/api/dataset").as("dataset");
|
||||
cy.intercept("POST", "/api/dataset/pivot").as("datasetPivot");
|
||||
cy.intercept("GET", "/api/dashboard/**").as("getDashboard");
|
||||
cy.intercept("POST", "/api/card/*/query").as("cardQuery");
|
||||
|
||||
cy.then(function () {
|
||||
H.createQuestion({
|
||||
type: "question",
|
||||
query: createPivotableQuery(this.baseQuestion),
|
||||
name: "Question - pivot viz",
|
||||
display: "pivot",
|
||||
}).then(response => {
|
||||
const card = response.body;
|
||||
QSHelpers.createAndVisitDashboard([card]);
|
||||
});
|
||||
});
|
||||
|
||||
function createPivotableQuery(source: Card): StructuredQuery {
|
||||
return {
|
||||
...QSHelpers.createQ1Query(source),
|
||||
aggregation: [["count"]],
|
||||
breakout: [
|
||||
[
|
||||
"field",
|
||||
ORDERS.CREATED_AT,
|
||||
{
|
||||
"base-type": "type/DateTime",
|
||||
"temporal-unit": "month",
|
||||
},
|
||||
],
|
||||
[
|
||||
"field",
|
||||
PRODUCTS.CATEGORY,
|
||||
{
|
||||
"base-type": "type/Text",
|
||||
"source-field": ORDERS.PRODUCT_ID,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
it("does not use extra filtering stage for pivot tables", () => {
|
||||
cy.log("dashboard parameters mapping");
|
||||
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
cy.button("Save").click();
|
||||
|
||||
cy.log("filter modal");
|
||||
|
||||
H.getDashboardCard(QUESTION_PIVOT_INDEX)
|
||||
.findByTestId("legend-caption-title")
|
||||
.click();
|
||||
cy.wait("@datasetPivot");
|
||||
cy.button("Filter").click();
|
||||
H.modal().findByText("Summaries").should("not.exist");
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(QUESTION_PIVOT_INDEX, [
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
]);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(QUESTION_PIVOT_INDEX, [
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
]);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(QUESTION_PIVOT_INDEX, [
|
||||
["Base Orders Question", [...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"]],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,874 @@
|
||||
import { H } from "e2e/support";
|
||||
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
|
||||
import type {
|
||||
Card,
|
||||
ConcreteFieldReference,
|
||||
StructuredQuery,
|
||||
} from "metabase-types/api";
|
||||
|
||||
const { ORDERS, ORDERS_ID, PEOPLE, PRODUCTS, REVIEWS, REVIEWS_ID } =
|
||||
SAMPLE_DATABASE;
|
||||
|
||||
const CARD_HEIGHT = 4;
|
||||
const CARD_WIDTH = 12;
|
||||
|
||||
const DATE_PARAMETER = {
|
||||
name: "Date",
|
||||
slug: "date",
|
||||
id: "717a5624",
|
||||
type: "date/all-options",
|
||||
sectionId: "date",
|
||||
};
|
||||
|
||||
const TEXT_PARAMETER = {
|
||||
name: "Text",
|
||||
slug: "text",
|
||||
id: "76817b51",
|
||||
type: "string/=",
|
||||
sectionId: "string",
|
||||
};
|
||||
|
||||
const NUMBER_PARAMETER = {
|
||||
name: "Number",
|
||||
slug: "number",
|
||||
id: "f5944ad9",
|
||||
type: "number/=",
|
||||
sectionId: "number",
|
||||
};
|
||||
|
||||
const TOTAL_FIELD: ConcreteFieldReference = [
|
||||
"field",
|
||||
"TOTAL",
|
||||
{
|
||||
"base-type": "type/Float",
|
||||
},
|
||||
];
|
||||
|
||||
const TAX_FIELD: ConcreteFieldReference = [
|
||||
"field",
|
||||
"TAX",
|
||||
{
|
||||
"base-type": "type/Float",
|
||||
},
|
||||
];
|
||||
|
||||
const PRODUCT_ID_FIELD: ConcreteFieldReference = [
|
||||
"field",
|
||||
"PRODUCT_ID",
|
||||
{
|
||||
"base-type": "type/Float",
|
||||
},
|
||||
];
|
||||
|
||||
export const ORDERS_DATE_COLUMNS = ["Created At"];
|
||||
export const ORDERS_NUMBER_COLUMNS = [
|
||||
"Subtotal",
|
||||
"Tax",
|
||||
"Total",
|
||||
"Discount",
|
||||
"Quantity",
|
||||
];
|
||||
|
||||
export const PRODUCTS_DATE_COLUMNS = ["Created At"];
|
||||
export const PRODUCTS_TEXT_COLUMNS = ["Ean", "Title", "Category", "Vendor"];
|
||||
export const PRODUCTS_NUMBER_COLUMNS = ["Price", "Rating"];
|
||||
|
||||
export const PEOPLE_DATE_COLUMNS = ["Birth Date", "Created At"];
|
||||
export const PEOPLE_TEXT_COLUMNS = [
|
||||
"Address",
|
||||
"Email",
|
||||
"Password",
|
||||
"Name",
|
||||
"Source",
|
||||
];
|
||||
export const PEOPLE_NUMBER_COLUMNS = ["Longitude", "Latitude"];
|
||||
|
||||
export const REVIEWS_DATE_COLUMNS = ["Created At"];
|
||||
export const REVIEWS_TEXT_COLUMNS = ["Reviewer", "Body"];
|
||||
export const REVIEWS_NUMBER_COLUMNS = ["Rating"];
|
||||
|
||||
export const QUESTION_BASED_QUESTION_INDEX = 0;
|
||||
export const MODEL_BASED_QUESTION_INDEX = 1;
|
||||
export const QUESTION_BASED_MODEL_INDEX = 2;
|
||||
export const MODEL_BASED_MODEL_INDEX = 3;
|
||||
|
||||
export function createBaseQuestions() {
|
||||
H.createQuestion({
|
||||
type: "question",
|
||||
name: "Q0 Orders",
|
||||
description: "Question based on a database table",
|
||||
query: {
|
||||
"source-table": ORDERS_ID,
|
||||
},
|
||||
}).then(response => cy.wrap(response.body).as("ordersQuestion"));
|
||||
|
||||
cy.then(function () {
|
||||
H.createQuestion({
|
||||
type: "question",
|
||||
name: "Base Orders Question",
|
||||
query: {
|
||||
"source-table": `card__${this.ordersQuestion.id}`,
|
||||
},
|
||||
}).then(response => cy.wrap(response.body).as("baseQuestion"));
|
||||
|
||||
H.createQuestion({
|
||||
type: "model",
|
||||
name: "Base Orders Model",
|
||||
query: {
|
||||
"source-table": `card__${this.ordersQuestion.id}`,
|
||||
},
|
||||
}).then(response => cy.wrap(response.body).as("baseModel"));
|
||||
});
|
||||
}
|
||||
|
||||
// Q1 - join, custom column, no aggregations, no breakouts
|
||||
export function createQ1Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
"source-table": `card__${source.id}`,
|
||||
expressions: {
|
||||
Net: ["-", TOTAL_FIELD, TAX_FIELD],
|
||||
},
|
||||
joins: [
|
||||
{
|
||||
fields: "all",
|
||||
strategy: "left-join",
|
||||
alias: "Reviews - Product",
|
||||
condition: [
|
||||
"=",
|
||||
PRODUCT_ID_FIELD,
|
||||
[
|
||||
"field",
|
||||
"PRODUCT_ID",
|
||||
{
|
||||
"base-type": "type/Integer",
|
||||
"join-alias": "Reviews - Product",
|
||||
},
|
||||
],
|
||||
],
|
||||
"source-table": REVIEWS_ID,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q2 - join, custom column, 2 aggregations, no breakouts
|
||||
export function createQ2Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ1Query(source),
|
||||
aggregation: [["count"], ["sum", TOTAL_FIELD]],
|
||||
};
|
||||
}
|
||||
|
||||
// Q3 - join, custom column, no aggregations, 3 breakouts
|
||||
export function createQ3Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ1Query(source),
|
||||
breakout: [
|
||||
[
|
||||
"field",
|
||||
ORDERS.CREATED_AT,
|
||||
{
|
||||
"base-type": "type/DateTime",
|
||||
"temporal-unit": "month",
|
||||
},
|
||||
],
|
||||
[
|
||||
"field",
|
||||
PRODUCTS.CATEGORY,
|
||||
{
|
||||
"base-type": "type/Text",
|
||||
"source-field": ORDERS.PRODUCT_ID,
|
||||
},
|
||||
],
|
||||
[
|
||||
"field",
|
||||
PEOPLE.CREATED_AT,
|
||||
{
|
||||
"base-type": "type/DateTime",
|
||||
"temporal-unit": "year",
|
||||
"source-field": ORDERS.USER_ID,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q4 - join, custom column, 2 aggregations, 2 breakouts
|
||||
export function createQ4Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ3Query(source),
|
||||
aggregation: [["count"], ["sum", TOTAL_FIELD]],
|
||||
};
|
||||
}
|
||||
|
||||
// Q5 - Q4 + 2nd stage with join, custom column, no aggregations, no breakouts
|
||||
export function createQ5Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
"source-query": createQ4Query(source),
|
||||
expressions: {
|
||||
"5 * Count": [
|
||||
"*",
|
||||
5,
|
||||
[
|
||||
"field",
|
||||
"count",
|
||||
{
|
||||
"base-type": "type/Integer",
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
joins: [
|
||||
{
|
||||
strategy: "left-join",
|
||||
alias: "Reviews - Created At: Month",
|
||||
condition: [
|
||||
"=",
|
||||
[
|
||||
"field",
|
||||
"CREATED_AT",
|
||||
{
|
||||
"base-type": "type/DateTime",
|
||||
"temporal-unit": "month",
|
||||
},
|
||||
],
|
||||
[
|
||||
"field",
|
||||
REVIEWS.CREATED_AT,
|
||||
{
|
||||
"base-type": "type/DateTime",
|
||||
"temporal-unit": "month",
|
||||
"join-alias": "Reviews - Created At: Month",
|
||||
},
|
||||
],
|
||||
],
|
||||
"source-table": REVIEWS_ID,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q6 - Q4 + 2nd stage with join, custom column, 2 aggregations, no breakouts
|
||||
export function createQ6Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ5Query(source),
|
||||
aggregation: [
|
||||
["count"],
|
||||
[
|
||||
"sum",
|
||||
[
|
||||
"field",
|
||||
REVIEWS.RATING,
|
||||
{
|
||||
"base-type": "type/Integer",
|
||||
"join-alias": "Reviews - Created At: Month",
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q7 - Q4 + 2nd stage with join, custom column, no aggregations, 2 breakouts
|
||||
export function createQ7Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ5Query(source),
|
||||
breakout: [
|
||||
[
|
||||
"field",
|
||||
REVIEWS.REVIEWER,
|
||||
{
|
||||
"base-type": "type/Text",
|
||||
"join-alias": "Reviews - Created At: Month",
|
||||
},
|
||||
],
|
||||
[
|
||||
"field",
|
||||
"PRODUCTS__via__PRODUCT_ID__CATEGORY",
|
||||
{
|
||||
"base-type": "type/Text",
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q8 - Q4 + 2nd stage with join, custom column, 2 aggregations, 2 breakouts
|
||||
export function createQ8Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ7Query(source),
|
||||
aggregation: [
|
||||
["count"],
|
||||
[
|
||||
"sum",
|
||||
[
|
||||
"field",
|
||||
REVIEWS.RATING,
|
||||
{
|
||||
"base-type": "type/Integer",
|
||||
"join-alias": "Reviews - Created At: Month",
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q9 - Q8 + 3rd stage with 1 aggregation
|
||||
export function createQ9Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
"source-query": createQ8Query(source),
|
||||
aggregation: [["count"]],
|
||||
};
|
||||
}
|
||||
|
||||
type CreateQuery = (source: Card) => StructuredQuery;
|
||||
|
||||
export function createAndVisitDashboardWithCardMatrix(
|
||||
createQuery: CreateQuery,
|
||||
) {
|
||||
cy.then(function () {
|
||||
H.createQuestion({
|
||||
type: "question",
|
||||
query: createQuery(this.baseQuestion),
|
||||
name: "Question-based Question",
|
||||
}).then(response => cy.wrap(response.body).as("qbq"));
|
||||
|
||||
H.createQuestion({
|
||||
type: "question",
|
||||
query: createQuery(this.baseModel),
|
||||
name: "Model-based Question",
|
||||
}).then(response => cy.wrap(response.body).as("mbq"));
|
||||
|
||||
H.createQuestion({
|
||||
type: "model",
|
||||
name: "Question-based Model",
|
||||
query: createQuery(this.baseQuestion),
|
||||
}).then(response => cy.wrap(response.body).as("qbm"));
|
||||
|
||||
H.createQuestion({
|
||||
type: "model",
|
||||
name: "Model-based Model",
|
||||
query: createQuery(this.baseModel),
|
||||
}).then(response => cy.wrap(response.body).as("mbm"));
|
||||
});
|
||||
|
||||
cy.then(function () {
|
||||
const cards = [this.qbq, this.mbq, this.qbm, this.mbm];
|
||||
createAndVisitDashboard(cards);
|
||||
});
|
||||
}
|
||||
|
||||
export function createAndVisitDashboard(cards: Card[]) {
|
||||
let id = 0;
|
||||
const getNextId = () => --id;
|
||||
|
||||
H.createDashboardWithTabs({
|
||||
enable_embedding: true,
|
||||
embedding_params: {
|
||||
[DATE_PARAMETER.slug]: "enabled",
|
||||
[TEXT_PARAMETER.slug]: "enabled",
|
||||
[NUMBER_PARAMETER.slug]: "enabled",
|
||||
},
|
||||
parameters: [DATE_PARAMETER, TEXT_PARAMETER, NUMBER_PARAMETER],
|
||||
dashcards: [
|
||||
...cards.map((card, index) => ({
|
||||
id: getNextId(),
|
||||
size_x: CARD_WIDTH,
|
||||
size_y: CARD_HEIGHT,
|
||||
row: CARD_HEIGHT * Math.floor(index / 2),
|
||||
col: index % 2 === 0 ? 0 : CARD_WIDTH,
|
||||
card,
|
||||
card_id: card.id,
|
||||
})),
|
||||
],
|
||||
}).then(dashboard => {
|
||||
H.visitDashboard(dashboard.id);
|
||||
cy.wrap(dashboard.id).as("dashboardId");
|
||||
cy.wait("@getDashboard");
|
||||
});
|
||||
}
|
||||
|
||||
export function setup1stStageExplicitJoinFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Reviewer", 0).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Reviewer", 0).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
}
|
||||
|
||||
export function apply1stStageExplicitJoinFilter() {
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByPlaceholderText("Search by Reviewer").type("abe.gorczany");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
}
|
||||
|
||||
export function setup1stStageImplicitJoinFromSourceFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Number").click();
|
||||
H.sidebar().findByText("Filter operator").next().click();
|
||||
H.popover().findByText("Between").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Price", 0).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Price", 0).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(0).type("0");
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(1).type("16");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup1stStageImplicitJoinFromJoinFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Category", 1).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Category", 1).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByLabelText("Gadget").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup1stStageCustomColumnFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Number").click();
|
||||
H.sidebar().findByText("Filter operator").next().click();
|
||||
H.popover().findByText("Between").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Net").click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Net").click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(0).type("0");
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(1).type("20");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup1stStageAggregationFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Number").click();
|
||||
H.sidebar().findByText("Filter operator").next().click();
|
||||
H.popover().findByText("Between").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Count").click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Count").click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(0).type("0");
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(1).type("2");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup1stStageBreakoutFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Category", 1).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Category", 1).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByLabelText("Gadget").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup2ndStageExplicitJoinFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Reviewer", 1).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Reviewer", 1).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByPlaceholderText("Search by Reviewer").type("abe.gorczany");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup2ndStageCustomColumnFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Number").click();
|
||||
H.sidebar().findByText("Filter operator").next().click();
|
||||
H.popover().findByText("Between").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("5 * Count").click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("5 * Count").click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
}
|
||||
|
||||
export function apply2ndStageCustomColumnFilter() {
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(0).type("0");
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(1).type("20");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
}
|
||||
|
||||
export function setup2ndStageAggregationFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Number").click();
|
||||
H.sidebar().findByText("Filter operator").next().click();
|
||||
H.popover().findByText("Between").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Count", 1).click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Count", 1).click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(2).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Count").click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(3).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Count").click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
}
|
||||
|
||||
export function apply2ndStageAggregationFilter() {
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(0).type("0");
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(1).type("2");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
}
|
||||
|
||||
export function setup2ndStageBreakoutFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Category", 2).click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Category", 2).click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(2).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Products Via Product ID Category").click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(3).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Products Via Product ID Category").click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
}
|
||||
|
||||
export function apply2ndStageBreakoutFilter() {
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByLabelText("Gadget").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
}
|
||||
|
||||
export function getFilter(name: string) {
|
||||
return cy.findByTestId("fixed-width-filters").findByText(name);
|
||||
}
|
||||
|
||||
export function getPopoverList() {
|
||||
return cy.findAllByRole("grid").eq(0);
|
||||
}
|
||||
|
||||
export function getPopoverItems() {
|
||||
return cy.get("[data-element-id=list-section]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index if more than 1 item with the same name is visible, specify which one should be used
|
||||
*/
|
||||
export function getPopoverItem(name: string, index = 0) {
|
||||
/**
|
||||
* Without scrollIntoView() the popover may scroll automatically to a different
|
||||
* place when clicking the item (unclear why).
|
||||
*/
|
||||
return cy.findAllByText(name).eq(index).scrollIntoView();
|
||||
}
|
||||
|
||||
export function dismissToast() {
|
||||
cy.findByTestId("toast-undo")
|
||||
.findByRole("img", { name: /close icon/ })
|
||||
.click();
|
||||
}
|
||||
|
||||
export function clickAway() {
|
||||
cy.get("body").click(0, 0);
|
||||
}
|
||||
|
||||
export function goBackToDashboard() {
|
||||
cy.findByLabelText("Back to Test Dashboard").click();
|
||||
cy.wait("@getDashboard");
|
||||
}
|
||||
|
||||
export function getDashboardId(): Cypress.Chainable<number> {
|
||||
return cy
|
||||
.get("@dashboardId")
|
||||
.then(dashboardId => dashboardId as unknown as number);
|
||||
}
|
||||
|
||||
export function waitForPublicDashboardData() {
|
||||
// tests with public dashboards always have 4 dashcards
|
||||
cy.wait([
|
||||
"@publicDashboardData",
|
||||
"@publicDashboardData",
|
||||
"@publicDashboardData",
|
||||
"@publicDashboardData",
|
||||
]);
|
||||
}
|
||||
|
||||
export function waitForEmbeddedDashboardData() {
|
||||
// tests with embedded dashboards always have 4 dashcards
|
||||
cy.wait([
|
||||
"@embeddedDashboardData",
|
||||
"@embeddedDashboardData",
|
||||
"@embeddedDashboardData",
|
||||
"@embeddedDashboardData",
|
||||
]);
|
||||
}
|
||||
|
||||
export function verifyDashcardMappingOptions(
|
||||
dashcardIndex: number,
|
||||
sections: MappingSection[],
|
||||
) {
|
||||
H.getDashboardCard(dashcardIndex).findByText("Select…").click();
|
||||
verifyPopoverMappingOptions(sections);
|
||||
clickAway();
|
||||
}
|
||||
|
||||
export function verifyNoDashcardMappingOptions(dashcardIndex: number) {
|
||||
H.getDashboardCard(dashcardIndex)
|
||||
.findByText("No valid fields")
|
||||
.should("be.visible");
|
||||
|
||||
H.getDashboardCard(dashcardIndex).findByText("No valid fields").realHover();
|
||||
cy.findByRole("tooltip")
|
||||
.findByText(
|
||||
"This card doesn't have any fields or parameters that can be mapped to this parameter type.",
|
||||
)
|
||||
.should("be.visible");
|
||||
}
|
||||
|
||||
type SectionName = string;
|
||||
type ColumnName = string;
|
||||
type MappingSection = [SectionName, ColumnName[]];
|
||||
|
||||
export function verifyPopoverMappingOptions(sections: MappingSection[]) {
|
||||
const expectedItemsCount = sections.reduce(
|
||||
(sum, [sectionName, columnNames]) =>
|
||||
sum + [sectionName, ...columnNames].length,
|
||||
0,
|
||||
);
|
||||
|
||||
H.popover().within(() => {
|
||||
getPopoverItems().then($items => {
|
||||
let index = 0;
|
||||
|
||||
for (const [sectionName, columnNames] of sections) {
|
||||
const item = cy.wrap($items[index]);
|
||||
item.scrollIntoView(); // the list is virtualized, we need to keep scrolling to see all the items
|
||||
item.should("have.text", sectionName);
|
||||
++index;
|
||||
|
||||
for (const columnName of columnNames) {
|
||||
const item = cy.wrap($items[index]);
|
||||
item.scrollIntoView();
|
||||
item.findByLabelText(columnName).should("be.visible");
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
expect($items.length).to.eq(expectedItemsCount);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function verifyDashcardRowsCount({
|
||||
dashcardIndex,
|
||||
dashboardCount,
|
||||
queryBuilderCount,
|
||||
}: {
|
||||
dashcardIndex: number;
|
||||
dashboardCount: string;
|
||||
queryBuilderCount: string;
|
||||
}) {
|
||||
H.getDashboardCard(dashcardIndex)
|
||||
.findByText(dashboardCount)
|
||||
.should("be.visible");
|
||||
H.getDashboardCard(dashcardIndex)
|
||||
.findByTestId("legend-caption-title")
|
||||
.click();
|
||||
cy.wait("@dataset");
|
||||
cy.findByTestId("question-row-count").should("have.text", queryBuilderCount);
|
||||
}
|
||||
|
||||
export function verifyDashcardCellValues({
|
||||
dashcardIndex,
|
||||
values,
|
||||
}: {
|
||||
dashcardIndex: number;
|
||||
values: string[];
|
||||
}) {
|
||||
for (let valueIndex = 0; valueIndex < values.length; ++valueIndex) {
|
||||
const value = values[valueIndex];
|
||||
|
||||
H.getDashboardCard(dashcardIndex)
|
||||
.findByTestId("table-row")
|
||||
.findAllByTestId("cell-data")
|
||||
.eq(valueIndex)
|
||||
.should("have.text", value);
|
||||
}
|
||||
|
||||
H.getDashboardCard(dashcardIndex)
|
||||
.findByTestId("legend-caption-title")
|
||||
.click();
|
||||
cy.wait("@dataset");
|
||||
|
||||
for (let valueIndex = 0; valueIndex < values.length; ++valueIndex) {
|
||||
const value = values[valueIndex];
|
||||
const cellIndex = valueIndex + values.length; // values.length to skip header row
|
||||
|
||||
cy.findAllByTestId("cell-data").eq(cellIndex).should("have.text", value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,723 @@
|
||||
import { H } from "e2e/support";
|
||||
|
||||
import * as QSHelpers from "./shared/dashboard-filters-query-stages";
|
||||
|
||||
/**
|
||||
* Empty section title element is rendered.
|
||||
* TODO: https://github.com/metabase/metabase/issues/47218
|
||||
*/
|
||||
const NAMELESS_SECTION = "";
|
||||
|
||||
/**
|
||||
* Abbreviations used for card aliases in this test suite:
|
||||
* qbq = question-based question
|
||||
* qbm = question-based model
|
||||
* mbq = model-based question
|
||||
* mbm = model-based model
|
||||
*/
|
||||
describe("scenarios > dashboard > filters > query stages", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
QSHelpers.createBaseQuestions();
|
||||
|
||||
cy.intercept("POST", "/api/dataset").as("dataset");
|
||||
cy.intercept("GET", "/api/dashboard/**").as("getDashboard");
|
||||
cy.intercept("PUT", "/api/dashboard/**").as("updateDashboard");
|
||||
cy.intercept("POST", "/api/dashboard/*/dashcard/*/card/*/query").as(
|
||||
"dashboardData",
|
||||
);
|
||||
cy.intercept("GET", "/api/public/dashboard/*/dashcard/*/card/*").as(
|
||||
"publicDashboardData",
|
||||
);
|
||||
cy.intercept("GET", "/api/embed/dashboard/*/dashcard/*/card/*").as(
|
||||
"embeddedDashboardData",
|
||||
);
|
||||
});
|
||||
|
||||
describe("1-stage queries", () => {
|
||||
describe("Q1 - join, custom column, no aggregations, no breakouts", () => {
|
||||
beforeEach(() => {
|
||||
QSHelpers.createAndVisitDashboardWithCardMatrix(
|
||||
QSHelpers.createQ1Query,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows to map to all relevant columns", () => {
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Model", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Question-based Model",
|
||||
[
|
||||
...QSHelpers.ORDERS_DATE_COLUMNS,
|
||||
"Reviews - Product → Created At",
|
||||
],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_DATE_COLUMNS],
|
||||
["Reviews - Product → Product", QSHelpers.PRODUCTS_DATE_COLUMNS],
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Model-based Model",
|
||||
[
|
||||
...QSHelpers.ORDERS_DATE_COLUMNS,
|
||||
"Reviews - Product → Created At",
|
||||
],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_DATE_COLUMNS],
|
||||
["Reviews - Product → Product", QSHelpers.PRODUCTS_DATE_COLUMNS],
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Question-based Model",
|
||||
["Reviews - Product → Reviewer", "Reviews - Product → Body"],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_TEXT_COLUMNS],
|
||||
["Reviews - Product → Product", QSHelpers.PRODUCTS_TEXT_COLUMNS],
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Model-based Model",
|
||||
["Reviews - Product → Reviewer", "Reviews - Product → Body"],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_TEXT_COLUMNS],
|
||||
["Reviews - Product → Product", QSHelpers.PRODUCTS_TEXT_COLUMNS],
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Question",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
],
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Model",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
],
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Question-based Model",
|
||||
[
|
||||
...QSHelpers.ORDERS_NUMBER_COLUMNS,
|
||||
"Net",
|
||||
"Reviews - Product → Rating",
|
||||
],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Reviews - Product → Product",
|
||||
QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
"Model-based Model",
|
||||
[
|
||||
...QSHelpers.ORDERS_NUMBER_COLUMNS,
|
||||
"Net",
|
||||
"Reviews - Product → Rating",
|
||||
],
|
||||
],
|
||||
["Product", QSHelpers.PRODUCTS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Reviews - Product → Product",
|
||||
QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Q2 - join, custom column, 2 aggregations, no breakouts", () => {
|
||||
beforeEach(() => {
|
||||
QSHelpers.createAndVisitDashboardWithCardMatrix(
|
||||
QSHelpers.createQ2Query,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows to map to all relevant columns (metabase#47184)", () => {
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Model", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(2);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(3);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(2);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(3);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Question",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Model",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count", "Sum of Total"]]],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count", "Sum of Total"]]],
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Q3 - join, custom column, no aggregations, 3 breakouts", () => {
|
||||
beforeEach(() => {
|
||||
QSHelpers.createAndVisitDashboardWithCardMatrix(
|
||||
QSHelpers.createQ3Query,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows to map to all relevant columns", () => {
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Model", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
NAMELESS_SECTION,
|
||||
["Created At: Month", "User → Created At: Year"],
|
||||
],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
NAMELESS_SECTION,
|
||||
["Created At: Month", "User → Created At: Year"],
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Product → Category"]]],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Product → Category"]]],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Question",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Model",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(2);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(3);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Q4 - join, custom column, 2 aggregations, 2 breakouts (metabase#47184)", () => {
|
||||
beforeEach(() => {
|
||||
QSHelpers.createAndVisitDashboardWithCardMatrix(
|
||||
QSHelpers.createQ4Query,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows to map to all relevant columns", () => {
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Model", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
NAMELESS_SECTION,
|
||||
["Created At: Month", "User → Created At: Year"],
|
||||
],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[
|
||||
[
|
||||
NAMELESS_SECTION,
|
||||
["Created At: Month", "User → Created At: Year"],
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Product → Category"]]],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Product → Category"]]],
|
||||
);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Question",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
["Summaries", ["Count", "Sum of Total"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Model",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
["Summaries", ["Count", "Sum of Total"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count", "Sum of Total"]]],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count", "Sum of Total"]]],
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,426 @@
|
||||
import { H } from "e2e/support";
|
||||
|
||||
import * as QSHelpers from "./shared/dashboard-filters-query-stages";
|
||||
|
||||
/**
|
||||
* Empty section title element is rendered.
|
||||
* TODO: https://github.com/metabase/metabase/issues/47218
|
||||
*/
|
||||
const NAMELESS_SECTION = "";
|
||||
|
||||
/**
|
||||
* Abbreviations used for card aliases in this test suite:
|
||||
* qbq = question-based question
|
||||
* qbm = question-based model
|
||||
* mbq = model-based question
|
||||
* mbm = model-based model
|
||||
*/
|
||||
describe("scenarios > dashboard > filters > query stages", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
QSHelpers.createBaseQuestions();
|
||||
|
||||
cy.intercept("POST", "/api/dataset").as("dataset");
|
||||
cy.intercept("GET", "/api/dashboard/**").as("getDashboard");
|
||||
cy.intercept("PUT", "/api/dashboard/**").as("updateDashboard");
|
||||
cy.intercept("POST", "/api/dashboard/*/dashcard/*/card/*/query").as(
|
||||
"dashboardData",
|
||||
);
|
||||
cy.intercept("GET", "/api/public/dashboard/*/dashcard/*/card/*").as(
|
||||
"publicDashboardData",
|
||||
);
|
||||
cy.intercept("GET", "/api/embed/dashboard/*/dashcard/*/card/*").as(
|
||||
"embeddedDashboardData",
|
||||
);
|
||||
});
|
||||
|
||||
describe("3-stage queries", () => {
|
||||
describe("Q9 - Q8 + 3rd stage with 1 aggregation", () => {
|
||||
beforeEach(() => {
|
||||
QSHelpers.createAndVisitDashboardWithCardMatrix(
|
||||
QSHelpers.createQ9Query,
|
||||
);
|
||||
});
|
||||
|
||||
it("allows to map to all relevant columns", () => {
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_DATE_COLUMNS,
|
||||
...QSHelpers.REVIEWS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
["Base Orders Model", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_DATE_COLUMNS,
|
||||
...QSHelpers.REVIEWS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
["Summaries", ["Created At: Month", "Created At: Year"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_TEXT_COLUMNS,
|
||||
...QSHelpers.REVIEWS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
["Summaries (2)", ["Reviewer", "Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_TEXT_COLUMNS,
|
||||
...QSHelpers.REVIEWS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
["Summaries", ["Category"]],
|
||||
["Summaries (2)", ["Reviewer", "Category"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
);
|
||||
QSHelpers.verifyNoDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Question",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_NUMBER_COLUMNS,
|
||||
...QSHelpers.REVIEWS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
["Summaries", ["Count", "Sum of Total", "5 * Count"]],
|
||||
["Summaries (2)", ["Count", "Sum of Rating"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_QUESTION_INDEX,
|
||||
[
|
||||
[
|
||||
"Base Orders Model",
|
||||
[...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"],
|
||||
],
|
||||
[
|
||||
"Reviews",
|
||||
[
|
||||
...QSHelpers.REVIEWS_NUMBER_COLUMNS,
|
||||
...QSHelpers.REVIEWS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
["Summaries", ["Count", "Sum of Total", "5 * Count"]],
|
||||
["Summaries (2)", ["Count", "Sum of Rating"]],
|
||||
],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.QUESTION_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count"]]],
|
||||
);
|
||||
QSHelpers.verifyDashcardMappingOptions(
|
||||
QSHelpers.MODEL_BASED_MODEL_INDEX,
|
||||
[[NAMELESS_SECTION, ["Count"]]],
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
describe("applies filter to the the dashcard and allows to drill via dashcard header", () => {
|
||||
it("1st stage explicit join", () => {
|
||||
QSHelpers.setup1stStageExplicitJoinFilter();
|
||||
QSHelpers.apply1stStageExplicitJoinFilter();
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["953"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["953"],
|
||||
});
|
||||
});
|
||||
|
||||
it("1st stage implicit join (data source)", () => {
|
||||
QSHelpers.setup1stStageImplicitJoinFromSourceFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["1,044"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["1,044"],
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: https://github.com/metabase/metabase/issues/46774
|
||||
it.skip("1st stage implicit join (joined data source)", () => {
|
||||
QSHelpers.setup1stStageImplicitJoinFromJoinFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["1,077"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["1,077"],
|
||||
});
|
||||
});
|
||||
|
||||
it("1st stage custom column", () => {
|
||||
QSHelpers.setup1stStageCustomColumnFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["688"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["688"],
|
||||
});
|
||||
});
|
||||
|
||||
it("1st stage aggregation", () => {
|
||||
QSHelpers.setup1stStageAggregationFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["3"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["3"],
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: https://github.com/metabase/metabase/issues/46774
|
||||
it.skip("1st stage breakout", () => {
|
||||
QSHelpers.setup1stStageBreakoutFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["1,077"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["1,077"],
|
||||
});
|
||||
});
|
||||
|
||||
it("2nd stage explicit join", () => {
|
||||
QSHelpers.setup2ndStageExplicitJoinFilter();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["4"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["4"],
|
||||
});
|
||||
});
|
||||
|
||||
it("2nd stage custom column", () => {
|
||||
QSHelpers.setup2ndStageCustomColumnFilter();
|
||||
QSHelpers.apply2ndStageCustomColumnFilter();
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["31"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["31"],
|
||||
});
|
||||
});
|
||||
|
||||
it("2nd stage aggregation", () => {
|
||||
QSHelpers.setup2ndStageAggregationFilter();
|
||||
QSHelpers.apply2ndStageAggregationFilter();
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["6"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["6"],
|
||||
});
|
||||
});
|
||||
|
||||
it("2nd stage breakout", () => {
|
||||
H.editDashboard();
|
||||
|
||||
QSHelpers.getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
QSHelpers.getPopoverList().scrollTo("bottom");
|
||||
QSHelpers.getPopoverItem("Category", 2).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
QSHelpers.getPopoverList().scrollTo("bottom");
|
||||
QSHelpers.getPopoverItem("Category", 2).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByLabelText("Gadget").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 0,
|
||||
values: ["1,077"],
|
||||
});
|
||||
|
||||
QSHelpers.goBackToDashboard();
|
||||
|
||||
QSHelpers.verifyDashcardCellValues({
|
||||
dashcardIndex: 1,
|
||||
values: ["1,077"],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,251 @@
|
||||
import { H } from "e2e/support";
|
||||
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
|
||||
import type { Card, StructuredQuery } from "metabase-types/api";
|
||||
|
||||
import * as QSHelpers from "./shared/dashboard-filters-query-stages";
|
||||
|
||||
const { ORDERS, PRODUCTS } = SAMPLE_DATABASE;
|
||||
|
||||
describe("scenarios > dashboard > filters > query stages + temporal unit parameters", () => {
|
||||
describe("applies filter to the the dashcard and allows to drill via dashcard header", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
|
||||
cy.intercept("POST", "/api/dataset").as("dataset");
|
||||
});
|
||||
|
||||
it("1st stage explicit join + unit of time parameter", () => {
|
||||
H.createDashboard(
|
||||
{
|
||||
name: "My new dashboard",
|
||||
},
|
||||
{ wrapId: true, idAlias: "myNewDash" },
|
||||
);
|
||||
|
||||
cy.get("@myNewDash").then((dashId: number | any) => {
|
||||
H.visitDashboard(dashId);
|
||||
});
|
||||
|
||||
H.startNewQuestion();
|
||||
|
||||
H.entityPickerModal().within(() => {
|
||||
H.entityPickerModalTab("Tables").click();
|
||||
H.entityPickerModalItem(2, "Orders").click();
|
||||
});
|
||||
|
||||
H.getNotebookStep("filter")
|
||||
.findByText("Add filters to narrow your answer")
|
||||
.click();
|
||||
H.popover().within(() => {
|
||||
cy.findByText("Orders").click();
|
||||
cy.findByText("Product").click();
|
||||
cy.findByText("Category").click();
|
||||
cy.findByLabelText("Gizmo").click();
|
||||
cy.findByLabelText("Doohickey").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
|
||||
H.getNotebookStep("summarize")
|
||||
.findByText("Pick a function or metric")
|
||||
.click();
|
||||
H.popover().findByText("Count of rows").click();
|
||||
H.getNotebookStep("summarize").icon("add").click();
|
||||
H.popover().within(() => {
|
||||
cy.findByText("Sum of ...").click();
|
||||
cy.findByText("Total").click();
|
||||
});
|
||||
|
||||
H.getNotebookStep("summarize")
|
||||
.findByText("Pick a column to group by")
|
||||
.click();
|
||||
H.popover()
|
||||
.findByLabelText("Created At")
|
||||
.findByLabelText("Temporal bucket")
|
||||
.click();
|
||||
H.popover().last().findByText("Week").click();
|
||||
H.getNotebookStep("summarize")
|
||||
.findByTestId("breakout-step")
|
||||
.icon("add")
|
||||
.click();
|
||||
H.popover().within(() => {
|
||||
cy.findByText("Orders").click();
|
||||
cy.findByText("Product").click();
|
||||
cy.findByText("Category").click();
|
||||
});
|
||||
|
||||
cy.findAllByTestId("action-buttons").last().button("Summarize").click();
|
||||
H.popover().findByText("Count of rows").click();
|
||||
H.getNotebookStep("summarize", { stage: 1 })
|
||||
.findByText("Pick a column to group by")
|
||||
.click();
|
||||
H.popover().findByLabelText("Created At: Week").click();
|
||||
|
||||
H.visualize(); // need to visualize because startNewQuestion does not set "display" property on a card
|
||||
cy.wait("@dataset");
|
||||
H.saveQuestion("test"); // added to new dash automatically
|
||||
|
||||
cy.findByLabelText("Add a filter or parameter").click();
|
||||
H.popover().findByText("Text or Category").click();
|
||||
H.getDashboardCard().findByText("Select…").click();
|
||||
cy.findAllByText("Category").first().click();
|
||||
|
||||
cy.findByLabelText("Add a filter or parameter").click();
|
||||
H.popover().findByText("Time grouping").click();
|
||||
H.getDashboardCard().findByText("Select…").click();
|
||||
H.popover().findByText("Created At: Week").click();
|
||||
|
||||
H.saveDashboard();
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByText("Gizmo").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
|
||||
H.filterWidget().eq(1).click();
|
||||
H.popover().findByText("Quarter").click();
|
||||
|
||||
H.getDashboardCard().findByText("Q1 2023").should("be.visible");
|
||||
H.getDashboardCard().findByTestId("legend-caption-title").click();
|
||||
cy.wait("@dataset");
|
||||
|
||||
// assert that new filter was applied
|
||||
cy.findByTestId("qb-filters-panel").within(() => {
|
||||
cy.findByText("Product → Category is 2 selections").should(
|
||||
"be.visible",
|
||||
);
|
||||
cy.findByText("Product → Category is Gizmo").should("be.visible");
|
||||
});
|
||||
|
||||
// assert that temporal unit parameter was applied
|
||||
cy.findByTestId("chart-container")
|
||||
.findByText("Q1 2023")
|
||||
.should("be.visible");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("pivot tables", () => {
|
||||
const QUESTION_PIVOT_INDEX = 0;
|
||||
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
QSHelpers.createBaseQuestions();
|
||||
|
||||
cy.intercept("POST", "/api/dataset").as("dataset");
|
||||
cy.intercept("POST", "/api/dataset/pivot").as("datasetPivot");
|
||||
cy.intercept("GET", "/api/dashboard/**").as("getDashboard");
|
||||
cy.intercept("POST", "/api/card/*/query").as("cardQuery");
|
||||
|
||||
cy.then(function () {
|
||||
H.createQuestion({
|
||||
type: "question",
|
||||
query: createPivotableQuery(this.baseQuestion),
|
||||
name: "Question - pivot viz",
|
||||
display: "pivot",
|
||||
}).then(response => {
|
||||
const card = response.body;
|
||||
QSHelpers.createAndVisitDashboard([card]);
|
||||
});
|
||||
});
|
||||
|
||||
function createPivotableQuery(source: Card): StructuredQuery {
|
||||
return {
|
||||
...QSHelpers.createQ1Query(source),
|
||||
aggregation: [["count"]],
|
||||
breakout: [
|
||||
[
|
||||
"field",
|
||||
ORDERS.CREATED_AT,
|
||||
{
|
||||
"base-type": "type/DateTime",
|
||||
"temporal-unit": "month",
|
||||
},
|
||||
],
|
||||
[
|
||||
"field",
|
||||
PRODUCTS.CATEGORY,
|
||||
{
|
||||
"base-type": "type/Text",
|
||||
"source-field": ORDERS.PRODUCT_ID,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
it("does not use extra filtering stage for pivot tables", () => {
|
||||
cy.log("dashboard parameters mapping");
|
||||
|
||||
H.editDashboard();
|
||||
|
||||
cy.log("## date columns");
|
||||
QSHelpers.getFilter("Date").click();
|
||||
verifyDateMappingOptions();
|
||||
|
||||
cy.log("## text columns");
|
||||
QSHelpers.getFilter("Text").click();
|
||||
verifyTextMappingOptions();
|
||||
|
||||
cy.log("## number columns");
|
||||
QSHelpers.getFilter("Number").click();
|
||||
verifyNumberMappingOptions();
|
||||
|
||||
cy.button("Save").click();
|
||||
|
||||
cy.log("filter modal");
|
||||
|
||||
H.getDashboardCard(QUESTION_PIVOT_INDEX)
|
||||
.findByTestId("legend-caption-title")
|
||||
.click();
|
||||
cy.wait("@datasetPivot");
|
||||
cy.button("Filter").click();
|
||||
H.modal().findByText("Summaries").should("not.exist");
|
||||
|
||||
function verifyDateMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(QUESTION_PIVOT_INDEX, [
|
||||
["Base Orders Question", QSHelpers.ORDERS_DATE_COLUMNS],
|
||||
["Reviews", QSHelpers.REVIEWS_DATE_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_DATE_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_DATE_COLUMNS],
|
||||
]);
|
||||
}
|
||||
|
||||
function verifyTextMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(QUESTION_PIVOT_INDEX, [
|
||||
["Reviews", QSHelpers.REVIEWS_TEXT_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_TEXT_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_TEXT_COLUMNS],
|
||||
]);
|
||||
}
|
||||
|
||||
function verifyNumberMappingOptions() {
|
||||
QSHelpers.verifyDashcardMappingOptions(QUESTION_PIVOT_INDEX, [
|
||||
["Base Orders Question", [...QSHelpers.ORDERS_NUMBER_COLUMNS, "Net"]],
|
||||
["Reviews", QSHelpers.REVIEWS_NUMBER_COLUMNS],
|
||||
[
|
||||
"Product",
|
||||
[
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
...QSHelpers.PRODUCTS_NUMBER_COLUMNS,
|
||||
],
|
||||
], // TODO: https://github.com/metabase/metabase/issues/46845
|
||||
["User", QSHelpers.PEOPLE_NUMBER_COLUMNS],
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,874 @@
|
||||
import { H } from "e2e/support";
|
||||
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
|
||||
import type {
|
||||
Card,
|
||||
ConcreteFieldReference,
|
||||
StructuredQuery,
|
||||
} from "metabase-types/api";
|
||||
|
||||
const { ORDERS, ORDERS_ID, PEOPLE, PRODUCTS, REVIEWS, REVIEWS_ID } =
|
||||
SAMPLE_DATABASE;
|
||||
|
||||
const CARD_HEIGHT = 4;
|
||||
const CARD_WIDTH = 12;
|
||||
|
||||
const DATE_PARAMETER = {
|
||||
name: "Date",
|
||||
slug: "date",
|
||||
id: "717a5624",
|
||||
type: "date/all-options",
|
||||
sectionId: "date",
|
||||
};
|
||||
|
||||
const TEXT_PARAMETER = {
|
||||
name: "Text",
|
||||
slug: "text",
|
||||
id: "76817b51",
|
||||
type: "string/=",
|
||||
sectionId: "string",
|
||||
};
|
||||
|
||||
const NUMBER_PARAMETER = {
|
||||
name: "Number",
|
||||
slug: "number",
|
||||
id: "f5944ad9",
|
||||
type: "number/=",
|
||||
sectionId: "number",
|
||||
};
|
||||
|
||||
const TOTAL_FIELD: ConcreteFieldReference = [
|
||||
"field",
|
||||
"TOTAL",
|
||||
{
|
||||
"base-type": "type/Float",
|
||||
},
|
||||
];
|
||||
|
||||
const TAX_FIELD: ConcreteFieldReference = [
|
||||
"field",
|
||||
"TAX",
|
||||
{
|
||||
"base-type": "type/Float",
|
||||
},
|
||||
];
|
||||
|
||||
const PRODUCT_ID_FIELD: ConcreteFieldReference = [
|
||||
"field",
|
||||
"PRODUCT_ID",
|
||||
{
|
||||
"base-type": "type/Float",
|
||||
},
|
||||
];
|
||||
|
||||
export const ORDERS_DATE_COLUMNS = ["Created At"];
|
||||
export const ORDERS_NUMBER_COLUMNS = [
|
||||
"Subtotal",
|
||||
"Tax",
|
||||
"Total",
|
||||
"Discount",
|
||||
"Quantity",
|
||||
];
|
||||
|
||||
export const PRODUCTS_DATE_COLUMNS = ["Created At"];
|
||||
export const PRODUCTS_TEXT_COLUMNS = ["Ean", "Title", "Category", "Vendor"];
|
||||
export const PRODUCTS_NUMBER_COLUMNS = ["Price", "Rating"];
|
||||
|
||||
export const PEOPLE_DATE_COLUMNS = ["Birth Date", "Created At"];
|
||||
export const PEOPLE_TEXT_COLUMNS = [
|
||||
"Address",
|
||||
"Email",
|
||||
"Password",
|
||||
"Name",
|
||||
"Source",
|
||||
];
|
||||
export const PEOPLE_NUMBER_COLUMNS = ["Longitude", "Latitude"];
|
||||
|
||||
export const REVIEWS_DATE_COLUMNS = ["Created At"];
|
||||
export const REVIEWS_TEXT_COLUMNS = ["Reviewer", "Body"];
|
||||
export const REVIEWS_NUMBER_COLUMNS = ["Rating"];
|
||||
|
||||
export const QUESTION_BASED_QUESTION_INDEX = 0;
|
||||
export const MODEL_BASED_QUESTION_INDEX = 1;
|
||||
export const QUESTION_BASED_MODEL_INDEX = 2;
|
||||
export const MODEL_BASED_MODEL_INDEX = 3;
|
||||
|
||||
export function createBaseQuestions() {
|
||||
H.createQuestion({
|
||||
type: "question",
|
||||
name: "Q0 Orders",
|
||||
description: "Question based on a database table",
|
||||
query: {
|
||||
"source-table": ORDERS_ID,
|
||||
},
|
||||
}).then(response => cy.wrap(response.body).as("ordersQuestion"));
|
||||
|
||||
cy.then(function () {
|
||||
H.createQuestion({
|
||||
type: "question",
|
||||
name: "Base Orders Question",
|
||||
query: {
|
||||
"source-table": `card__${this.ordersQuestion.id}`,
|
||||
},
|
||||
}).then(response => cy.wrap(response.body).as("baseQuestion"));
|
||||
|
||||
H.createQuestion({
|
||||
type: "model",
|
||||
name: "Base Orders Model",
|
||||
query: {
|
||||
"source-table": `card__${this.ordersQuestion.id}`,
|
||||
},
|
||||
}).then(response => cy.wrap(response.body).as("baseModel"));
|
||||
});
|
||||
}
|
||||
|
||||
// Q1 - join, custom column, no aggregations, no breakouts
|
||||
export function createQ1Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
"source-table": `card__${source.id}`,
|
||||
expressions: {
|
||||
Net: ["-", TOTAL_FIELD, TAX_FIELD],
|
||||
},
|
||||
joins: [
|
||||
{
|
||||
fields: "all",
|
||||
strategy: "left-join",
|
||||
alias: "Reviews - Product",
|
||||
condition: [
|
||||
"=",
|
||||
PRODUCT_ID_FIELD,
|
||||
[
|
||||
"field",
|
||||
"PRODUCT_ID",
|
||||
{
|
||||
"base-type": "type/Integer",
|
||||
"join-alias": "Reviews - Product",
|
||||
},
|
||||
],
|
||||
],
|
||||
"source-table": REVIEWS_ID,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q2 - join, custom column, 2 aggregations, no breakouts
|
||||
export function createQ2Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ1Query(source),
|
||||
aggregation: [["count"], ["sum", TOTAL_FIELD]],
|
||||
};
|
||||
}
|
||||
|
||||
// Q3 - join, custom column, no aggregations, 3 breakouts
|
||||
export function createQ3Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ1Query(source),
|
||||
breakout: [
|
||||
[
|
||||
"field",
|
||||
ORDERS.CREATED_AT,
|
||||
{
|
||||
"base-type": "type/DateTime",
|
||||
"temporal-unit": "month",
|
||||
},
|
||||
],
|
||||
[
|
||||
"field",
|
||||
PRODUCTS.CATEGORY,
|
||||
{
|
||||
"base-type": "type/Text",
|
||||
"source-field": ORDERS.PRODUCT_ID,
|
||||
},
|
||||
],
|
||||
[
|
||||
"field",
|
||||
PEOPLE.CREATED_AT,
|
||||
{
|
||||
"base-type": "type/DateTime",
|
||||
"temporal-unit": "year",
|
||||
"source-field": ORDERS.USER_ID,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q4 - join, custom column, 2 aggregations, 2 breakouts
|
||||
export function createQ4Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ3Query(source),
|
||||
aggregation: [["count"], ["sum", TOTAL_FIELD]],
|
||||
};
|
||||
}
|
||||
|
||||
// Q5 - Q4 + 2nd stage with join, custom column, no aggregations, no breakouts
|
||||
export function createQ5Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
"source-query": createQ4Query(source),
|
||||
expressions: {
|
||||
"5 * Count": [
|
||||
"*",
|
||||
5,
|
||||
[
|
||||
"field",
|
||||
"count",
|
||||
{
|
||||
"base-type": "type/Integer",
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
joins: [
|
||||
{
|
||||
strategy: "left-join",
|
||||
alias: "Reviews - Created At: Month",
|
||||
condition: [
|
||||
"=",
|
||||
[
|
||||
"field",
|
||||
"CREATED_AT",
|
||||
{
|
||||
"base-type": "type/DateTime",
|
||||
"temporal-unit": "month",
|
||||
},
|
||||
],
|
||||
[
|
||||
"field",
|
||||
REVIEWS.CREATED_AT,
|
||||
{
|
||||
"base-type": "type/DateTime",
|
||||
"temporal-unit": "month",
|
||||
"join-alias": "Reviews - Created At: Month",
|
||||
},
|
||||
],
|
||||
],
|
||||
"source-table": REVIEWS_ID,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q6 - Q4 + 2nd stage with join, custom column, 2 aggregations, no breakouts
|
||||
export function createQ6Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ5Query(source),
|
||||
aggregation: [
|
||||
["count"],
|
||||
[
|
||||
"sum",
|
||||
[
|
||||
"field",
|
||||
REVIEWS.RATING,
|
||||
{
|
||||
"base-type": "type/Integer",
|
||||
"join-alias": "Reviews - Created At: Month",
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q7 - Q4 + 2nd stage with join, custom column, no aggregations, 2 breakouts
|
||||
export function createQ7Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ5Query(source),
|
||||
breakout: [
|
||||
[
|
||||
"field",
|
||||
REVIEWS.REVIEWER,
|
||||
{
|
||||
"base-type": "type/Text",
|
||||
"join-alias": "Reviews - Created At: Month",
|
||||
},
|
||||
],
|
||||
[
|
||||
"field",
|
||||
"PRODUCTS__via__PRODUCT_ID__CATEGORY",
|
||||
{
|
||||
"base-type": "type/Text",
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q8 - Q4 + 2nd stage with join, custom column, 2 aggregations, 2 breakouts
|
||||
export function createQ8Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
...createQ7Query(source),
|
||||
aggregation: [
|
||||
["count"],
|
||||
[
|
||||
"sum",
|
||||
[
|
||||
"field",
|
||||
REVIEWS.RATING,
|
||||
{
|
||||
"base-type": "type/Integer",
|
||||
"join-alias": "Reviews - Created At: Month",
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Q9 - Q8 + 3rd stage with 1 aggregation
|
||||
export function createQ9Query(source: Card): StructuredQuery {
|
||||
return {
|
||||
"source-query": createQ8Query(source),
|
||||
aggregation: [["count"]],
|
||||
};
|
||||
}
|
||||
|
||||
type CreateQuery = (source: Card) => StructuredQuery;
|
||||
|
||||
export function createAndVisitDashboardWithCardMatrix(
|
||||
createQuery: CreateQuery,
|
||||
) {
|
||||
cy.then(function () {
|
||||
H.createQuestion({
|
||||
type: "question",
|
||||
query: createQuery(this.baseQuestion),
|
||||
name: "Question-based Question",
|
||||
}).then(response => cy.wrap(response.body).as("qbq"));
|
||||
|
||||
H.createQuestion({
|
||||
type: "question",
|
||||
query: createQuery(this.baseModel),
|
||||
name: "Model-based Question",
|
||||
}).then(response => cy.wrap(response.body).as("mbq"));
|
||||
|
||||
H.createQuestion({
|
||||
type: "model",
|
||||
name: "Question-based Model",
|
||||
query: createQuery(this.baseQuestion),
|
||||
}).then(response => cy.wrap(response.body).as("qbm"));
|
||||
|
||||
H.createQuestion({
|
||||
type: "model",
|
||||
name: "Model-based Model",
|
||||
query: createQuery(this.baseModel),
|
||||
}).then(response => cy.wrap(response.body).as("mbm"));
|
||||
});
|
||||
|
||||
cy.then(function () {
|
||||
const cards = [this.qbq, this.mbq, this.qbm, this.mbm];
|
||||
createAndVisitDashboard(cards);
|
||||
});
|
||||
}
|
||||
|
||||
export function createAndVisitDashboard(cards: Card[]) {
|
||||
let id = 0;
|
||||
const getNextId = () => --id;
|
||||
|
||||
H.createDashboardWithTabs({
|
||||
enable_embedding: true,
|
||||
embedding_params: {
|
||||
[DATE_PARAMETER.slug]: "enabled",
|
||||
[TEXT_PARAMETER.slug]: "enabled",
|
||||
[NUMBER_PARAMETER.slug]: "enabled",
|
||||
},
|
||||
parameters: [DATE_PARAMETER, TEXT_PARAMETER, NUMBER_PARAMETER],
|
||||
dashcards: [
|
||||
...cards.map((card, index) => ({
|
||||
id: getNextId(),
|
||||
size_x: CARD_WIDTH,
|
||||
size_y: CARD_HEIGHT,
|
||||
row: CARD_HEIGHT * Math.floor(index / 2),
|
||||
col: index % 2 === 0 ? 0 : CARD_WIDTH,
|
||||
card,
|
||||
card_id: card.id,
|
||||
})),
|
||||
],
|
||||
}).then(dashboard => {
|
||||
H.visitDashboard(dashboard.id);
|
||||
cy.wrap(dashboard.id).as("dashboardId");
|
||||
cy.wait("@getDashboard");
|
||||
});
|
||||
}
|
||||
|
||||
export function setup1stStageExplicitJoinFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Reviewer", 0).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Reviewer", 0).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
}
|
||||
|
||||
export function apply1stStageExplicitJoinFilter() {
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByPlaceholderText("Search by Reviewer").type("abe.gorczany");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
}
|
||||
|
||||
export function setup1stStageImplicitJoinFromSourceFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Number").click();
|
||||
H.sidebar().findByText("Filter operator").next().click();
|
||||
H.popover().findByText("Between").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Price", 0).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Price", 0).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(0).type("0");
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(1).type("16");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup1stStageImplicitJoinFromJoinFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Category", 1).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Category", 1).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByLabelText("Gadget").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup1stStageCustomColumnFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Number").click();
|
||||
H.sidebar().findByText("Filter operator").next().click();
|
||||
H.popover().findByText("Between").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Net").click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Net").click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(0).type("0");
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(1).type("20");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup1stStageAggregationFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Number").click();
|
||||
H.sidebar().findByText("Filter operator").next().click();
|
||||
H.popover().findByText("Between").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Count").click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Count").click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(0).type("0");
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(1).type("2");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup1stStageBreakoutFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Category", 1).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Category", 1).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByLabelText("Gadget").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup2ndStageExplicitJoinFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Reviewer", 1).click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Reviewer", 1).click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByPlaceholderText("Search by Reviewer").type("abe.gorczany");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
cy.wait(["@dashboardData", "@dashboardData"]);
|
||||
}
|
||||
|
||||
export function setup2ndStageCustomColumnFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Number").click();
|
||||
H.sidebar().findByText("Filter operator").next().click();
|
||||
H.popover().findByText("Between").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("5 * Count").click();
|
||||
});
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("5 * Count").click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
}
|
||||
|
||||
export function apply2ndStageCustomColumnFilter() {
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(0).type("0");
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(1).type("20");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
}
|
||||
|
||||
export function setup2ndStageAggregationFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Number").click();
|
||||
H.sidebar().findByText("Filter operator").next().click();
|
||||
H.popover().findByText("Between").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Count", 1).click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Count", 1).click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(2).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Count").click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(3).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Count").click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
}
|
||||
|
||||
export function apply2ndStageAggregationFilter() {
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(0).type("0");
|
||||
cy.findAllByPlaceholderText("Enter a number").eq(1).type("2");
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
}
|
||||
|
||||
export function setup2ndStageBreakoutFilter() {
|
||||
H.editDashboard();
|
||||
|
||||
getFilter("Text").click();
|
||||
|
||||
H.getDashboardCard(0).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Category", 2).click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(1).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverList().scrollTo("bottom");
|
||||
getPopoverItem("Category", 2).click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(2).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Products Via Product ID Category").click();
|
||||
});
|
||||
dismissToast();
|
||||
|
||||
H.getDashboardCard(3).findByText("Select…").click();
|
||||
H.popover().within(() => {
|
||||
getPopoverItem("Products Via Product ID Category").click();
|
||||
});
|
||||
|
||||
cy.button("Save").click();
|
||||
cy.wait("@updateDashboard");
|
||||
}
|
||||
|
||||
export function apply2ndStageBreakoutFilter() {
|
||||
H.filterWidget().eq(0).click();
|
||||
H.popover().within(() => {
|
||||
cy.findByLabelText("Gadget").click();
|
||||
cy.button("Add filter").click();
|
||||
});
|
||||
}
|
||||
|
||||
export function getFilter(name: string) {
|
||||
return cy.findByTestId("fixed-width-filters").findByText(name);
|
||||
}
|
||||
|
||||
export function getPopoverList() {
|
||||
return cy.findAllByRole("grid").eq(0);
|
||||
}
|
||||
|
||||
export function getPopoverItems() {
|
||||
return cy.get("[data-element-id=list-section]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index if more than 1 item with the same name is visible, specify which one should be used
|
||||
*/
|
||||
export function getPopoverItem(name: string, index = 0) {
|
||||
/**
|
||||
* Without scrollIntoView() the popover may scroll automatically to a different
|
||||
* place when clicking the item (unclear why).
|
||||
*/
|
||||
return cy.findAllByText(name).eq(index).scrollIntoView();
|
||||
}
|
||||
|
||||
export function dismissToast() {
|
||||
cy.findByTestId("toast-undo")
|
||||
.findByRole("img", { name: /close icon/ })
|
||||
.click();
|
||||
}
|
||||
|
||||
export function clickAway() {
|
||||
cy.get("body").click(0, 0);
|
||||
}
|
||||
|
||||
export function goBackToDashboard() {
|
||||
cy.findByLabelText("Back to Test Dashboard").click();
|
||||
cy.wait("@getDashboard");
|
||||
}
|
||||
|
||||
export function getDashboardId(): Cypress.Chainable<number> {
|
||||
return cy
|
||||
.get("@dashboardId")
|
||||
.then(dashboardId => dashboardId as unknown as number);
|
||||
}
|
||||
|
||||
export function waitForPublicDashboardData() {
|
||||
// tests with public dashboards always have 4 dashcards
|
||||
cy.wait([
|
||||
"@publicDashboardData",
|
||||
"@publicDashboardData",
|
||||
"@publicDashboardData",
|
||||
"@publicDashboardData",
|
||||
]);
|
||||
}
|
||||
|
||||
export function waitForEmbeddedDashboardData() {
|
||||
// tests with embedded dashboards always have 4 dashcards
|
||||
cy.wait([
|
||||
"@embeddedDashboardData",
|
||||
"@embeddedDashboardData",
|
||||
"@embeddedDashboardData",
|
||||
"@embeddedDashboardData",
|
||||
]);
|
||||
}
|
||||
|
||||
export function verifyDashcardMappingOptions(
|
||||
dashcardIndex: number,
|
||||
sections: MappingSection[],
|
||||
) {
|
||||
H.getDashboardCard(dashcardIndex).findByText("Select…").click();
|
||||
verifyPopoverMappingOptions(sections);
|
||||
clickAway();
|
||||
}
|
||||
|
||||
export function verifyNoDashcardMappingOptions(dashcardIndex: number) {
|
||||
H.getDashboardCard(dashcardIndex)
|
||||
.findByText("No valid fields")
|
||||
.should("be.visible");
|
||||
|
||||
H.getDashboardCard(dashcardIndex).findByText("No valid fields").realHover();
|
||||
cy.findByRole("tooltip")
|
||||
.findByText(
|
||||
"This card doesn't have any fields or parameters that can be mapped to this parameter type.",
|
||||
)
|
||||
.should("be.visible");
|
||||
}
|
||||
|
||||
type SectionName = string;
|
||||
type ColumnName = string;
|
||||
type MappingSection = [SectionName, ColumnName[]];
|
||||
|
||||
export function verifyPopoverMappingOptions(sections: MappingSection[]) {
|
||||
const expectedItemsCount = sections.reduce(
|
||||
(sum, [sectionName, columnNames]) =>
|
||||
sum + [sectionName, ...columnNames].length,
|
||||
0,
|
||||
);
|
||||
|
||||
H.popover().within(() => {
|
||||
getPopoverItems().then($items => {
|
||||
let index = 0;
|
||||
|
||||
for (const [sectionName, columnNames] of sections) {
|
||||
const item = cy.wrap($items[index]);
|
||||
item.scrollIntoView(); // the list is virtualized, we need to keep scrolling to see all the items
|
||||
item.should("have.text", sectionName);
|
||||
++index;
|
||||
|
||||
for (const columnName of columnNames) {
|
||||
const item = cy.wrap($items[index]);
|
||||
item.scrollIntoView();
|
||||
item.findByLabelText(columnName).should("be.visible");
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
expect($items.length).to.eq(expectedItemsCount);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function verifyDashcardRowsCount({
|
||||
dashcardIndex,
|
||||
dashboardCount,
|
||||
queryBuilderCount,
|
||||
}: {
|
||||
dashcardIndex: number;
|
||||
dashboardCount: string;
|
||||
queryBuilderCount: string;
|
||||
}) {
|
||||
H.getDashboardCard(dashcardIndex)
|
||||
.findByText(dashboardCount)
|
||||
.should("be.visible");
|
||||
H.getDashboardCard(dashcardIndex)
|
||||
.findByTestId("legend-caption-title")
|
||||
.click();
|
||||
cy.wait("@dataset");
|
||||
cy.findByTestId("question-row-count").should("have.text", queryBuilderCount);
|
||||
}
|
||||
|
||||
export function verifyDashcardCellValues({
|
||||
dashcardIndex,
|
||||
values,
|
||||
}: {
|
||||
dashcardIndex: number;
|
||||
values: string[];
|
||||
}) {
|
||||
for (let valueIndex = 0; valueIndex < values.length; ++valueIndex) {
|
||||
const value = values[valueIndex];
|
||||
|
||||
H.getDashboardCard(dashcardIndex)
|
||||
.findByTestId("table-row")
|
||||
.findAllByTestId("cell-data")
|
||||
.eq(valueIndex)
|
||||
.should("have.text", value);
|
||||
}
|
||||
|
||||
H.getDashboardCard(dashcardIndex)
|
||||
.findByTestId("legend-caption-title")
|
||||
.click();
|
||||
cy.wait("@dataset");
|
||||
|
||||
for (let valueIndex = 0; valueIndex < values.length; ++valueIndex) {
|
||||
const value = values[valueIndex];
|
||||
const cellIndex = valueIndex + values.length; // values.length to skip header row
|
||||
|
||||
cy.findAllByTestId("cell-data").eq(cellIndex).should("have.text", value);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,9 @@ import { H } from "e2e/support";
|
||||
import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data";
|
||||
|
||||
describe("scenarios > navigation > navbar", () => {
|
||||
describe("OSS", () => {
|
||||
describe("Normal user", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsNormalUser();
|
||||
});
|
||||
|
||||
@@ -25,6 +26,7 @@ describe("scenarios > navigation > navbar", () => {
|
||||
H.navigationSidebar().should("be.visible");
|
||||
H.visitDashboard(ORDERS_DASHBOARD_ID);
|
||||
H.navigationSidebar().should("not.be.visible");
|
||||
|
||||
cy.findByTestId("main-logo-link").click();
|
||||
H.navigationSidebar().should("not.be.visible");
|
||||
});
|
||||
@@ -34,7 +36,7 @@ describe("scenarios > navigation > navbar", () => {
|
||||
H.navigationSidebar().should("not.be.visible");
|
||||
});
|
||||
|
||||
it("should preserve state when visting a collection", () => {
|
||||
it("should preserve state when visiting a collection", () => {
|
||||
H.visitDashboard(ORDERS_DASHBOARD_ID);
|
||||
H.navigationSidebar().should("not.be.visible");
|
||||
H.appBar().contains("Our analytics").parentsUntil("a").first().click();
|
||||
@@ -60,22 +62,28 @@ describe("scenarios > navigation > navbar", () => {
|
||||
.click();
|
||||
H.navigationSidebar().should("not.be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
it("should be open when visiting home with a custom home page configured", () => {
|
||||
describe("Custom Homepage", () => {
|
||||
beforeEach(() => {
|
||||
H.restore();
|
||||
cy.signInAsAdmin();
|
||||
H.updateSetting("custom-homepage", true);
|
||||
H.updateSetting("custom-homepage-dashboard", ORDERS_DASHBOARD_ID);
|
||||
});
|
||||
|
||||
it("should be open when visiting home with a custom home page configured", () => {
|
||||
cy.visit("/");
|
||||
cy.reload();
|
||||
cy.url().should("contain", "question");
|
||||
cy.url().should("contain", "/dashboard/");
|
||||
H.navigationSidebar().should("be.visible");
|
||||
|
||||
cy.findByTestId("main-logo-link").click();
|
||||
H.navigationSidebar().should("be.visible");
|
||||
});
|
||||
|
||||
it("should preserve state when clicking the mb logo and a custom home page is configured", () => {
|
||||
cy.signInAsAdmin();
|
||||
H.updateSetting("custom-homepage", true);
|
||||
H.updateSetting("custom-homepage-dashboard", ORDERS_DASHBOARD_ID);
|
||||
it("should preserve state when clicking the mb logo and custom home page is configured", () => {
|
||||
H.visitDashboard(ORDERS_DASHBOARD_ID);
|
||||
H.navigationSidebar().should("not.be.visible");
|
||||
cy.findByTestId("main-logo-link").click();
|
||||
H.navigationSidebar().should("not.be.visible");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user