Compare commits

...

18 Commits

Author SHA1 Message Date
Ryan Laurie
a199b2d71c facepalm 2024-12-20 15:25:22 -07:00
Ryan Laurie
f842041668 snapshot via env var 2024-12-20 15:18:07 -07:00
Ryan Laurie
88f06e1976 fix 2024-12-20 15:17:38 -07:00
Ryan Laurie
db35f2e14a do the snaps 2024-12-20 15:17:38 -07:00
Ryan Laurie
d2bae3d18a test metabase in dev mode 2024-12-20 15:17:38 -07:00
Ryan Laurie
716e944681 split up query stage tests 2024-12-20 15:17:24 -07:00
Ryan Laurie
ecedbf2a11 cleanup 2024-12-20 09:08:34 -07:00
Ryan Laurie
b3a6ec5cce fix clock tests 2024-12-20 09:08:34 -07:00
Ryan Laurie
6d4480bc3e more cleanup 2024-12-20 09:08:34 -07:00
Ryan Laurie
94221b292e cleanup 2024-12-20 09:08:34 -07:00
Ryan Laurie
ac95fe9476 fix navbar tests 2024-12-20 09:08:34 -07:00
Ryan Laurie
13767bc988 more betterer 2024-12-20 09:07:19 -07:00
Ryan Laurie
4eef0140f5 pass the tags 😅 2024-12-20 09:07:18 -07:00
Ryan Laurie
8188d7a9af YOLO 2024-12-20 09:07:18 -07:00
Ryan Laurie
87cc7bc096 YOLO 2024-12-20 09:07:18 -07:00
Ryan Laurie
c61d0a0cac fix 2024-12-20 08:58:56 -07:00
Ryan Laurie
bd80bc35df update for merge to master 2024-12-20 08:54:44 -07:00
Ryan Laurie
b4f5290468 split up query stage tests
# Conflicts:
#	e2e/test/scenarios/dashboard-filters-2/dashboard-filters-query-stages.cy.spec.ts
2024-12-20 08:50:13 -07:00
19 changed files with 8052 additions and 3210 deletions

View File

@@ -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;

View 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
View 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();

View File

@@ -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]

View File

@@ -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);
};

View File

@@ -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();

View File

@@ -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");
});

View File

@@ -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

View File

@@ -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"],
});
});
});
});
});
});

View File

@@ -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],
]);
}
});
});

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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"],
});
});
});
});
});
});

View File

@@ -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],
]);
}
});
});

View File

@@ -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);
}
}

View File

@@ -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");
});