Merge remote-tracking branch 'upstream/master'

This commit is contained in:
FutureMeng 2025-02-08 15:29:53 +08:00
commit b99ce6686b
1042 changed files with 49944 additions and 91498 deletions

View File

@ -18,6 +18,7 @@
# https://cwiki.apache.org/confluence/display/INFRA/.asf.yaml+features+for+git+repositories # https://cwiki.apache.org/confluence/display/INFRA/.asf.yaml+features+for+git+repositories
--- ---
github: github:
del_branch_on_merge: true
description: "Apache Superset is a Data Visualization and Data Exploration Platform" description: "Apache Superset is a Data Visualization and Data Exploration Platform"
homepage: https://superset.apache.org/ homepage: https://superset.apache.org/
labels: labels:
@ -75,16 +76,13 @@ github:
- dependency-review - dependency-review
- frontend-build - frontend-build
- pre-commit (current) - pre-commit (current)
- pre-commit (next)
- pre-commit (previous) - pre-commit (previous)
- test-mysql - test-mysql
- test-postgres (current) - test-postgres (current)
- test-postgres (next)
- test-postgres-hive - test-postgres-hive
- test-postgres-presto - test-postgres-presto
- test-sqlite - test-sqlite
- unit-tests (current) - unit-tests (current)
- unit-tests (next)
required_pull_request_reviews: required_pull_request_reviews:
dismiss_stale_reviews: false dismiss_stale_reviews: false

View File

@ -34,7 +34,6 @@
**/*.sqllite **/*.sqllite
**/*.swp **/*.swp
**/.terser-plugin-cache/ **/.terser-plugin-cache/
**/.storybook/
**/node_modules/ **/node_modules/
tests/ tests/

4
.github/CODEOWNERS vendored
View File

@ -12,7 +12,7 @@
# Notify Helm Chart maintainers about changes in it # Notify Helm Chart maintainers about changes in it
/helm/superset/ @craig-rueda @dpgaspar @villebro @nytai @michael-s-molina /helm/superset/ @craig-rueda @dpgaspar @villebro @nytai @michael-s-molina @mistercrunch @rusackas @Antonio-RiveroMartnez
# Notify E2E test maintainers of changes # Notify E2E test maintainers of changes
@ -24,7 +24,7 @@
# Notify PMC members of changes to required GitHub Actions # Notify PMC members of changes to required GitHub Actions
/.asf.yaml @villebro @geido @eschutho @rusackas @betodealmeida @nytai @mistercrunch @craig-rueda @kgabryje @dpgaspar /.asf.yaml @villebro @geido @eschutho @rusackas @betodealmeida @nytai @mistercrunch @craig-rueda @kgabryje @dpgaspar @Antonio-RiveroMartnez
# Maps are a finicky contribution process we care about # Maps are a finicky contribution process we care about

View File

@ -0,0 +1,23 @@
name: Label Draft PRs
on:
pull_request:
types:
- opened
- converted_to_draft
jobs:
label-draft:
runs-on: ubuntu-latest
steps:
- name: Check if the PR is a draft
id: check-draft
uses: actions/github-script@v6
with:
script: |
const isDraft = context.payload.pull_request.draft;
core.setOutput('isDraft', isDraft);
- name: Add `review:draft` Label
if: steps.check-draft.outputs.isDraft == 'true'
uses: actions-ecosystem/action-add-labels@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: "review:draft"

View File

@ -26,11 +26,12 @@ runs:
shell: bash shell: bash
run: | run: |
if [ "${{ inputs.python-version }}" = "current" ]; then if [ "${{ inputs.python-version }}" = "current" ]; then
echo "PYTHON_VERSION=3.10" >> $GITHUB_ENV
elif [ "${{ inputs.python-version }}" = "next" ]; then
echo "PYTHON_VERSION=3.11" >> $GITHUB_ENV echo "PYTHON_VERSION=3.11" >> $GITHUB_ENV
elif [ "${{ inputs.python-version }}" = "next" ]; then
# currently disabled in GHA matrixes because of library compatibility issues
echo "PYTHON_VERSION=3.12" >> $GITHUB_ENV
elif [ "${{ inputs.python-version }}" = "previous" ]; then elif [ "${{ inputs.python-version }}" = "previous" ]; then
echo "PYTHON_VERSION=3.9" >> $GITHUB_ENV echo "PYTHON_VERSION=3.10" >> $GITHUB_ENV
else else
echo "PYTHON_VERSION=${{ inputs.python-version }}" >> $GITHUB_ENV echo "PYTHON_VERSION=${{ inputs.python-version }}" >> $GITHUB_ENV
fi fi
@ -43,6 +44,7 @@ runs:
run: | run: |
if [ "${{ inputs.install-superset }}" = "true" ]; then if [ "${{ inputs.install-superset }}" = "true" ]; then
sudo apt-get update && sudo apt-get -y install libldap2-dev libsasl2-dev sudo apt-get update && sudo apt-get -y install libldap2-dev libsasl2-dev
pip install --upgrade pip setuptools wheel uv pip install --upgrade pip setuptools wheel uv
if [ "${{ inputs.requirements-type }}" = "dev" ]; then if [ "${{ inputs.requirements-type }}" = "dev" ]; then

View File

@ -30,6 +30,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: "Dependency Review" - name: "Dependency Review"
uses: actions/dependency-review-action@v4 uses: actions/dependency-review-action@v4
continue-on-error: true
with: with:
fail-on-severity: critical fail-on-severity: critical
# compatible/incompatible licenses addressed here: https://www.apache.org/legal/resolved.html # compatible/incompatible licenses addressed here: https://www.apache.org/legal/resolved.html

View File

@ -14,6 +14,7 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
setup_matrix: setup_matrix:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
outputs: outputs:
@ -36,6 +37,7 @@ jobs:
env: env:
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
IMAGE_TAG: apache/superset:GHA-${{ matrix.build_preset }}-${{ github.run_id }}
steps: steps:
@ -71,35 +73,65 @@ jobs:
# Single platform builds in pull_request context to speed things up # Single platform builds in pull_request context to speed things up
if [ "${{ github.event_name }}" = "push" ]; then if [ "${{ github.event_name }}" = "push" ]; then
PLATFORM_ARG="--platform linux/arm64 --platform linux/amd64" PLATFORM_ARG="--platform linux/arm64 --platform linux/amd64"
# can only --load images in single-platform builds
PUSH_OR_LOAD="--push"
elif [ "${{ github.event_name }}" = "pull_request" ]; then elif [ "${{ github.event_name }}" = "pull_request" ]; then
PLATFORM_ARG="--platform linux/amd64" PLATFORM_ARG="--platform linux/amd64"
PUSH_OR_LOAD="--load"
fi fi
supersetbot docker \ supersetbot docker \
--push \ $PUSH_OR_LOAD \
--preset ${{ matrix.build_preset }} \ --preset ${{ matrix.build_preset }} \
--context "$EVENT" \ --context "$EVENT" \
--context-ref "$RELEASE" $FORCE_LATEST \ --context-ref "$RELEASE" $FORCE_LATEST \
--extra-flags "--build-arg INCLUDE_CHROMIUM=false" \ --extra-flags "--build-arg INCLUDE_CHROMIUM=false --tag $IMAGE_TAG" \
$PLATFORM_ARG $PLATFORM_ARG
# in the context of push (using multi-platform build), we need to pull the image locally
- name: Docker pull - name: Docker pull
if: steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker if: github.event_name == 'push' && (steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker)
run: docker pull apache/superset:GHA-${GITHUB_RUN_ID} run: docker pull $IMAGE_TAG
- name: Print docker stats - name: Print docker stats
if: steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker if: steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker
run: | run: |
IMAGE_ID=$(docker images --filter "label=sha=${{ github.sha }}" --format "{{.ID}}" | head -n 1)
echo "SHA: ${{ github.sha }}" echo "SHA: ${{ github.sha }}"
echo "IMAGE: $IMAGE_ID" echo "IMAGE: $IMAGE_TAG"
docker images $IMAGE_ID docker images $IMAGE_TAG
docker history $IMAGE_ID docker history $IMAGE_TAG
- name: docker-compose sanity check - name: docker-compose sanity check
if: (steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker) && (matrix.build_preset == 'dev' || matrix.build_preset == 'lean') if: (steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker) && (matrix.build_preset == 'dev' || matrix.build_preset == 'lean')
shell: bash shell: bash
run: | run: |
export SUPERSET_BUILD_TARGET=${{ matrix.build_preset }} export SUPERSET_BUILD_TARGET=${{ matrix.build_preset }}
# This should reuse the CACHED image built in the previous steps
docker compose build superset-init --build-arg DEV_MODE=false --build-arg INCLUDE_CHROMIUM=false docker compose build superset-init --build-arg DEV_MODE=false --build-arg INCLUDE_CHROMIUM=false
docker compose up superset-init --exit-code-from superset-init docker compose up superset-init --exit-code-from superset-init
docker-compose-image-tag:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Check for file changes
id: check
uses: ./.github/actions/change-detector/
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Docker Environment
if: steps.check.outputs.docker
uses: ./.github/actions/setup-docker
with:
dockerhub-user: ${{ secrets.DOCKERHUB_USER }}
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
build: "false"
install-docker-compose: "true"
- name: docker-compose sanity check
if: steps.check.outputs.docker
shell: bash
run: |
docker compose -f docker-compose-image-tag.yml up superset-init --exit-code-from superset-init

View File

@ -1,145 +1,153 @@
name: Ephemeral env workflow name: Ephemeral env workflow
# Example manual trigger: gh workflow run ephemeral-env.yml --ref fix_ephemerals --field comment_body="/testenv up" --field issue_number=666 # Example manual trigger:
# gh workflow run ephemeral-env.yml --ref fix_ephemerals --field label_name="testenv-up" --field issue_number=666
on: on:
issue_comment: pull_request_target:
types: [created] types:
- labeled
workflow_dispatch: workflow_dispatch:
inputs: inputs:
comment_body: label_name:
description: 'Comment body to simulate /testenv command' description: 'Label name to simulate label-based /testenv trigger'
required: true required: true
default: '/testenv up' default: 'testenv-up'
issue_number: issue_number:
description: 'Issue or PR number' description: 'Issue or PR number'
required: true required: true
jobs: jobs:
ephemeral-env-comment: ephemeral-env-label:
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.event.inputs.issue_number || github.event.issue.number || github.run_id }}-comment group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}-label
cancel-in-progress: true cancel-in-progress: true
name: Evaluate ephemeral env comment trigger (/testenv) name: Evaluate ephemeral env label trigger
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
permissions: permissions:
pull-requests: write pull-requests: write
outputs: outputs:
slash-command: ${{ steps.eval-body.outputs.result }} slash-command: ${{ steps.eval-label.outputs.result }}
feature-flags: ${{ steps.eval-feature-flags.outputs.result }} feature-flags: ${{ steps.eval-feature-flags.outputs.result }}
sha: ${{ steps.get-sha.outputs.sha }}
env: env:
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
steps: steps:
- name: Debug - name: Check for the "testenv-up" label
run: | id: eval-label
echo "Comment on PR #${{ github.event.issue.number }} by ${{ github.event.issue.user.login }}, ${{ github.event.comment.author_association }}" run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
LABEL_NAME="${{ github.event.inputs.label_name }}"
else
LABEL_NAME="${{ github.event.label.name }}"
fi
- name: Eval comment body for /testenv slash command echo "Evaluating label: $LABEL_NAME"
uses: actions/github-script@v7
env:
COMMENT_BODY: ${{ github.event.inputs.comment_body || github.event.comment.body }}
id: eval-body
with:
result-encoding: string
script: |
const pattern = /^\/testenv (up|down)/;
const result = pattern.exec(process.env.COMMENT_BODY || '');
return result === null ? 'noop' : result[1];
- name: Looking for feature flags if [[ "$LABEL_NAME" == "testenv-up" ]]; then
uses: actions/github-script@v7 echo "result=up" >> $GITHUB_OUTPUT
env: else
COMMENT_BODY: ${{ github.event.inputs.comment_body || github.event.comment.body }} echo "result=noop" >> $GITHUB_OUTPUT
id: eval-feature-flags fi
with:
script: |
const pattern = /FEATURE_(\w+)=(\w+)/g;
let results = [];
[...process.env.COMMENT_BODY.matchAll(pattern)].forEach(match => {
const config = {
name: `SUPERSET_FEATURE_${match[1]}`,
value: match[2],
};
results.push(config);
});
return results;
- name: Limit to committers - name: Get event SHA
if: > id: get-sha
steps.eval-body.outputs.result != 'noop' && if: steps.eval-label.outputs.result == 'up'
github.event_name == 'issue_comment' && uses: actions/github-script@v7
github.event.comment.author_association != 'MEMBER' && with:
github.event.comment.author_association != 'OWNER' github-token: ${{ secrets.GITHUB_TOKEN }}
uses: actions/github-script@v7 script: |
with: let prSha;
github-token: ${{ github.token }}
script: | // If event is workflow_dispatch, use the issue_number from inputs
const errMsg = '@${{ github.event.comment.user.login }} Ephemeral environment creation is currently limited to committers.'; if (context.eventName === "workflow_dispatch") {
github.rest.issues.createComment({ const prNumber = "${{ github.event.inputs.issue_number }}";
issue_number: ${{ github.event.issue.number }}, if (!prNumber) {
owner: context.repo.owner, console.log("No PR number found.");
repo: context.repo.repo, return;
body: errMsg }
});
core.setFailed(errMsg); // Fetch PR details using the provided issue_number
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
prSha = pr.head.sha;
} else {
// If it's not workflow_dispatch, use the PR head sha from the event
prSha = context.payload.pull_request.head.sha;
}
console.log(`PR SHA: ${prSha}`);
core.setOutput("sha", prSha);
- name: Looking for feature flags in PR description
uses: actions/github-script@v7
id: eval-feature-flags
if: steps.eval-label.outputs.result == 'up'
with:
script: |
const description = context.payload.pull_request
? context.payload.pull_request.body || ''
: context.payload.inputs.pr_description || '';
const pattern = /FEATURE_(\w+)=(\w+)/g;
let results = [];
[...description.matchAll(pattern)].forEach(match => {
const config = {
name: `SUPERSET_FEATURE_${match[1]}`,
value: match[2],
};
results.push(config);
});
return results;
- name: Reply with confirmation comment
uses: actions/github-script@v7
if: steps.eval-label.outputs.result == 'up'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const action = '${{ steps.eval-label.outputs.result }}';
const user = context.actor;
const runId = context.runId;
const workflowUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
const issueNumber = context.payload.pull_request
? context.payload.pull_request.number
: context.payload.inputs.issue_number;
if (!issueNumber) {
throw new Error("Issue number is not available.");
}
const body = `@${user} Processing your ephemeral environment request [here](${workflowUrl}). Action: **${action}**.`;
- name: Reply with confirmation comment
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const issueNumber = ${{ github.event.inputs.issue_number || github.event.issue.number }};
const user = '${{ github.event.comment.user.login || github.actor }}';
const action = '${{ steps.eval-body.outputs.result }}';
const runId = context.runId;
const workflowUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
const body = `@${user} Processing your ephemeral environment request [here](${workflowUrl}).`;
if (action !== 'noop') {
await github.rest.issues.createComment({ await github.rest.issues.createComment({
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
issue_number: issueNumber, issue_number: issueNumber,
body, body,
}); });
}
else {
core.setFailed('No ephemeral environment action detected.');
}
ephemeral-docker-build: ephemeral-docker-build:
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.event.inputs.issue_number || github.event.issue.number || github.run_id }}-build group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}-build
cancel-in-progress: true cancel-in-progress: true
needs: ephemeral-env-comment needs: ephemeral-env-label
if: needs.ephemeral-env-comment.outputs.slash-command == 'up' if: needs.ephemeral-env-label.outputs.slash-command == 'up'
name: ephemeral-docker-build name: ephemeral-docker-build
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- name: Get Info from comment - name: "Checkout ${{ github.ref }} ( ${{ needs.ephemeral-env-label.outputs.sha }} : ${{steps.get-sha.outputs.sha}} )"
uses: actions/github-script@v7
id: get-pr-info
with:
script: |
const request = {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: ${{ github.event.inputs.issue_number || github.event.issue.number }},
};
core.info(`Getting PR #${request.pull_number} from ${request.owner}/${request.repo}`);
const pr = await github.rest.pulls.get(request);
return pr.data;
- name: Debug
id: get-sha
run: |
echo "sha=${{ fromJSON(steps.get-pr-info.outputs.result).head.sha }}" >> $GITHUB_OUTPUT
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} : ${{steps.get-sha.outputs.sha}} )"
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
ref: ${{ steps.get-sha.outputs.sha }} ref: ${{ needs.ephemeral-env-label.outputs.sha }}
persist-credentials: false persist-credentials: false
- name: Setup Docker Environment - name: Setup Docker Environment
@ -181,14 +189,14 @@ jobs:
env: env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: superset-ci ECR_REPOSITORY: superset-ci
IMAGE_TAG: apache/superset:${{ steps.get-sha.outputs.sha }}-ci IMAGE_TAG: apache/superset:${{ needs.ephemeral-env-label.outputs.sha }}-ci
run: | run: |
docker tag $IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-ci docker tag $IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-ci
docker push -a $ECR_REGISTRY/$ECR_REPOSITORY docker push -a $ECR_REGISTRY/$ECR_REPOSITORY
ephemeral-env-up: ephemeral-env-up:
needs: [ephemeral-env-comment, ephemeral-docker-build] needs: [ephemeral-env-label, ephemeral-docker-build]
if: needs.ephemeral-env-comment.outputs.slash-command == 'up' if: needs.ephemeral-env-label.outputs.slash-command == 'up'
name: Spin up an ephemeral environment name: Spin up an ephemeral environment
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
permissions: permissions:
@ -196,120 +204,122 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
persist-credentials: false persist-credentials: false
- name: Configure AWS credentials - name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4 uses: aws-actions/configure-aws-credentials@v4
with: with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2 aws-region: us-west-2
- name: Login to Amazon ECR - name: Login to Amazon ECR
id: login-ecr id: login-ecr
uses: aws-actions/amazon-ecr-login@v2 uses: aws-actions/amazon-ecr-login@v2
- name: Check target image exists in ECR - name: Check target image exists in ECR
id: check-image id: check-image
continue-on-error: true continue-on-error: true
run: | run: |
aws ecr describe-images \ aws ecr describe-images \
--registry-id $(echo "${{ steps.login-ecr.outputs.registry }}" | grep -Eo "^[0-9]+") \ --registry-id $(echo "${{ steps.login-ecr.outputs.registry }}" | grep -Eo "^[0-9]+") \
--repository-name superset-ci \ --repository-name superset-ci \
--image-ids imageTag=pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-ci --image-ids imageTag=pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-ci
- name: Fail on missing container image - name: Fail on missing container image
if: steps.check-image.outcome == 'failure' if: steps.check-image.outcome == 'failure'
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{ github.token }} github-token: ${{ github.token }}
script: | script: |
const errMsg = '@${{ github.event.comment.user.login }} Container image not yet published for this PR. Please try again when build is complete.'; const errMsg = '@${{ github.event.comment.user.login }} Container image not yet published for this PR. Please try again when build is complete.';
github.rest.issues.createComment({ github.rest.issues.createComment({
issue_number: ${{ github.event.inputs.issue_number || github.event.issue.number }}, issue_number: ${{ github.event.inputs.issue_number || github.event.issue.number }},
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
body: errMsg body: errMsg
}); });
core.setFailed(errMsg); core.setFailed(errMsg);
- name: Fill in the new image ID in the Amazon ECS task definition - name: Fill in the new image ID in the Amazon ECS task definition
id: task-def id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1 uses: aws-actions/amazon-ecs-render-task-definition@v1
with: with:
task-definition: .github/workflows/ecs-task-definition.json task-definition: .github/workflows/ecs-task-definition.json
container-name: superset-ci container-name: superset-ci
image: ${{ steps.login-ecr.outputs.registry }}/superset-ci:pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-ci image: ${{ steps.login-ecr.outputs.registry }}/superset-ci:pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-ci
- name: Update env vars in the Amazon ECS task definition - name: Update env vars in the Amazon ECS task definition
run: | run: |
cat <<< "$(jq '.containerDefinitions[0].environment += ${{ needs.ephemeral-env-comment.outputs.feature-flags }}' < ${{ steps.task-def.outputs.task-definition }})" > ${{ steps.task-def.outputs.task-definition }} cat <<< "$(jq '.containerDefinitions[0].environment += ${{ needs.ephemeral-env-label.outputs.feature-flags }}' < ${{ steps.task-def.outputs.task-definition }})" > ${{ steps.task-def.outputs.task-definition }}
- name: Describe ECS service - name: Describe ECS service
id: describe-services id: describe-services
run: | run: |
echo "active=$(aws ecs describe-services --cluster superset-ci --services pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service | jq '.services[] | select(.status == "ACTIVE") | any')" >> $GITHUB_OUTPUT echo "active=$(aws ecs describe-services --cluster superset-ci --services pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service | jq '.services[] | select(.status == "ACTIVE") | any')" >> $GITHUB_OUTPUT
- name: Create ECS service - name: Create ECS service
id: create-service id: create-service
if: steps.describe-services.outputs.active != 'true' if: steps.describe-services.outputs.active != 'true'
env: env:
ECR_SUBNETS: subnet-0e15a5034b4121710,subnet-0e8efef4a72224974 ECR_SUBNETS: subnet-0e15a5034b4121710,subnet-0e8efef4a72224974
ECR_SECURITY_GROUP: sg-092ff3a6ae0574d91 ECR_SECURITY_GROUP: sg-092ff3a6ae0574d91
run: | run: |
aws ecs create-service \ aws ecs create-service \
--cluster superset-ci \ --cluster superset-ci \
--service-name pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service \ --service-name pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service \
--task-definition superset-ci \ --task-definition superset-ci \
--launch-type FARGATE \ --launch-type FARGATE \
--desired-count 1 \ --desired-count 1 \
--platform-version LATEST \ --platform-version LATEST \
--network-configuration "awsvpcConfiguration={subnets=[$ECR_SUBNETS],securityGroups=[$ECR_SECURITY_GROUP],assignPublicIp=ENABLED}" \ --network-configuration "awsvpcConfiguration={subnets=[$ECR_SUBNETS],securityGroups=[$ECR_SECURITY_GROUP],assignPublicIp=ENABLED}" \
--tags key=pr,value=${{ github.event.inputs.issue_number || github.event.issue.number }} key=github_user,value=${{ github.actor }} --tags key=pr,value=${{ github.event.inputs.issue_number || github.event.issue.number }} key=github_user,value=${{ github.actor }}
- name: Deploy Amazon ECS task definition - name: Deploy Amazon ECS task definition
id: deploy-task id: deploy-task
uses: aws-actions/amazon-ecs-deploy-task-definition@v2 uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with: with:
task-definition: ${{ steps.task-def.outputs.task-definition }} task-definition: ${{ steps.task-def.outputs.task-definition }}
service: pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service service: pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service
cluster: superset-ci cluster: superset-ci
wait-for-service-stability: true wait-for-service-stability: true
wait-for-minutes: 10 wait-for-minutes: 10
- name: List tasks - name: List tasks
id: list-tasks id: list-tasks
run: | run: |
echo "task=$(aws ecs list-tasks --cluster superset-ci --service-name pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service | jq '.taskArns | first')" >> $GITHUB_OUTPUT echo "task=$(aws ecs list-tasks --cluster superset-ci --service-name pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service | jq '.taskArns | first')" >> $GITHUB_OUTPUT
- name: Get network interface - name: Get network interface
id: get-eni id: get-eni
run: | run: |
echo "eni=$(aws ecs describe-tasks --cluster superset-ci --tasks ${{ steps.list-tasks.outputs.task }} | jq '.tasks | .[0] | .attachments | .[0] | .details | map(select(.name==\"networkInterfaceId\")) | .[0] | .value')" >> $GITHUB_OUTPUT echo "eni=$(aws ecs describe-tasks --cluster superset-ci --tasks ${{ steps.list-tasks.outputs.task }} | jq '.tasks[0].attachments[0].details | map(select(.name==\"networkInterfaceId\"))[0].value')" >> $GITHUB_OUTPUT
- name: Get public IP - name: Get public IP
id: get-ip id: get-ip
run: | run: |
echo "ip=$(aws ec2 describe-network-interfaces --network-interface-ids ${{ steps.get-eni.outputs.eni }} | jq -r '.NetworkInterfaces | first | .Association.PublicIp')" >> $GITHUB_OUTPUT echo "ip=$(aws ec2 describe-network-interfaces --network-interface-ids ${{ steps.get-eni.outputs.eni }} | jq -r '.NetworkInterfaces | first | .Association.PublicIp')" >> $GITHUB_OUTPUT
- name: Comment (success) - name: Comment (success)
if: ${{ success() }} if: ${{ success() }}
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{github.token}} github-token: ${{github.token}}
script: | script: |
github.rest.issues.createComment({ const issue_number = context.payload.inputs?.issue_number || context.issue.number;
issue_number: ${{ github.event.inputs.issue_number || github.event.issue.number }}, github.rest.issues.createComment({
owner: context.repo.owner, issue_number: issue_number,
repo: context.repo.repo, owner: context.repo.owner,
body: '@${{ github.event.inputs.user_login || github.event.comment.user.login }} Ephemeral environment spinning up at http://${{ steps.get-ip.outputs.ip }}:8080. Credentials are `admin`/`admin`. Please allow several minutes for bootstrapping and startup.' repo: context.repo.repo,
}) body: `@${{ github.actor }} Ephemeral environment spinning up at http://${{ steps.get-ip.outputs.ip }}:8080. Credentials are 'admin'/'admin'. Please allow several minutes for bootstrapping and startup.`
- name: Comment (failure) });
if: ${{ failure() }} - name: Comment (failure)
uses: actions/github-script@v7 if: ${{ failure() }}
with: uses: actions/github-script@v7
github-token: ${{github.token}} with:
script: | github-token: ${{github.token}}
github.rest.issues.createComment({ script: |
issue_number: ${{ github.event.inputs.issue_number || github.event.issue.number }}, const issue_number = context.payload.inputs?.issue_number || context.issue.number;
owner: context.repo.owner, github.rest.issues.createComment({
repo: context.repo.repo, issue_number: issue_number,
body: '@${{ github.event.inputs.user_login || github.event.comment.user.login }} Ephemeral environment creation failed. Please check the Actions logs for details.' owner: context.repo.owner,
}) repo: context.repo.repo,
body: '@${{ github.event.inputs.user_login || github.event.comment.user.login }} Ephemeral environment creation failed. Please check the Actions logs for details.'
})

View File

@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
strategy: strategy:
matrix: matrix:
python-version: ["current", "next", "previous"] python-version: ["current", "previous"]
steps: steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )" - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -41,6 +41,8 @@ jobs:
- name: pre-commit - name: pre-commit
run: | run: |
set +e # Don't exit immediately on failure set +e # Don't exit immediately on failure
# Skip eslint as it requires `npm ci` and is executed in another job
export SKIP=eslint
pre-commit run --all-files pre-commit run --all-files
if [ $? -ne 0 ] || ! git diff --quiet --exit-code; then if [ $? -ne 0 ] || ! git diff --quiet --exit-code; then
echo "❌ Pre-commit check failed." echo "❌ Pre-commit check failed."

View File

@ -24,11 +24,10 @@ jobs:
- uses: JustinBeckwith/linkinator-action@v1.11.0 - uses: JustinBeckwith/linkinator-action@v1.11.0
continue-on-error: true # This will make the job advisory (non-blocking, no red X) continue-on-error: true # This will make the job advisory (non-blocking, no red X)
with: with:
paths: "**/*.md, **/*.mdx" paths: "**/*.md, **/*.mdx, !superset-frontend/CHANGELOG.md"
linksToSkip: >- linksToSkip: >-
^https://github.com/apache/(superset|incubator-superset)/(pull|issue)/\d+, ^https://github.com/apache/(superset|incubator-superset)/(pull|issue)/\d+,
http://localhost:8088/, http://localhost:8088/,
docker/.env-non-dev,
http://127.0.0.1:3000/, http://127.0.0.1:3000/,
http://localhost:9001/, http://localhost:9001/,
https://charts.bitnami.com/bitnami, https://charts.bitnami.com/bitnami,

View File

@ -138,7 +138,7 @@ jobs:
run: cypress-run-all ${{ env.USE_DASHBOARD }} run: cypress-run-all ${{ env.USE_DASHBOARD }}
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: github.event_name == 'workflow_dispatch' && (steps.check.outputs.python || steps.check.outputs.frontend) if: failure()
with: with:
path: ${{ github.workspace }}/superset-frontend/cypress-base/cypress/screenshots path: ${{ github.workspace }}/superset-frontend/cypress-base/cypress/screenshots
name: cypress-artifact-${{ github.run_id }}-${{ github.job }} name: cypress-artifact-${{ github.run_id }}-${{ github.job }}-${{ matrix.browser }}-${{ matrix.parallel_id }}

View File

@ -1,4 +1,4 @@
name: Frontend name: "Frontend Build CI (unit tests, linting & sanity checks)"
on: on:
push: push:
@ -13,68 +13,168 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true cancel-in-progress: true
env:
TAG: apache/superset:GHA-${{ github.run_id }}
jobs: jobs:
frontend-build: frontend-build:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
outputs:
should-run: ${{ steps.check.outputs.frontend }}
steps: steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )" - name: Checkout Code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
persist-credentials: false persist-credentials: false
submodules: recursive
- name: Check npm lock file version - name: Check for File Changes
run: ./scripts/ci_check_npm_lock_version.sh ./superset-frontend/package-lock.json
- name: Check for file changes
id: check id: check
uses: ./.github/actions/change-detector/ uses: ./.github/actions/change-detector/
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
- name: Build Docker Image
if: steps.check.outputs.frontend if: steps.check.outputs.frontend
uses: actions/setup-node@v4 shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
docker buildx build \
-t $TAG \
--cache-from=type=registry,ref=apache/superset-cache:3.10-slim-bookworm \
--target superset-node-ci \
.
- name: Save Docker Image as Artifact
if: steps.check.outputs.frontend
run: |
docker save $TAG | gzip > docker-image.tar.gz
- name: Upload Docker Image Artifact
if: steps.check.outputs.frontend
uses: actions/upload-artifact@v4
with: with:
node-version: "20" name: docker-image
- name: Install dependencies path: docker-image.tar.gz
if: steps.check.outputs.frontend
uses: ./.github/actions/cached-dependencies sharded-jest-tests:
needs: frontend-build
if: needs.frontend-build.outputs.should-run == 'true'
strategy:
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8]
fail-fast: false
runs-on: ubuntu-24.04
steps:
- name: Download Docker Image Artifact
uses: actions/download-artifact@v4
with: with:
run: npm-install name: docker-image
- name: eslint
if: steps.check.outputs.frontend - name: Load Docker Image
working-directory: ./superset-frontend run: docker load < docker-image.tar.gz
- name: npm run test with coverage
run: | run: |
npm run eslint -- . --quiet mkdir -p ${{ github.workspace }}/superset-frontend/coverage
- name: tsc docker run \
if: steps.check.outputs.frontend -v ${{ github.workspace }}/superset-frontend/coverage:/app/superset-frontend/coverage \
working-directory: ./superset-frontend --rm $TAG \
run: | bash -c \
npm run type "npm run test -- --coverage --shard=${{ matrix.shard }}/8 --coverageReporters=json-summary"
- name: Build plugins packages
if: steps.check.outputs.frontend - name: Upload Coverage Artifact
working-directory: ./superset-frontend uses: actions/upload-artifact@v4
run: npm run plugins:build with:
- name: Build plugins Storybook name: coverage-artifacts-${{ matrix.shard }}
if: steps.check.outputs.frontend path: superset-frontend/coverage
working-directory: ./superset-frontend
run: npm run plugins:build-storybook report-coverage:
- name: superset-ui/core coverage needs: [sharded-jest-tests]
if: steps.check.outputs.frontend if: needs.frontend-build.outputs.should-run == 'true'
working-directory: ./superset-frontend runs-on: ubuntu-24.04
run: | steps:
npm run core:cover - name: Download Coverage Artifacts
- name: unit tests uses: actions/download-artifact@v4
if: steps.check.outputs.frontend with:
working-directory: ./superset-frontend pattern: coverage-artifacts-*
run: | path: coverage/
npm run test -- --coverage --silent
# todo: remove this step when fix generator as a project in root jest.config.js - name: Show Files
- name: generator-superset unit tests run: find coverage/
if: steps.check.outputs.frontend
working-directory: ./superset-frontend/packages/generator-superset - name: Merge Code Coverage
run: npm run test run: npx nyc merge coverage/ merged-output/coverage-summary.json
- name: Upload code coverage
- name: Upload Code Coverage
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@v5
with: with:
flags: javascript flags: javascript
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
verbose: true verbose: true
files: merged-output/coverage-summary.json
slug: apache/superset
core-cover:
needs: frontend-build
if: needs.frontend-build.outputs.should-run == 'true'
runs-on: ubuntu-24.04
steps:
- name: Download Docker Image Artifact
uses: actions/download-artifact@v4
with:
name: docker-image
- name: Load Docker Image
run: docker load < docker-image.tar.gz
- name: superset-ui/core coverage
run: |
docker run --rm $TAG bash -c \
"npm run core:cover"
lint-frontend:
needs: frontend-build
if: needs.frontend-build.outputs.should-run == 'true'
runs-on: ubuntu-24.04
steps:
- name: Download Docker Image Artifact
uses: actions/download-artifact@v4
with:
name: docker-image
- name: Load Docker Image
run: docker load < docker-image.tar.gz
- name: eslint
run: |
docker run --rm $TAG bash -c \
"npm i && npm run eslint -- . --quiet"
- name: tsc
run: |
docker run --rm $TAG bash -c \
"npm run type"
validate-frontend:
needs: frontend-build
if: needs.frontend-build.outputs.should-run == 'true'
runs-on: ubuntu-24.04
steps:
- name: Download Docker Image Artifact
uses: actions/download-artifact@v4
with:
name: docker-image
- name: Load Docker Image
run: docker load < docker-image.tar.gz
- name: Build Plugins Packages
run: |
docker run --rm $TAG bash -c \
"npm run plugins:build"
- name: Build Plugins Storybook
run: |
docker run --rm $TAG bash -c \
"npm run plugins:build-storybook"

View File

@ -25,7 +25,7 @@ jobs:
- name: Set up Helm - name: Set up Helm
uses: azure/setup-helm@v4 uses: azure/setup-helm@v4
with: with:
version: v3.5.4 version: v3.16.4
- name: Setup Python - name: Setup Python
uses: ./.github/actions/setup-backend/ uses: ./.github/actions/setup-backend/

View File

@ -77,7 +77,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
strategy: strategy:
matrix: matrix:
python-version: ["current", "next", "previous"] python-version: ["current", "previous"]
env: env:
PYTHONPATH: ${{ github.workspace }} PYTHONPATH: ${{ github.workspace }}
SUPERSET_CONFIG: tests.integration_tests.superset_test_config SUPERSET_CONFIG: tests.integration_tests.superset_test_config

View File

@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
strategy: strategy:
matrix: matrix:
python-version: ["current", "next"] python-version: ["previous", "current"]
env: env:
PYTHONPATH: ${{ github.workspace }} PYTHONPATH: ${{ github.workspace }}
steps: steps:

7
.gitignore vendored
View File

@ -50,7 +50,6 @@ env
venv* venv*
env_py3 env_py3
envpy3 envpy3
env36
local_config.py local_config.py
/superset_config.py /superset_config.py
/superset_text.yml /superset_text.yml
@ -66,7 +65,10 @@ superset-websocket/config.json
*.js.map *.js.map
node_modules node_modules
npm-debug.log* npm-debug.log*
superset/static/assets superset/static/assets/*
!superset/static/assets/.gitkeep
superset/static/uploads/*
!superset/static/uploads/.gitkeep
superset/static/version_info.json superset/static/version_info.json
superset-frontend/**/esm/* superset-frontend/**/esm/*
superset-frontend/**/lib/* superset-frontend/**/lib/*
@ -122,3 +124,4 @@ docker/*local*
# Jest test report # Jest test report
test-report.html test-report.html
superset/static/stats/statistics.html superset/static/stats/statistics.html
.aider*

View File

@ -60,6 +60,14 @@ repos:
- prettier@3.3.3 - prettier@3.3.3
args: ["--ignore-path=./superset-frontend/.prettierignore"] args: ["--ignore-path=./superset-frontend/.prettierignore"]
files: "superset-frontend" files: "superset-frontend"
- repo: local
hooks:
- id: eslint
name: eslint
entry: bash -c 'cd superset-frontend && npm run eslint -- $(echo "$@" | sed "s|superset-frontend/||g")'
language: system
pass_filenames: true
files: \.(js|jsx|ts|tsx)$
# blacklist unsafe functions like make_url (see #19526) # blacklist unsafe functions like make_url (see #19526)
- repo: https://github.com/skorokithakis/blacklist-pre-commit-hook - repo: https://github.com/skorokithakis/blacklist-pre-commit-hook
rev: e2f070289d8eddcaec0b580d3bde29437e7c8221 rev: e2f070289d8eddcaec0b580d3bde29437e7c8221

View File

@ -18,16 +18,19 @@
###################################################################### ######################################################################
# Node stage to deal with static asset construction # Node stage to deal with static asset construction
###################################################################### ######################################################################
ARG PY_VER=3.10-slim-bookworm ARG PY_VER=3.11-slim-bookworm
# If BUILDPLATFORM is null, set it to 'amd64' (or leave as is otherwise). # If BUILDPLATFORM is null, set it to 'amd64' (or leave as is otherwise).
ARG BUILDPLATFORM=${BUILDPLATFORM:-amd64} ARG BUILDPLATFORM=${BUILDPLATFORM:-amd64}
# Include translations in the final build
ARG BUILD_TRANSLATIONS="false"
###################################################################### ######################################################################
# superset-node used for building frontend assets # superset-node-ci used as a base for building frontend assets and CI
###################################################################### ######################################################################
FROM --platform=${BUILDPLATFORM} node:20-bullseye-slim AS superset-node FROM --platform=${BUILDPLATFORM} node:20-bullseye-slim AS superset-node-ci
ARG BUILD_TRANSLATIONS="false" # Include translations in the final build ARG BUILD_TRANSLATIONS
ENV BUILD_TRANSLATIONS=${BUILD_TRANSLATIONS} ENV BUILD_TRANSLATIONS=${BUILD_TRANSLATIONS}
ARG DEV_MODE="false" # Skip frontend build in dev mode ARG DEV_MODE="false" # Skip frontend build in dev mode
ENV DEV_MODE=${DEV_MODE} ENV DEV_MODE=${DEV_MODE}
@ -55,6 +58,10 @@ RUN mkdir -p /app/superset/static/assets \
/app/superset/translations /app/superset/translations
# Mount package files and install dependencies if not in dev mode # Mount package files and install dependencies if not in dev mode
# NOTE: we mount packages and plugins as they are referenced in package.json as workspaces
# ideally we'd COPY only their package.json. Here npm ci will be cached as long
# as the full content of these folders don't change, yielding a decent cache reuse rate.
# Note that's it's not possible selectively COPY of mount using blobs.
RUN --mount=type=bind,source=./superset-frontend/package.json,target=./package.json \ RUN --mount=type=bind,source=./superset-frontend/package.json,target=./package.json \
--mount=type=bind,source=./superset-frontend/package-lock.json,target=./package-lock.json \ --mount=type=bind,source=./superset-frontend/package-lock.json,target=./package-lock.json \
--mount=type=cache,target=/root/.cache \ --mount=type=cache,target=/root/.cache \
@ -68,9 +75,13 @@ RUN --mount=type=bind,source=./superset-frontend/package.json,target=./package.j
# Runs the webpack build process # Runs the webpack build process
COPY superset-frontend /app/superset-frontend COPY superset-frontend /app/superset-frontend
######################################################################
# superset-node used for compile frontend assets
######################################################################
FROM superset-node-ci AS superset-node
# Build the frontend if not in dev mode # Build the frontend if not in dev mode
RUN --mount=type=cache,target=/app/superset-frontend/.temp_cache \ RUN --mount=type=cache,target=/root/.npm \
--mount=type=cache,target=/root/.npm \
if [ "$DEV_MODE" = "false" ]; then \ if [ "$DEV_MODE" = "false" ]; then \
echo "Running 'npm run ${BUILD_CMD}'"; \ echo "Running 'npm run ${BUILD_CMD}'"; \
npm run ${BUILD_CMD}; \ npm run ${BUILD_CMD}; \
@ -93,21 +104,14 @@ RUN if [ "$BUILD_TRANSLATIONS" = "true" ]; then \
# Base python layer # Base python layer
###################################################################### ######################################################################
FROM python:${PY_VER} AS python-base FROM python:${PY_VER} AS python-base
ARG BUILD_TRANSLATIONS="false" # Include translations in the final build
ENV BUILD_TRANSLATIONS=${BUILD_TRANSLATIONS}
ARG DEV_MODE="false" # Skip frontend build in dev mode
ENV DEV_MODE=${DEV_MODE}
ENV LANG=C.UTF-8 \ ARG SUPERSET_HOME="/app/superset_home"
LC_ALL=C.UTF-8 \ ENV SUPERSET_HOME=${SUPERSET_HOME}
SUPERSET_ENV=production \
FLASK_APP="superset.app:create_app()" \
PYTHONPATH="/app/pythonpath" \
SUPERSET_HOME="/app/superset_home" \
SUPERSET_PORT=8088
RUN mkdir -p $SUPERSET_HOME
RUN useradd --user-group -d ${SUPERSET_HOME} -m --no-log-init --shell /bin/bash superset RUN useradd --user-group -d ${SUPERSET_HOME} -m --no-log-init --shell /bin/bash superset \
&& chmod -R 1777 $SUPERSET_HOME \
&& chown -R superset:superset $SUPERSET_HOME
# Some bash scripts needed throughout the layers # Some bash scripts needed throughout the layers
COPY --chmod=755 docker/*.sh /app/docker/ COPY --chmod=755 docker/*.sh /app/docker/
@ -118,28 +122,18 @@ RUN pip install --no-cache-dir --upgrade uv
RUN uv venv /app/.venv RUN uv venv /app/.venv
ENV PATH="/app/.venv/bin:${PATH}" ENV PATH="/app/.venv/bin:${PATH}"
# Install Playwright and optionally setup headless browsers
ARG INCLUDE_CHROMIUM="true"
ARG INCLUDE_FIREFOX="false"
RUN --mount=type=cache,target=/root/.cache/uv\
if [ "$INCLUDE_CHROMIUM" = "true" ] || [ "$INCLUDE_FIREFOX" = "true" ]; then \
uv pip install playwright && \
playwright install-deps && \
if [ "$INCLUDE_CHROMIUM" = "true" ]; then playwright install chromium; fi && \
if [ "$INCLUDE_FIREFOX" = "true" ]; then playwright install firefox; fi; \
else \
echo "Skipping browser installation"; \
fi
###################################################################### ######################################################################
# Python translation compiler layer # Python translation compiler layer
###################################################################### ######################################################################
FROM python-base AS python-translation-compiler FROM python-base AS python-translation-compiler
ARG BUILD_TRANSLATIONS
ENV BUILD_TRANSLATIONS=${BUILD_TRANSLATIONS}
# Install Python dependencies using docker/pip-install.sh # Install Python dependencies using docker/pip-install.sh
COPY requirements/translations.txt requirements/ COPY requirements/translations.txt requirements/
RUN --mount=type=cache,target=/root/.cache/uv \ RUN --mount=type=cache,target=/root/.cache/uv \
/app/docker/pip-install.sh --requires-build-essential -r requirements/translations.txt . /app/.venv/bin/activate && /app/docker/pip-install.sh --requires-build-essential -r requirements/translations.txt
COPY superset/translations/ /app/translations_mo/ COPY superset/translations/ /app/translations_mo/
RUN if [ "$BUILD_TRANSLATIONS" = "true" ]; then \ RUN if [ "$BUILD_TRANSLATIONS" = "true" ]; then \
@ -152,13 +146,20 @@ RUN if [ "$BUILD_TRANSLATIONS" = "true" ]; then \
# Python APP common layer # Python APP common layer
###################################################################### ######################################################################
FROM python-base AS python-common FROM python-base AS python-common
ENV SUPERSET_HOME="/app/superset_home" \
HOME="/app/superset_home" \
SUPERSET_ENV="production" \
FLASK_APP="superset.app:create_app()" \
PYTHONPATH="/app/pythonpath" \
SUPERSET_PORT="8088"
# Copy the entrypoints, make them executable in userspace # Copy the entrypoints, make them executable in userspace
COPY --chmod=755 docker/entrypoints /app/docker/entrypoints COPY --chmod=755 docker/entrypoints /app/docker/entrypoints
WORKDIR /app WORKDIR /app
# Set up necessary directories and user # Set up necessary directories and user
RUN mkdir -p \ RUN mkdir -p \
${SUPERSET_HOME} \
${PYTHONPATH} \ ${PYTHONPATH} \
superset/static \ superset/static \
requirements \ requirements \
@ -167,6 +168,19 @@ RUN mkdir -p \
requirements \ requirements \
&& touch superset/static/version_info.json && touch superset/static/version_info.json
# Install Playwright and optionally setup headless browsers
ARG INCLUDE_CHROMIUM="true"
ARG INCLUDE_FIREFOX="false"
RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
if [ "$INCLUDE_CHROMIUM" = "true" ] || [ "$INCLUDE_FIREFOX" = "true" ]; then \
uv pip install playwright && \
playwright install-deps && \
if [ "$INCLUDE_CHROMIUM" = "true" ]; then playwright install chromium; fi && \
if [ "$INCLUDE_FIREFOX" = "true" ]; then playwright install firefox; fi; \
else \
echo "Skipping browser installation"; \
fi
# Copy required files for Python build # Copy required files for Python build
COPY pyproject.toml setup.py MANIFEST.in README.md ./ COPY pyproject.toml setup.py MANIFEST.in README.md ./
COPY superset-frontend/package.json superset-frontend/ COPY superset-frontend/package.json superset-frontend/
@ -209,12 +223,11 @@ FROM python-common AS lean
# Install Python dependencies using docker/pip-install.sh # Install Python dependencies using docker/pip-install.sh
COPY requirements/base.txt requirements/ COPY requirements/base.txt requirements/
RUN --mount=type=cache,target=/root/.cache/uv \ RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
/app/docker/pip-install.sh --requires-build-essential -r requirements/base.txt /app/docker/pip-install.sh --requires-build-essential -r requirements/base.txt
# Install the superset package # Install the superset package
RUN --mount=type=cache,target=/root/.cache/uv \ RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
uv pip install . uv pip install .
RUN python -m compileall /app/superset RUN python -m compileall /app/superset
USER superset USER superset
@ -233,12 +246,13 @@ RUN /app/docker/apt-install.sh \
# Copy development requirements and install them # Copy development requirements and install them
COPY requirements/*.txt requirements/ COPY requirements/*.txt requirements/
# Install Python dependencies using docker/pip-install.sh # Install Python dependencies using docker/pip-install.sh
RUN --mount=type=cache,target=/root/.cache/uv \ RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
/app/docker/pip-install.sh --requires-build-essential -r requirements/development.txt /app/docker/pip-install.sh --requires-build-essential -r requirements/development.txt
# Install the superset package # Install the superset package
RUN --mount=type=cache,target=/root/.cache/uv \ RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
uv pip install . uv pip install .
RUN uv pip install .[postgres]
RUN python -m compileall /app/superset RUN python -m compileall /app/superset
USER superset USER superset
@ -247,5 +261,7 @@ USER superset
# CI image... # CI image...
###################################################################### ######################################################################
FROM lean AS ci FROM lean AS ci
USER root
RUN uv pip install .[postgres]
USER superset
CMD ["/app/docker/entrypoints/docker-ci.sh"] CMD ["/app/docker/entrypoints/docker-ci.sh"]

View File

@ -137,6 +137,7 @@ Here are some of the major database solutions that are supported:
<img src="https://superset.apache.org/img/databases/sap-hana.png" alt="oceanbase" border="0" width="220" /> <img src="https://superset.apache.org/img/databases/sap-hana.png" alt="oceanbase" border="0" width="220" />
<img src="https://superset.apache.org/img/databases/denodo.png" alt="denodo" border="0" width="200" /> <img src="https://superset.apache.org/img/databases/denodo.png" alt="denodo" border="0" width="200" />
<img src="https://superset.apache.org/img/databases/ydb.svg" alt="ydb" border="0" width="200" /> <img src="https://superset.apache.org/img/databases/ydb.svg" alt="ydb" border="0" width="200" />
<img src="https://superset.apache.org/img/databases/tdengine.png" alt="TDengine" border="0" width="200" />
</p> </p>
**A more comprehensive list of supported databases** along with the configuration instructions can be found [here](https://superset.apache.org/docs/configuration/databases). **A more comprehensive list of supported databases** along with the configuration instructions can be found [here](https://superset.apache.org/docs/configuration/databases).

View File

@ -30,12 +30,12 @@ RUN apt-get install -y apt-transport-https apt-utils
# Install superset dependencies # Install superset dependencies
# https://superset.apache.org/docs/installation/installing-superset-from-scratch # https://superset.apache.org/docs/installation/installing-superset-from-scratch
RUN apt-get install -y build-essential libssl-dev \ RUN apt-get install -y build-essential libssl-dev \
libffi-dev python3-dev libsasl2-dev libldap2-dev libxi-dev chromium libffi-dev python3-dev libsasl2-dev libldap2-dev libxi-dev chromium zstd
# Install nodejs for custom build # Install nodejs for custom build
# https://nodejs.org/en/download/package-manager/ # https://nodejs.org/en/download/package-manager/
RUN set -eux; \ RUN set -eux; \
curl -sL https://deb.nodesource.com/setup_18.x | bash -; \ curl -sL https://deb.nodesource.com/setup_20.x | bash -; \
apt-get install -y nodejs; \ apt-get install -y nodejs; \
node --version; node --version;
RUN if ! which npm; then apt-get install -y npm; fi RUN if ! which npm; then apt-get install -y npm; fi
@ -64,7 +64,7 @@ RUN pip install --upgrade setuptools pip \
RUN flask fab babel-compile --target superset/translations RUN flask fab babel-compile --target superset/translations
ENV PATH=/home/superset/superset/bin:$PATH \ ENV PATH=/home/superset/superset/bin:$PATH \
PYTHONPATH=/home/superset/superset/:$PYTHONPATH \ PYTHONPATH=/home/superset/superset/ \
SUPERSET_TESTENV=true SUPERSET_TESTENV=true
COPY from_tarball_entrypoint.sh /entrypoint.sh COPY from_tarball_entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"] ENTRYPOINT ["/entrypoint.sh"]

View File

@ -29,13 +29,16 @@ RUN apt-get install -y apt-transport-https apt-utils
# Install superset dependencies # Install superset dependencies
# https://superset.apache.org/docs/installation/installing-superset-from-scratch # https://superset.apache.org/docs/installation/installing-superset-from-scratch
RUN apt-get install -y build-essential libssl-dev \ RUN apt-get install -y subversion build-essential libssl-dev \
libffi-dev python3-dev libsasl2-dev libldap2-dev libxi-dev chromium libffi-dev python3-dev libsasl2-dev libldap2-dev libxi-dev chromium zstd
# Install nodejs for custom build # Install nodejs for custom build
# https://nodejs.org/en/download/package-manager/ # https://nodejs.org/en/download/package-manager/
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - \ RUN set -eux; \
&& apt-get install -y nodejs curl -sL https://deb.nodesource.com/setup_20.x | bash -; \
apt-get install -y nodejs; \
node --version;
RUN if ! which npm; then apt-get install -y npm; fi
RUN mkdir -p /home/superset RUN mkdir -p /home/superset
RUN chown superset /home/superset RUN chown superset /home/superset
@ -46,14 +49,12 @@ ARG VERSION
# Can fetch source from svn or copy tarball from local mounted directory # Can fetch source from svn or copy tarball from local mounted directory
RUN svn co https://dist.apache.org/repos/dist/dev/superset/$VERSION ./ RUN svn co https://dist.apache.org/repos/dist/dev/superset/$VERSION ./
RUN tar -xvf *.tar.gz RUN tar -xvf *.tar.gz
WORKDIR apache-superset-$VERSION WORKDIR /home/superset/apache-superset-$VERSION/superset-frontend
RUN cd superset-frontend \ RUN npm ci \
&& npm ci \
&& npm run build \ && npm run build \
&& rm -rf node_modules && rm -rf node_modules
WORKDIR /home/superset/apache-superset-$VERSION WORKDIR /home/superset/apache-superset-$VERSION
RUN pip install --upgrade setuptools pip \ RUN pip install --upgrade setuptools pip \
&& pip install -r requirements/base.txt \ && pip install -r requirements/base.txt \
@ -62,6 +63,6 @@ RUN pip install --upgrade setuptools pip \
RUN flask fab babel-compile --target superset/translations RUN flask fab babel-compile --target superset/translations
ENV PATH=/home/superset/superset/bin:$PATH \ ENV PATH=/home/superset/superset/bin:$PATH \
PYTHONPATH=/home/superset/superset/:$PYTHONPATH PYTHONPATH=/home/superset/superset/
COPY from_tarball_entrypoint.sh /entrypoint.sh COPY from_tarball_entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"] ENTRYPOINT ["/entrypoint.sh"]

View File

@ -44,8 +44,8 @@ These features are **finished** but currently being tested. They are usable, but
- ALLOW_FULL_CSV_EXPORT - ALLOW_FULL_CSV_EXPORT
- CACHE_IMPERSONATION - CACHE_IMPERSONATION
- CONFIRM_DASHBOARD_DIFF - CONFIRM_DASHBOARD_DIFF
- DRILL_TO_DETAIL
- DYNAMIC_PLUGINS - DYNAMIC_PLUGINS
- DATE_FORMAT_IN_EMAIL_SUBJECT: [(docs)](https://superset.apache.org/docs/configuration/alerts-reports#commons)
- ENABLE_SUPERSET_META_DB: [(docs)](https://superset.apache.org/docs/configuration/databases/#querying-across-databases) - ENABLE_SUPERSET_META_DB: [(docs)](https://superset.apache.org/docs/configuration/databases/#querying-across-databases)
- ESTIMATE_QUERY_COST - ESTIMATE_QUERY_COST
- GLOBAL_ASYNC_QUERIES [(docs)](https://github.com/apache/superset/blob/master/CONTRIBUTING.md#async-chart-queries) - GLOBAL_ASYNC_QUERIES [(docs)](https://github.com/apache/superset/blob/master/CONTRIBUTING.md#async-chart-queries)
@ -63,9 +63,8 @@ These features flags are **safe for production**. They have been tested and will
[//]: # "PLEASE KEEP THESE LISTS SORTED ALPHABETICALLY" [//]: # "PLEASE KEEP THESE LISTS SORTED ALPHABETICALLY"
### Flags on the path to feature launch and flag deprecation/removal ### Flags on the path to feature launch and flag deprecation/removal
- DASHBOARD_VIRTUALIZATION - DASHBOARD_VIRTUALIZATION
- DRILL_BY
- DISABLE_LEGACY_DATASOURCE_EDITOR
### Flags retained for runtime configuration ### Flags retained for runtime configuration
@ -79,6 +78,7 @@ independently. This new framework will also allow for non-boolean configurations
- ALLOW_ADHOC_SUBQUERY - ALLOW_ADHOC_SUBQUERY
- DASHBOARD_RBAC [(docs)](https://superset.apache.org/docs/using-superset/creating-your-first-dashboard#manage-access-to-dashboards) - DASHBOARD_RBAC [(docs)](https://superset.apache.org/docs/using-superset/creating-your-first-dashboard#manage-access-to-dashboards)
- DATAPANEL_CLOSED_BY_DEFAULT - DATAPANEL_CLOSED_BY_DEFAULT
- DRILL_BY
- DRUID_JOINS - DRUID_JOINS
- EMBEDDABLE_CHARTS - EMBEDDABLE_CHARTS
- EMBEDDED_SUPERSET - EMBEDDED_SUPERSET
@ -98,6 +98,6 @@ These features flags currently default to True and **will be removed in a future
[//]: # "PLEASE KEEP THE LIST SORTED ALPHABETICALLY" [//]: # "PLEASE KEEP THE LIST SORTED ALPHABETICALLY"
- AVOID_COLORS_COLLISION - AVOID_COLORS_COLLISION
- DASHBOARD_CROSS_FILTERS - DRILL_TO_DETAIL
- ENABLE_JAVASCRIPT_CONTROLS - ENABLE_JAVASCRIPT_CONTROLS
- KV_STORE - KV_STORE

View File

@ -43,10 +43,12 @@ Join our growing community!
- [Capital Service S.A.](https://capitalservice.pl) [@pkonarzewski] - [Capital Service S.A.](https://capitalservice.pl) [@pkonarzewski]
- [Clark.de](https://clark.de/) - [Clark.de](https://clark.de/)
- [KarrotPay](https://www.daangnpay.com/) - [KarrotPay](https://www.daangnpay.com/)
- [Remita](https://remita.net) [@mujibishola]
- [Taveo](https://www.taveo.com) [@codek] - [Taveo](https://www.taveo.com) [@codek]
- [Unit](https://www.unit.co/about-us) [@amitmiran137] - [Unit](https://www.unit.co/about-us) [@amitmiran137]
- [Wise](https://wise.com) [@koszti] - [Wise](https://wise.com) [@koszti]
- [Xendit](https://xendit.co/) [@LieAlbertTriAdrian] - [Xendit](https://xendit.co/) [@LieAlbertTriAdrian]
- [Cover Genius](https://covergenius.com/)
### Gaming ### Gaming
- [Popoko VM Games Studio](https://popoko.live) - [Popoko VM Games Studio](https://popoko.live)
@ -58,19 +60,23 @@ Join our growing community!
- [Dropit Shopping](https://www.dropit.shop/) [@dropit-dev] - [Dropit Shopping](https://www.dropit.shop/) [@dropit-dev]
- [Fanatics](https://www.fanatics.com/) [@coderfender] - [Fanatics](https://www.fanatics.com/) [@coderfender]
- [Fordeal](https://www.fordeal.com) [@Renkai] - [Fordeal](https://www.fordeal.com) [@Renkai]
- [Fynd](https://www.fynd.com/) [@darpanjain07]
- [GFG - Global Fashion Group](https://global-fashion-group.com) [@ksaagariconic] - [GFG - Global Fashion Group](https://global-fashion-group.com) [@ksaagariconic]
- [GoTo/Gojek](https://www.gojek.io/) [@gwthm-in]
- [HuiShouBao](https://www.huishoubao.com/) [@Yukinoshita-Yukino] - [HuiShouBao](https://www.huishoubao.com/) [@Yukinoshita-Yukino]
- [Now](https://www.now.vn/) [@davidkohcw] - [Now](https://www.now.vn/) [@davidkohcw]
- [Qunar](https://www.qunar.com/) [@flametest] - [Qunar](https://www.qunar.com/) [@flametest]
- [Rakuten Viki](https://www.viki.com) - [Rakuten Viki](https://www.viki.com)
- [Shopee](https://shopee.sg) [@xiaohanyu] - [Shopee](https://shopee.sg) [@xiaohanyu]
- [Shopkick](https://www.shopkick.com) [@LAlbertalli] - [Shopkick](https://www.shopkick.com) [@LAlbertalli]
- [ShopUp](https://www.shopup.org/) [@gwthm-in]
- [Tails.com](https://tails.com/gb/) [@alanmcruickshank] - [Tails.com](https://tails.com/gb/) [@alanmcruickshank]
- [THE ICONIC](https://theiconic.com.au/) [@ksaagariconic] - [THE ICONIC](https://theiconic.com.au/) [@ksaagariconic]
- [Utair](https://www.utair.ru) [@utair-digital] - [Utair](https://www.utair.ru) [@utair-digital]
- [VkusVill](https://vkusvill.ru/) [@ETselikov] - [VkusVill](https://vkusvill.ru/) [@ETselikov]
- [Zalando](https://www.zalando.com) [@dmigo] - [Zalando](https://www.zalando.com) [@dmigo]
- [Zalora](https://www.zalora.com) [@ksaagariconic] - [Zalora](https://www.zalora.com) [@ksaagariconic]
- [Zepto](https://www.zeptonow.com/) [@gwthm-in]
### Enterprise Technology ### Enterprise Technology
- [A3Data](https://a3data.com.br) [@neylsoncrepalde] - [A3Data](https://a3data.com.br) [@neylsoncrepalde]
@ -139,6 +145,7 @@ Join our growing community!
### Education ### Education
- [Aveti Learning](https://avetilearning.com/) [@TheShubhendra] - [Aveti Learning](https://avetilearning.com/) [@TheShubhendra]
- [Brilliant.org](https://brilliant.org/) - [Brilliant.org](https://brilliant.org/)
- [Open edX](https://openedx.org/)
- [Platzi.com](https://platzi.com/) - [Platzi.com](https://platzi.com/)
- [Sunbird](https://www.sunbird.org/) [@eksteporg] - [Sunbird](https://www.sunbird.org/) [@eksteporg]
- [The GRAPH Network](https://thegraphnetwork.org/) [@fccoelho] - [The GRAPH Network](https://thegraphnetwork.org/) [@fccoelho]

View File

@ -24,13 +24,28 @@ assists people when migrating to a new version.
## Next ## Next
- [31976](https://github.com/apache/superset/pull/31976) Removed the `DISABLE_LEGACY_DATASOURCE_EDITOR` feature flag. The previous value of the feature flag was `True` and now the feature is permanently removed.
- [31959](https://github.com/apache/superset/pull/32000) Removes CSV_UPLOAD_MAX_SIZE config, use your web server to control file upload size.
- [31959](https://github.com/apache/superset/pull/31959) Removes the following endpoints from data uploads: `/api/v1/database/<id>/<file type>_upload` and `/api/v1/database/<file type>_metadata`, in favour of new one (Details on the PR). And simplifies permissions.
- [31844](https://github.com/apache/superset/pull/31844) The `ALERT_REPORTS_EXECUTE_AS` and `THUMBNAILS_EXECUTE_AS` config parameters have been renamed to `ALERT_REPORTS_EXECUTORS` and `THUMBNAILS_EXECUTORS` respectively. A new config flag `CACHE_WARMUP_EXECUTORS` has also been introduced to be able to control which user is used to execute cache warmup tasks. Finally, the config flag `THUMBNAILS_SELENIUM_USER` has been removed. To use a fixed executor for async tasks, use the new `FixedExecutor` class. See the config and docs for more info on setting up different executor profiles.
- [31894](https://github.com/apache/superset/pull/31894) Domain sharding is deprecated in favor of HTTP2. The `SUPERSET_WEBSERVER_DOMAINS` configuration will be removed in the next major version (6.0)
- [31794](https://github.com/apache/superset/pull/31794) Removed the previously deprecated `DASHBOARD_CROSS_FILTERS` feature flag
- [31774](https://github.com/apache/superset/pull/31774): Fixes the spelling of the `USE-ANALAGOUS-COLORS` feature flag. Please update any scripts/configuration item to use the new/corrected `USE-ANALOGOUS-COLORS` flag spelling.
- [31582](https://github.com/apache/superset/pull/31582) Removed the legacy Area, Bar, Event Flow, Heatmap, Histogram, Line, Sankey, and Sankey Loop charts. They were all automatically migrated to their ECharts counterparts with the exception of the Event Flow and Sankey Loop charts which were removed as they were not actively maintained and not widely used. If you were using the Event Flow or Sankey Loop charts, you will need to find an alternative solution.
- [31198](https://github.com/apache/superset/pull/31198) Disallows by default the use of the following ClickHouse functions: "version", "currentDatabase", "hostName". - [31198](https://github.com/apache/superset/pull/31198) Disallows by default the use of the following ClickHouse functions: "version", "currentDatabase", "hostName".
- [29798](https://github.com/apache/superset/pull/29798) Since 3.1.0, the intial schedule for an alert or report was mistakenly offset by the specified timezone's relation to UTC. The initial schedule should now begin at the correct time. - [29798](https://github.com/apache/superset/pull/29798) Since 3.1.0, the intial schedule for an alert or report was mistakenly offset by the specified timezone's relation to UTC. The initial schedule should now begin at the correct time.
- [30021](https://github.com/apache/superset/pull/30021) The `dev` layer in our Dockerfile no long includes firefox binaries, only Chromium to reduce bloat/docker-build-time. - [30021](https://github.com/apache/superset/pull/30021) The `dev` layer in our Dockerfile no long includes firefox binaries, only Chromium to reduce bloat/docker-build-time.
- [30099](https://github.com/apache/superset/pull/30099) Translations are no longer included in the default docker image builds. If your environment requires translations, you'll want to set the docker build arg `BUILD_TRANSACTION=true`. - [30099](https://github.com/apache/superset/pull/30099) Translations are no longer included in the default docker image builds. If your environment requires translations, you'll want to set the docker build arg `BUILD_TRANSACTION=true`.
- [31262](https://github.com/apache/superset/pull/31262) NOTE: deprecated `pylint` in favor of `ruff` as our only python linter. Only affect development workflows positively (not the release itself). It should cover most important rules, be much faster, but some things linting rules that were enforced before may not be enforce in the exact same way as before. - [31262](https://github.com/apache/superset/pull/31262) NOTE: deprecated `pylint` in favor of `ruff` as our only python linter. Only affect development workflows positively (not the release itself). It should cover most important rules, be much faster, but some things linting rules that were enforced before may not be enforce in the exact same way as before.
- [31173](https://github.com/apache/superset/pull/31173) Modified `fetch_csrf_token` to align with HTTP standards, particularly regarding how cookies are handled. If you encounter any issues related to CSRF functionality, please report them as a new issue and reference this PR for context. - [31173](https://github.com/apache/superset/pull/31173) Modified `fetch_csrf_token` to align with HTTP standards, particularly regarding how cookies are handled. If you encounter any issues related to CSRF functionality, please report them as a new issue and reference this PR for context.
- [31413](https://github.com/apache/superset/pull/31413) Enable the DATE_FORMAT_IN_EMAIL_SUBJECT feature flag to allow users to specify a date format for the email subject, which will then be replaced with the actual date.
- [31385](https://github.com/apache/superset/pull/31385) Significant docker refactor, reducing access levels for the `superset` user, streamlining layer building, ... - [31385](https://github.com/apache/superset/pull/31385) Significant docker refactor, reducing access levels for the `superset` user, streamlining layer building, ...
- [31503](https://github.com/apache/superset/pull/31503) Deprecating python 3.9.x support, 3.11 is now the recommended version and 3.10 is still supported over the Superset 5.0 lifecycle.
- [29121](https://github.com/apache/superset/pull/29121) Removed the `css`, `position_json`, and `json_metadata` from the payload of the dashboard list endpoint (`GET api/v1/dashboard`) for performance reasons.
- [29163](https://github.com/apache/superset/pull/29163) Removed the `SHARE_QUERIES_VIA_KV_STORE` and `KV_STORE` feature flags and changed the way Superset shares SQL Lab queries to use permalinks. The legacy `/kv` API was removed but we still support legacy links in 5.0. In 6.0, only permalinks will be supported.
- [25166](https://github.com/apache/superset/pull/25166) Changed the default configuration of `UPLOAD_FOLDER` from `/app/static/uploads/` to `/static/uploads/`. It also removed the unused `IMG_UPLOAD_FOLDER` and `IMG_UPLOAD_URL` configuration options.
- [30284](https://github.com/apache/superset/pull/30284) Deprecated GLOBAL_ASYNC_QUERIES_REDIS_CONFIG in favor of the new GLOBAL_ASYNC_QUERIES_CACHE_BACKEND configuration. To leverage Redis Sentinel, set CACHE_TYPE to RedisSentinelCache, or use RedisCache for standalone Redis
- [31961](https://github.com/apache/superset/pull/31961) Upgraded React from version 16.13.1 to 17.0.2. If you are using custom frontend extensions or plugins, you may need to update them to be compatible with React 17.
### Potential Downtime ### Potential Downtime

View File

@ -22,9 +22,6 @@
# unique random secure passwords and SECRET_KEY. # unique random secure passwords and SECRET_KEY.
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
x-superset-image: &superset-image apachesuperset.docker.scarf.sh/apache/superset:${TAG:-latest-dev} x-superset-image: &superset-image apachesuperset.docker.scarf.sh/apache/superset:${TAG:-latest-dev}
x-superset-depends-on: &superset-depends-on
- db
- redis
x-superset-volumes: x-superset-volumes:
&superset-volumes # /app/pythonpath_docker will be appended to the PYTHONPATH in the final container &superset-volumes # /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
- ./docker:/app/docker - ./docker:/app/docker
@ -64,8 +61,12 @@ services:
restart: unless-stopped restart: unless-stopped
ports: ports:
- 8088:8088 - 8088:8088
depends_on: *superset-depends-on depends_on:
superset-init:
condition: service_completed_successfully
volumes: *superset-volumes volumes: *superset-volumes
environment:
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
superset-init: superset-init:
image: *superset-image image: *superset-image
@ -76,11 +77,18 @@ services:
required: true required: true
- path: docker/.env-local # optional override - path: docker/.env-local # optional override
required: false required: false
depends_on: *superset-depends-on depends_on:
db:
condition: service_started
redis:
condition: service_started
user: "root" user: "root"
volumes: *superset-volumes volumes: *superset-volumes
healthcheck: healthcheck:
disable: true disable: true
environment:
SUPERSET_LOAD_EXAMPLES: "${SUPERSET_LOAD_EXAMPLES:-yes}"
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
superset-worker: superset-worker:
image: *superset-image image: *superset-image
@ -92,7 +100,9 @@ services:
- path: docker/.env-local # optional override - path: docker/.env-local # optional override
required: false required: false
restart: unless-stopped restart: unless-stopped
depends_on: *superset-depends-on depends_on:
superset-init:
condition: service_completed_successfully
user: "root" user: "root"
volumes: *superset-volumes volumes: *superset-volumes
healthcheck: healthcheck:
@ -101,6 +111,8 @@ services:
"CMD-SHELL", "CMD-SHELL",
"celery -A superset.tasks.celery_app:app inspect ping -d celery@$$HOSTNAME", "celery -A superset.tasks.celery_app:app inspect ping -d celery@$$HOSTNAME",
] ]
environment:
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
superset-worker-beat: superset-worker-beat:
image: *superset-image image: *superset-image
@ -112,11 +124,15 @@ services:
- path: docker/.env-local # optional override - path: docker/.env-local # optional override
required: false required: false
restart: unless-stopped restart: unless-stopped
depends_on: *superset-depends-on depends_on:
superset-init:
condition: service_completed_successfully
user: "root" user: "root"
volumes: *superset-volumes volumes: *superset-volumes
healthcheck: healthcheck:
disable: true disable: true
environment:
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
volumes: volumes:
superset_home: superset_home:

View File

@ -21,9 +21,6 @@
# create you own docker environment file (docker/.env) with your own # create you own docker environment file (docker/.env) with your own
# unique random secure passwords and SECRET_KEY. # unique random secure passwords and SECRET_KEY.
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
x-superset-depends-on: &superset-depends-on
- db
- redis
x-superset-volumes: x-superset-volumes:
&superset-volumes # /app/pythonpath_docker will be appended to the PYTHONPATH in the final container &superset-volumes # /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
- ./docker:/app/docker - ./docker:/app/docker
@ -70,8 +67,12 @@ services:
restart: unless-stopped restart: unless-stopped
ports: ports:
- 8088:8088 - 8088:8088
depends_on: *superset-depends-on depends_on:
superset-init:
condition: service_completed_successfully
volumes: *superset-volumes volumes: *superset-volumes
environment:
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
superset-init: superset-init:
container_name: superset_init container_name: superset_init
@ -83,11 +84,18 @@ services:
required: true required: true
- path: docker/.env-local # optional override - path: docker/.env-local # optional override
required: false required: false
depends_on: *superset-depends-on depends_on:
db:
condition: service_started
redis:
condition: service_started
user: "root" user: "root"
volumes: *superset-volumes volumes: *superset-volumes
healthcheck: healthcheck:
disable: true disable: true
environment:
SUPERSET_LOAD_EXAMPLES: "${SUPERSET_LOAD_EXAMPLES:-yes}"
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
superset-worker: superset-worker:
build: build:
@ -100,7 +108,9 @@ services:
- path: docker/.env-local # optional override - path: docker/.env-local # optional override
required: false required: false
restart: unless-stopped restart: unless-stopped
depends_on: *superset-depends-on depends_on:
superset-init:
condition: service_completed_successfully
user: "root" user: "root"
volumes: *superset-volumes volumes: *superset-volumes
healthcheck: healthcheck:
@ -109,6 +119,8 @@ services:
"CMD-SHELL", "CMD-SHELL",
"celery -A superset.tasks.celery_app:app inspect ping -d celery@$$HOSTNAME", "celery -A superset.tasks.celery_app:app inspect ping -d celery@$$HOSTNAME",
] ]
environment:
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
superset-worker-beat: superset-worker-beat:
build: build:
@ -121,11 +133,15 @@ services:
- path: docker/.env-local # optional override - path: docker/.env-local # optional override
required: false required: false
restart: unless-stopped restart: unless-stopped
depends_on: *superset-depends-on depends_on:
superset-init:
condition: service_completed_successfully
user: "root" user: "root"
volumes: *superset-volumes volumes: *superset-volumes
healthcheck: healthcheck:
disable: true disable: true
environment:
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
volumes: volumes:
superset_home: superset_home:

View File

@ -22,9 +22,6 @@
# unique random secure passwords and SECRET_KEY. # unique random secure passwords and SECRET_KEY.
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
x-superset-user: &superset-user root x-superset-user: &superset-user root
x-superset-depends-on: &superset-depends-on
- db
- redis
x-superset-volumes: &superset-volumes x-superset-volumes: &superset-volumes
# /app/pythonpath_docker will be appended to the PYTHONPATH in the final container # /app/pythonpath_docker will be appended to the PYTHONPATH in the final container
- ./docker:/app/docker - ./docker:/app/docker
@ -90,15 +87,20 @@ services:
container_name: superset_app container_name: superset_app
command: ["/app/docker/docker-bootstrap.sh", "app"] command: ["/app/docker/docker-bootstrap.sh", "app"]
restart: unless-stopped restart: unless-stopped
# ports: ports:
# - 8088:8088 - 8088:8088
# When in cypress-mode ->
- 8081:8081
extra_hosts: extra_hosts:
- "host.docker.internal:host-gateway" - "host.docker.internal:host-gateway"
user: *superset-user user: *superset-user
depends_on: *superset-depends-on depends_on:
superset-init:
condition: service_completed_successfully
volumes: *superset-volumes volumes: *superset-volumes
environment: environment:
CYPRESS_CONFIG: "${CYPRESS_CONFIG:-}" CYPRESS_CONFIG: "${CYPRESS_CONFIG:-}"
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
superset-websocket: superset-websocket:
container_name: superset_websocket container_name: superset_websocket
@ -143,11 +145,17 @@ services:
required: true required: true
- path: docker/.env-local # optional override - path: docker/.env-local # optional override
required: false required: false
depends_on: *superset-depends-on depends_on:
db:
condition: service_started
redis:
condition: service_started
user: *superset-user user: *superset-user
volumes: *superset-volumes volumes: *superset-volumes
environment: environment:
CYPRESS_CONFIG: "${CYPRESS_CONFIG:-}" CYPRESS_CONFIG: "${CYPRESS_CONFIG:-}"
SUPERSET_LOAD_EXAMPLES: "${SUPERSET_LOAD_EXAMPLES:-yes}"
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
healthcheck: healthcheck:
disable: true disable: true
@ -167,6 +175,10 @@ services:
BUILD_SUPERSET_FRONTEND_IN_DOCKER: true BUILD_SUPERSET_FRONTEND_IN_DOCKER: true
NPM_RUN_PRUNE: false NPM_RUN_PRUNE: false
SCARF_ANALYTICS: "${SCARF_ANALYTICS:-}" SCARF_ANALYTICS: "${SCARF_ANALYTICS:-}"
# configuring the dev-server to use the host.docker.internal to connect to the backend
superset: "http://host.docker.internal:8088"
ports:
- "127.0.0.1:9000:9000" # exposing the dynamic webpack dev server
container_name: superset_node container_name: superset_node
command: ["/app/docker/docker-frontend.sh"] command: ["/app/docker/docker-frontend.sh"]
env_file: env_file:
@ -174,7 +186,6 @@ services:
required: true required: true
- path: docker/.env-local # optional override - path: docker/.env-local # optional override
required: false required: false
depends_on: *superset-depends-on
volumes: *superset-volumes volumes: *superset-volumes
superset-worker: superset-worker:
@ -189,8 +200,12 @@ services:
required: false required: false
environment: environment:
CELERYD_CONCURRENCY: 2 CELERYD_CONCURRENCY: 2
CYPRESS_CONFIG: "${CYPRESS_CONFIG:-}"
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
restart: unless-stopped restart: unless-stopped
depends_on: *superset-depends-on depends_on:
superset-init:
condition: service_completed_successfully
user: *superset-user user: *superset-user
volumes: *superset-volumes volumes: *superset-volumes
extra_hosts: extra_hosts:
@ -212,11 +227,15 @@ services:
- path: docker/.env-local # optional override - path: docker/.env-local # optional override
required: false required: false
restart: unless-stopped restart: unless-stopped
depends_on: *superset-depends-on depends_on:
- superset-worker
user: *superset-user user: *superset-user
volumes: *superset-volumes volumes: *superset-volumes
healthcheck: healthcheck:
disable: true disable: true
environment:
CYPRESS_CONFIG: "${CYPRESS_CONFIG:-}"
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
superset-tests-worker: superset-tests-worker:
build: build:
@ -237,8 +256,11 @@ services:
REDIS_RESULTS_DB: 3 REDIS_RESULTS_DB: 3
REDIS_HOST: localhost REDIS_HOST: localhost
CELERYD_CONCURRENCY: 8 CELERYD_CONCURRENCY: 8
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
network_mode: host network_mode: host
depends_on: *superset-depends-on depends_on:
superset-init:
condition: service_completed_successfully
user: *superset-user user: *superset-user
volumes: *superset-volumes volumes: *superset-volumes
healthcheck: healthcheck:

View File

@ -15,6 +15,8 @@
# limitations under the License. # limitations under the License.
# #
# Allowing python to print() in docker
PYTHONUNBUFFERED=1
COMPOSE_PROJECT_NAME=superset COMPOSE_PROJECT_NAME=superset
DEV_MODE=true DEV_MODE=true
@ -64,3 +66,4 @@ SUPERSET_SECRET_KEY=TEST_NON_DEV_SECRET
ENABLE_PLAYWRIGHT=false ENABLE_PLAYWRIGHT=false
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
BUILD_SUPERSET_FRONTEND_IN_DOCKER=true BUILD_SUPERSET_FRONTEND_IN_DOCKER=true
SUPERSET_LOG_LEVEL=info

View File

@ -68,7 +68,7 @@ Don't forget to reload the page to take the new frontend into account though.
## Production ## Production
It is possible to run Superset in non-development mode by using [`docker-compose-non-dev.yml`](../docker-compose-non-dev.yml). This file excludes the volumes needed for development and uses [`./docker/.env-non-dev`](./.env-non-dev) which sets the variable `SUPERSET_ENV` to `production`. It is possible to run Superset in non-development mode by using [`docker-compose-non-dev.yml`](../docker-compose-non-dev.yml). This file excludes the volumes needed for development.
## Resource Constraints ## Resource Constraints

View File

@ -18,24 +18,32 @@
set -eo pipefail set -eo pipefail
# UV may not be installed in older images
pip install uv
# Make python interactive # Make python interactive
if [ "$DEV_MODE" == "true" ]; then if [ "$DEV_MODE" == "true" ]; then
echo "Reinstalling the app in editable mode" if [ "$(whoami)" = "root" ] && command -v uv > /dev/null 2>&1; then
uv pip install -e . echo "Reinstalling the app in editable mode"
uv pip install -e .
fi
fi fi
REQUIREMENTS_LOCAL="/app/docker/requirements-local.txt" REQUIREMENTS_LOCAL="/app/docker/requirements-local.txt"
PORT=${PORT:-8088}
# If Cypress run overwrite the password for admin and export env variables # If Cypress run overwrite the password for admin and export env variables
if [ "$CYPRESS_CONFIG" == "true" ]; then if [ "$CYPRESS_CONFIG" == "true" ]; then
export SUPERSET_CONFIG=tests.integration_tests.superset_test_config
export SUPERSET_TESTENV=true export SUPERSET_TESTENV=true
export SUPERSET__SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://superset:superset@db:5432/superset export POSTGRES_DB=superset_cypress
export SUPERSET__SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://superset:superset@db:5432/superset_cypress
PORT=8081
fi fi
if [[ "$DATABASE_DIALECT" == postgres* ]] ; then if [[ "$DATABASE_DIALECT" == postgres* ]] && [ "$(whoami)" = "root" ]; then
# older images may not have the postgres dev requirements installed
echo "Installing postgres requirements" echo "Installing postgres requirements"
uv pip install -e .[postgres] if command -v uv > /dev/null 2>&1; then
# Use uv in newer images
uv pip install -e .[postgres]
else
# Use pip in older images
pip install -e .[postgres]
fi
fi fi
# #
# Make sure we have dev requirements installed # Make sure we have dev requirements installed
@ -60,7 +68,7 @@ case "${1}" in
;; ;;
app) app)
echo "Starting web app (using development server)..." echo "Starting web app (using development server)..."
flask run -p 8088 --with-threads --reload --debugger --host=0.0.0.0 flask run -p $PORT --with-threads --reload --debugger --host=0.0.0.0
;; ;;
app-gunicorn) app-gunicorn)
echo "Starting web app..." echo "Starting web app..."

View File

@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ------------------------------------------------------------------------
# Creates the examples database and respective user. This database location
# and access credentials are defined on the environment variables
# ------------------------------------------------------------------------
set -e
psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER}" <<-EOSQL
CREATE DATABASE superset_cypress;
EOSQL

View File

@ -36,7 +36,9 @@ if [ "$BUILD_SUPERSET_FRONTEND_IN_DOCKER" = "true" ]; then
npm install npm install
echo "Start webpack dev server" echo "Start webpack dev server"
npm run dev # start the webpack dev server, serving dynamically at http://localhost:9000
# it proxies to the backend served at http://localhost:8088
npm run dev-server
else else
echo "Skipping frontend build steps - YOU NEED TO RUN IT MANUALLY ON THE HOST!" echo "Skipping frontend build steps - YOU NEED TO RUN IT MANUALLY ON THE HOST!"

View File

@ -30,24 +30,18 @@ fi
echo_step() { echo_step() {
cat <<EOF cat <<EOF
###################################################################### ######################################################################
Init Step ${1}/${STEP_CNT} [${2}] -- ${3} Init Step ${1}/${STEP_CNT} [${2}] -- ${3}
###################################################################### ######################################################################
EOF EOF
} }
ADMIN_PASSWORD="${ADMIN_PASSWORD:-admin}" ADMIN_PASSWORD="${ADMIN_PASSWORD:-admin}"
# If Cypress run overwrite the password for admin and export env variables # If Cypress run overwrite the password for admin and export env variables
if [ "$CYPRESS_CONFIG" == "true" ]; then if [ "$CYPRESS_CONFIG" == "true" ]; then
ADMIN_PASSWORD="general" ADMIN_PASSWORD="general"
export SUPERSET_CONFIG=tests.integration_tests.superset_test_config
export SUPERSET_TESTENV=true export SUPERSET_TESTENV=true
export SUPERSET__SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://superset:superset@db:5432/superset export POSTGRES_DB=superset_cypress
export SUPERSET__SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://superset:superset@db:5432/superset_cypress
fi fi
# Initialize the database # Initialize the database
echo_step "1" "Starting" "Applying DB migrations" echo_step "1" "Starting" "Applying DB migrations"
@ -56,12 +50,16 @@ echo_step "1" "Complete" "Applying DB migrations"
# Create an admin user # Create an admin user
echo_step "2" "Starting" "Setting up admin user ( admin / $ADMIN_PASSWORD )" echo_step "2" "Starting" "Setting up admin user ( admin / $ADMIN_PASSWORD )"
superset fab create-admin \ if [ "$CYPRESS_CONFIG" == "true" ]; then
--username admin \ superset load_test_users
--firstname Superset \ else
--lastname Admin \ superset fab create-admin \
--email admin@superset.com \ --username admin \
--password "$ADMIN_PASSWORD" --email admin@superset.com \
--password "$ADMIN_PASSWORD" \
--firstname Superset \
--lastname Admin
fi
echo_step "2" "Complete" "Setting up admin user" echo_step "2" "Complete" "Setting up admin user"
# Create default roles and permissions # Create default roles and permissions
echo_step "3" "Starting" "Setting up roles and perms" echo_step "3" "Starting" "Setting up roles and perms"
@ -73,10 +71,9 @@ if [ "$SUPERSET_LOAD_EXAMPLES" = "yes" ]; then
echo_step "4" "Starting" "Loading examples" echo_step "4" "Starting" "Loading examples"
# If Cypress run which consumes superset_test_config load required data for tests # If Cypress run which consumes superset_test_config load required data for tests
if [ "$CYPRESS_CONFIG" == "true" ]; then if [ "$CYPRESS_CONFIG" == "true" ]; then
superset load_test_users
superset load_examples --load-test-data superset load_examples --load-test-data
else else
superset load_examples --force superset load_examples
fi fi
echo_step "4" "Complete" "Loading examples" echo_step "4" "Complete" "Loading examples"
fi fi

View File

@ -23,4 +23,4 @@
export SERVER_THREADS_AMOUNT=8 export SERVER_THREADS_AMOUNT=8
# start up the web server # start up the web server
/usr/bin/run-server.sh /app/docker/entrypoints/run-server.sh

View File

@ -22,6 +22,7 @@
# #
import logging import logging
import os import os
import sys
from celery.schedules import crontab from celery.schedules import crontab
from flask_caching.backends.filesystemcache import FileSystemCache from flask_caching.backends.filesystemcache import FileSystemCache
@ -104,6 +105,21 @@ WEBDRIVER_BASEURL = "http://superset:8088/" # When using docker compose baseurl
WEBDRIVER_BASEURL_USER_FRIENDLY = WEBDRIVER_BASEURL WEBDRIVER_BASEURL_USER_FRIENDLY = WEBDRIVER_BASEURL
SQLLAB_CTAS_NO_LIMIT = True SQLLAB_CTAS_NO_LIMIT = True
log_level_text = os.getenv("SUPERSET_LOG_LEVEL", "INFO")
LOG_LEVEL = getattr(logging, log_level_text.upper(), logging.INFO)
if os.getenv("CYPRESS_CONFIG") == "true":
# When running the service as a cypress backend, we need to import the config
# located @ tests/integration_tests/superset_test_config.py
base_dir = os.path.dirname(__file__)
module_folder = os.path.abspath(
os.path.join(base_dir, "../../tests/integration_tests/")
)
sys.path.insert(0, module_folder)
from superset_test_config import * # noqa
sys.path.pop(0)
# #
# Optionally import superset_config_docker.py (which will have been included on # Optionally import superset_config_docker.py (which will have been included on
# the PYTHONPATH) in order to allow for local settings to be overridden # the PYTHONPATH) in order to allow for local settings to be overridden

View File

@ -25,6 +25,9 @@ Alerts and reports are disabled by default. To turn them on, you need to do some
- At least one of those must be configured, depending on what you want to use: - At least one of those must be configured, depending on what you want to use:
- emails: `SMTP_*` settings - emails: `SMTP_*` settings
- Slack messages: `SLACK_API_TOKEN` - Slack messages: `SLACK_API_TOKEN`
- Users can customize the email subject by including date code placeholders, which will automatically be replaced with the corresponding UTC date when the email is sent. To enable this functionality, activate the `"DATE_FORMAT_IN_EMAIL_SUBJECT"` [feature flag](/docs/configuration/configuring-superset#feature-flags). This enables date formatting in email subjects, preventing all reporting emails from being grouped into the same thread (optional for the reporting feature).
- Use date codes from [strftime.org](https://strftime.org/) to create the email subject.
- If no date code is provided, the original string will be used as the email subject.
##### Disable dry-run mode ##### Disable dry-run mode
@ -177,10 +180,9 @@ By default, Alerts and Reports are executed as the owner of the alert/report obj
just change the config as follows (`admin` in this example): just change the config as follows (`admin` in this example):
```python ```python
from superset.tasks.types import ExecutorType from superset.tasks.types import FixedExecutor
THUMBNAIL_SELENIUM_USER = 'admin' ALERT_REPORTS_EXECUTORS = [FixedExecutor("admin")]
ALERT_REPORTS_EXECUTE_AS = [ExecutorType.SELENIUM]
``` ```
Please refer to `ExecutorType` in the codebase for other executor types. Please refer to `ExecutorType` in the codebase for other executor types.

View File

@ -94,10 +94,9 @@ By default thumbnails are rendered per user, and will fall back to the Selenium
To always render thumbnails as a fixed user (`admin` in this example), use the following configuration: To always render thumbnails as a fixed user (`admin` in this example), use the following configuration:
```python ```python
from superset.tasks.types import ExecutorType from superset.tasks.types import FixedExecutor
THUMBNAIL_SELENIUM_USER = "admin" THUMBNAIL_EXECUTORS = [FixedExecutor("admin")]
THUMBNAIL_EXECUTE_AS = [ExecutorType.SELENIUM]
``` ```
@ -130,8 +129,6 @@ def init_thumbnail_cache(app: Flask) -> S3Cache:
THUMBNAIL_CACHE_CONFIG = init_thumbnail_cache THUMBNAIL_CACHE_CONFIG = init_thumbnail_cache
# Async selenium thumbnail task will use the following user
THUMBNAIL_SELENIUM_USER = "Admin"
``` ```
Using the above example cache keys for dashboards will be `superset_thumb__dashboard__{ID}`. You can Using the above example cache keys for dashboards will be `superset_thumb__dashboard__{ID}`. You can

View File

@ -69,6 +69,7 @@ are compatible with Superset.
| [MySQL](/docs/configuration/databases#mysql) | `pip install mysqlclient` | `mysql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` | | [MySQL](/docs/configuration/databases#mysql) | `pip install mysqlclient` | `mysql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
| [OceanBase](/docs/configuration/databases#oceanbase) | `pip install oceanbase_py` | `oceanbase://<UserName>:<DBPassword>@<Database Host>/<Database Name>` | | [OceanBase](/docs/configuration/databases#oceanbase) | `pip install oceanbase_py` | `oceanbase://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
| [Oracle](/docs/configuration/databases#oracle) | `pip install cx_Oracle` | `oracle://` | | [Oracle](/docs/configuration/databases#oracle) | `pip install cx_Oracle` | `oracle://` |
| [Parseable](/docs/configuration/databases#parseable) | `pip install sqlalchemy-parseable` | `parseable://<UserName>:<DBPassword>@<Database Host>/<Stream Name>` |
| [PostgreSQL](/docs/configuration/databases#postgres) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` | | [PostgreSQL](/docs/configuration/databases#postgres) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
| [Presto](/docs/configuration/databases#presto) | `pip install pyhive` | `presto://` | | [Presto](/docs/configuration/databases#presto) | `pip install pyhive` | `presto://` |
| [Rockset](/docs/configuration/databases#rockset) | `pip install rockset-sqlalchemy` | `rockset://<api_key>:@<api_server>` | | [Rockset](/docs/configuration/databases#rockset) | `pip install rockset-sqlalchemy` | `rockset://<api_key>:@<api_server>` |
@ -77,6 +78,7 @@ are compatible with Superset.
| [Snowflake](/docs/configuration/databases#snowflake) | `pip install snowflake-sqlalchemy` | `snowflake://{user}:{password}@{account}.{region}/{database}?role={role}&warehouse={warehouse}` | | [Snowflake](/docs/configuration/databases#snowflake) | `pip install snowflake-sqlalchemy` | `snowflake://{user}:{password}@{account}.{region}/{database}?role={role}&warehouse={warehouse}` |
| SQLite | No additional library needed | `sqlite://path/to/file.db?check_same_thread=false` | | SQLite | No additional library needed | `sqlite://path/to/file.db?check_same_thread=false` |
| [SQL Server](/docs/configuration/databases#sql-server) | `pip install pymssql` | `mssql+pymssql://` | | [SQL Server](/docs/configuration/databases#sql-server) | `pip install pymssql` | `mssql+pymssql://` |
| [TDengine](/docs/configuration/databases#tdengine) | `pip install taospy` `pip install taos-ws-py` | `taosws://<user>:<password>@<host>:<port>` |
| [Teradata](/docs/configuration/databases#teradata) | `pip install teradatasqlalchemy` | `teradatasql://{user}:{password}@{host}` | | [Teradata](/docs/configuration/databases#teradata) | `pip install teradatasqlalchemy` | `teradatasql://{user}:{password}@{host}` |
| [TimescaleDB](/docs/configuration/databases#timescaledb) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>:<Port>/<Database Name>` | | [TimescaleDB](/docs/configuration/databases#timescaledb) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>:<Port>/<Database Name>` |
| [Trino](/docs/configuration/databases#trino) | `pip install trino` | `trino://{username}:{password}@{hostname}:{port}/{catalog}` | | [Trino](/docs/configuration/databases#trino) | `pip install trino` | `trino://{username}:{password}@{hostname}:{port}/{catalog}` |
@ -1074,6 +1076,23 @@ The connection string is formatted as follows:
oracle://<username>:<password>@<hostname>:<port> oracle://<username>:<password>@<hostname>:<port>
``` ```
#### Parseable
[Parseable](https://www.parseable.io) is a distributed log analytics database that provides SQL-like query interface for log data. The recommended connector library is [sqlalchemy-parseable](https://github.com/parseablehq/sqlalchemy-parseable).
The connection string is formatted as follows:
```
parseable://<username>:<password>@<hostname>:<port>/<stream_name>
```
For example:
```
parseable://admin:admin@demo.parseable.com:443/ingress-nginx
```
Note: The stream_name in the URI represents the Parseable logstream you want to query. You can use both HTTP (port 80) and HTTPS (port 443) connections.
#### Apache Pinot #### Apache Pinot
@ -1336,6 +1355,24 @@ starrocks://<User>:<Password>@<Host>:<Port>/<Catalog>.<Database>
StarRocks maintains their Superset docuementation [here](https://docs.starrocks.io/docs/integrations/BI_integrations/Superset/). StarRocks maintains their Superset docuementation [here](https://docs.starrocks.io/docs/integrations/BI_integrations/Superset/).
::: :::
#### TDengine
[TDengine](https://www.tdengine.com) is a High-Performance, Scalable Time-Series Database for Industrial IoT and provides SQL-like query interface.
The recommended connector library for TDengine is [taospy](https://pypi.org/project/taospy/) and [taos-ws-py](https://pypi.org/project/taos-ws-py/)
The expected connection string is formatted as follows:
```
taosws://<user>:<password>@<host>:<port>
```
For example:
```
taosws://root:taosdata@127.0.0.1:6041
```
#### Teradata #### Teradata
The recommended connector library is The recommended connector library is

View File

@ -18,7 +18,7 @@ The following keys in `superset_config.py` can be specified to configure CORS:
- `ENABLE_CORS`: Must be set to `True` in order to enable CORS - `ENABLE_CORS`: Must be set to `True` in order to enable CORS
- `CORS_OPTIONS`: options passed to Flask-CORS - `CORS_OPTIONS`: options passed to Flask-CORS
([documentation](https://flask-cors.corydolphin.com/en/latest/api.html#extension)) ([documentation](https://flask-cors.readthedocs.io/en/latest/api.html#extension))
## HTTP headers ## HTTP headers
@ -108,6 +108,10 @@ running a custom auth postback endpoint), you can add the endpoints to `WTF_CSRF
## Domain Sharding ## Domain Sharding
:::note
Domain Sharding is deprecated as of Superset 5.0.0, and will be removed in Superset 6.0.0. Please Enable HTTP2 to keep more open connections per domain.
:::
Chrome allows up to 6 open connections per domain at a time. When there are more than 6 slices in Chrome allows up to 6 open connections per domain at a time. When there are more than 6 slices in
dashboard, a lot of time fetch requests are queued up and wait for next available socket. dashboard, a lot of time fetch requests are queued up and wait for next available socket.
[PR 5039](https://github.com/apache/superset/pull/5039) adds domain sharding to Superset, [PR 5039](https://github.com/apache/superset/pull/5039) adds domain sharding to Superset,

View File

@ -72,6 +72,7 @@ documentation.
configured to be secure. configured to be secure.
::: :::
### Supported environment variables ### Supported environment variables
Affecting the Docker build process: Affecting the Docker build process:
@ -79,11 +80,26 @@ Affecting the Docker build process:
- **INCLUDE_FIREFOX (default=false):** whether to include the Firefox headless browser in the build - **INCLUDE_FIREFOX (default=false):** whether to include the Firefox headless browser in the build
- **INCLUDE_CHROMIUM (default=false):** whether to include the Firefox headless browser in the build - **INCLUDE_CHROMIUM (default=false):** whether to include the Firefox headless browser in the build
- **BUILD_TRANSLATIONS(default=false):** whether to compile the translations from the .po files available - **BUILD_TRANSLATIONS(default=false):** whether to compile the translations from the .po files available
- **SUPERSET_LOAD_EXAMPLES (default=yes):** whether to load the examples into the database upon startup,
save some precious time on startup by `SUPERSET_LOAD_EXAMPLES=no docker compose up`
- **SUPERSET_LOG_LEVEL (default=info)**: Can be set to debug, info, warning, error, critical
for more verbose logging
For more env vars that affect your configuration, see this For more env vars that affect your configuration, see this
[superset_config.py](https://github.com/apache/superset/blob/master/docker/pythonpath_dev/superset_config.py) [superset_config.py](https://github.com/apache/superset/blob/master/docker/pythonpath_dev/superset_config.py)
used in the `docker compose` context to assign env vars to the superset configuration. used in the `docker compose` context to assign env vars to the superset configuration.
### Accessing the postgres database
Sometimes it's useful to access the database in the docker container directly.
You can enter a `psql` shell (the official Postgres client) by running the following command:
```bash
docker compose exec db psql -U superset
```
Also note that the database is exposed on port 5432, so you can connect to it using your favorite
Postgres client or even SQL Lab itselft directly in Superset by creating a new database connection
to `localhost:5432`.
### Nuking the postgres database ### Nuking the postgres database
@ -117,7 +133,8 @@ instance, but many people like to run that tooling from their host.
Assuming you already have a way to setup your python environments Assuming you already have a way to setup your python environments
like `pyenv`, `virtualenv` or something else, all you should have to like `pyenv`, `virtualenv` or something else, all you should have to
do is to install our dev, pinned python requirements bundle do is to install our dev, pinned python requirements bundle, after installing
the prerequisites mentioned in [OS Dependencies](https://superset.apache.org/docs/installation/pypi/#os-dependencies)
```bash ```bash
pip install -r requirements/development.txt pip install -r requirements/development.txt
@ -626,71 +643,6 @@ To run a single test file:
npm run test -- path/to/file.js npm run test -- path/to/file.js
``` ```
### Integration Testing
We use [Cypress](https://www.cypress.io/) for integration tests. To open Cypress and explore tests first setup and run test server:
```bash
export SUPERSET_CONFIG=tests.integration_tests.superset_test_config
export SUPERSET_TESTENV=true
export CYPRESS_BASE_URL="http://localhost:8081"
superset db upgrade
superset load_test_users
superset load-examples --load-test-data
superset init
superset run --port 8081
```
Run Cypress tests:
```bash
cd superset-frontend
npm run build-instrumented
cd cypress-base
npm install
# run tests via headless Chrome browser (requires Chrome 64+)
npm run cypress-run-chrome
# run tests from a specific file
npm run cypress-run-chrome -- --spec cypress/e2e/explore/link.test.ts
# run specific file with video capture
npm run cypress-run-chrome -- --spec cypress/e2e/dashboard/index.test.js --config video=true
# to open the cypress ui
npm run cypress-debug
# to point cypress to a url other than the default (http://localhost:8088) set the environment variable before running the script
# e.g., CYPRESS_BASE_URL="http://localhost:9000"
CYPRESS_BASE_URL=<your url> npm run cypress open
```
See [`superset-frontend/cypress_build.sh`](https://github.com/apache/superset/blob/master/superset-frontend/cypress_build.sh).
As an alternative you can use docker compose environment for testing:
Make sure you have added below line to your /etc/hosts file:
`127.0.0.1 db`
If you already have launched Docker environment please use the following command to ensure a fresh database instance:
`docker compose down -v`
Launch environment:
`CYPRESS_CONFIG=true docker compose up --build`
It will serve the backend and frontend on port 8088.
Run Cypress tests:
```bash
cd cypress-base
npm install
npm run cypress open
```
### Debugging Server App ### Debugging Server App
#### Local #### Local

View File

@ -225,34 +225,37 @@ npm run test -- path/to/file.js
### e2e Integration Testing ### e2e Integration Testing
For e2e testing, we recommend that you use a `docker-compose` backed-setup For e2e testing, we recommend that you use a `docker compose` backend
Alternatively, you can go lower level and set things up in your
development environment by following these steps:
First set up a python/flask backend:
```bash ```bash
export SUPERSET_CONFIG=tests.integration_tests.superset_test_config CYPRESS_CONFIG=true docker compose up --build
export SUPERSET_TESTENV=true ```
export CYPRESS_BASE_URL="http://localhost:8081" `docker compose` will get to work and expose a Cypress-ready Superset app.
superset db upgrade This app uses a different database schema (`superset_cypress`) to keep it isolated from
superset load_test_users your other dev environmen(s)t, a specific set of examples, and a set of configurations that
superset init aligns with the expectations within the end-to-end tests. Also note that it's served on a
superset load-examples --load-test-data different port than the default port for the backend (`8088`).
superset run --port 8081
Now in another terminal, let's get ready to execute some Cypress commands. First, tell cypress
to connect to the Cypress-ready Superset backend.
```
CYPRESS_BASE_URL=http://localhost:8081
``` ```
In another terminal, prepare the frontend and run Cypress tests:
```bash ```bash
cd superset-frontend # superset-frontend/cypress-base is the base folder for everything Cypress-related
npm run build-instrumented # It's essentially its own npm app, with its own dependencies, configurations and utilities
cd superset-frontend/cypress-base
cd cypress-base
npm install npm install
# run tests via headless Chrome browser (requires Chrome 64+) # use interactive mode to run tests, while keeping memory usage contained
# this will fire up an interactive Cypress UI
# as you alter the code, the tests will re-run automatically, and you can visualize each
# and every step for debugging purposes
npx cypress open --config numTestsKeptInMemory=5
# to run the test suite on the command line using chrome (same as CI)
npm run cypress-run-chrome npm run cypress-run-chrome
# run tests from a specific file # run tests from a specific file
@ -264,9 +267,6 @@ npm run cypress-run-chrome -- --spec cypress/e2e/dashboard/index.test.js --confi
# to open the cypress ui # to open the cypress ui
npm run cypress-debug npm run cypress-debug
# to point cypress to a url other than the default (http://localhost:8088) set the environment variable before running the script
# e.g., CYPRESS_BASE_URL="http://localhost:9000"
CYPRESS_BASE_URL=<your url> npm run cypress open
``` ```
See [`superset-frontend/cypress_build.sh`](https://github.com/apache/superset/blob/master/superset-frontend/cypress_build.sh). See [`superset-frontend/cypress_build.sh`](https://github.com/apache/superset/blob/master/superset-frontend/cypress_build.sh).

View File

@ -45,7 +45,7 @@ This is the core application. Superset operates like this:
This is where chart and dashboard definitions, user information, logs, etc. are stored. Superset is tested to work with PostgreSQL and MySQL databases as the metadata database (not be confused with a data source like your data warehouse, which could be a much greater variety of options like Snowflake, Redshift, etc.). This is where chart and dashboard definitions, user information, logs, etc. are stored. Superset is tested to work with PostgreSQL and MySQL databases as the metadata database (not be confused with a data source like your data warehouse, which could be a much greater variety of options like Snowflake, Redshift, etc.).
Some installation methods like our Quickstart and PyPI come configured by default to use a SQLite on-disk database. And in a Docker Compose installation, the data would be stored in a PostgresQL container volume. Neither of these cases are recommended for production instances of Superset. Some installation methods like our Quickstart and PyPI come configured by default to use a SQLite on-disk database. And in a Docker Compose installation, the data would be stored in a PostgreSQL container volume. Neither of these cases are recommended for production instances of Superset.
For production, a properly-configured, managed, standalone database is recommended. No matter what database you use, you should plan to back it up regularly. For production, a properly-configured, managed, standalone database is recommended. No matter what database you use, you should plan to back it up regularly.

View File

@ -17,7 +17,7 @@ Since `docker compose` is primarily designed to run a set of containers on **a s
and can't support requirements for **high availability**, we do not support nor recommend and can't support requirements for **high availability**, we do not support nor recommend
using our `docker compose` constructs to support production-type use-cases. For single host using our `docker compose` constructs to support production-type use-cases. For single host
environments, we recommend using [minikube](https://minikube.sigs.k8s.io/docs/start/) along environments, we recommend using [minikube](https://minikube.sigs.k8s.io/docs/start/) along
our [installing on k8s](https://superset.apache.org/docs/installation/running-on-kubernetes) with our [installing on k8s](https://superset.apache.org/docs/installation/running-on-kubernetes)
documentation. documentation.
::: :::

View File

@ -150,6 +150,9 @@ Superset requires a Python DB-API database driver and a SQLAlchemy
dialect to be installed for each datastore you want to connect to. dialect to be installed for each datastore you want to connect to.
See [Install Database Drivers](/docs/configuration/databases) for more information. See [Install Database Drivers](/docs/configuration/databases) for more information.
It is recommended that you refer to versions listed in
[pyproject.toml](https://github.com/apache/superset/blob/master/pyproject.toml)
instead of hard-coding them in your bootstrap script, as seen below.
::: :::
@ -157,9 +160,9 @@ The following example installs the drivers for BigQuery and Elasticsearch, allow
```yaml ```yaml
bootstrapScript: | bootstrapScript: |
#!/bin/bash #!/bin/bash
pip install psycopg2==2.9.6 \ pip install .[postgres] \
sqlalchemy-bigquery==1.6.1 \ .[bigquery] \
elasticsearch-dbapi==0.2.5 &&\ .[elasticsearch] &&\
if [ ! -f ~/bootstrap ]; then echo "Running Superset with uid {{ .Values.runAsUser }}" > ~/bootstrap; fi if [ ! -f ~/bootstrap ]; then echo "Running Superset with uid {{ .Values.runAsUser }}" > ~/bootstrap; fi
``` ```

View File

@ -253,6 +253,9 @@ You can get current nonce value by calling jinja macro `csp_nonce()`.
connect-src 'self' https://api.mapbox.com https://events.mapbox.com connect-src 'self' https://api.mapbox.com https://events.mapbox.com
``` ```
- Cartodiagram charts request map data (image and json) from external resources that can be edited by users,
and therefore either require a list of allowed domains to request from or a wildcard (`'*'`) for `img-src` and `connect-src`.
* Other CSP directives default to `'self'` to limit content to the same origin as the Superset server. * Other CSP directives default to `'self'` to limit content to the same origin as the Superset server.
In order to adjust provided CSP configuration to your needs, follow the instructions and examples provided in In order to adjust provided CSP configuration to your needs, follow the instructions and examples provided in

View File

@ -29,12 +29,12 @@
"@saucelabs/theme-github-codeblock": "^0.3.0", "@saucelabs/theme-github-codeblock": "^0.3.0",
"@superset-ui/style": "^0.14.23", "@superset-ui/style": "^0.14.23",
"@svgr/webpack": "^8.1.0", "@svgr/webpack": "^8.1.0",
"antd": "^5.22.5", "antd": "^5.22.7",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"docusaurus-plugin-less": "^2.0.2", "docusaurus-plugin-less": "^2.0.2",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"less": "^4.2.1", "less": "^4.2.2",
"less-loader": "^11.0.0", "less-loader": "^11.0.0",
"prism-react-renderer": "^2.4.1", "prism-react-renderer": "^2.4.1",
"react": "^18.3.1", "react": "^18.3.1",

View File

@ -137,4 +137,9 @@ export const Databases = [
href: 'https://www.denodo.com/', href: 'https://www.denodo.com/',
imgName: 'denodo.png', imgName: 'denodo.png',
}, },
{
title: 'TDengine',
href: 'https://www.tdengine.com/',
imgName: 'tdengine.png',
},
]; ];

BIN
docs/static/img/databases/tdengine.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -3455,10 +3455,10 @@ ansi-styles@^6.1.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
antd@^5.22.5: antd@^5.22.7:
version "5.22.5" version "5.22.7"
resolved "https://registry.yarnpkg.com/antd/-/antd-5.22.5.tgz#e959381faca86c984cc853a0ab5cb3f140178336" resolved "https://registry.yarnpkg.com/antd/-/antd-5.22.7.tgz#90a0eb3e4cd65b7fc042318132d34b7c492783dd"
integrity sha512-+0UP8w+ULVv2OIzCDVz7j6I0UfH6mMLHSWO6qzpBc+9psOoVQLRbyAE21XnZM/eGrt2MNsEDL5fmlhXL/V8JyQ== integrity sha512-koT5QMliDgXc21yNcs4Uyuq6TeB5AJbzGZ2qjNExzE7Tjr8yYIX6sJsQfunsEV80wC1mpF7m9ldKuNj+PafcFA==
dependencies: dependencies:
"@ant-design/colors" "^7.1.0" "@ant-design/colors" "^7.1.0"
"@ant-design/cssinjs" "^1.21.1" "@ant-design/cssinjs" "^1.21.1"
@ -3487,13 +3487,13 @@ antd@^5.22.5:
rc-input-number "~9.3.0" rc-input-number "~9.3.0"
rc-mentions "~2.17.0" rc-mentions "~2.17.0"
rc-menu "~9.16.0" rc-menu "~9.16.0"
rc-motion "^2.9.4" rc-motion "^2.9.5"
rc-notification "~5.6.2" rc-notification "~5.6.2"
rc-pagination "~5.0.0" rc-pagination "~5.0.0"
rc-picker "~4.8.3" rc-picker "~4.8.3"
rc-progress "~4.0.0" rc-progress "~4.0.0"
rc-rate "~2.13.0" rc-rate "~2.13.0"
rc-resize-observer "^1.4.1" rc-resize-observer "^1.4.3"
rc-segmented "~2.5.0" rc-segmented "~2.5.0"
rc-select "~14.16.4" rc-select "~14.16.4"
rc-slider "~11.1.7" rc-slider "~11.1.7"
@ -3506,7 +3506,7 @@ antd@^5.22.5:
rc-tree "~5.10.1" rc-tree "~5.10.1"
rc-tree-select "~5.24.5" rc-tree-select "~5.24.5"
rc-upload "~4.8.1" rc-upload "~4.8.1"
rc-util "^5.44.2" rc-util "^5.44.3"
scroll-into-view-if-needed "^3.1.0" scroll-into-view-if-needed "^3.1.0"
throttle-debounce "^5.0.2" throttle-debounce "^5.0.2"
@ -6359,10 +6359,10 @@ less-loader@^11.0.0:
resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-11.1.4.tgz#e8a070844efaefbe59b978acaf57b9d3e868cf08" resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-11.1.4.tgz#e8a070844efaefbe59b978acaf57b9d3e868cf08"
integrity sha512-6/GrYaB6QcW6Vj+/9ZPgKKs6G10YZai/l/eJ4SLwbzqNTBsAqt5hSLVF47TgsiBxV1P6eAU0GYRH3YRuQU9V3A== integrity sha512-6/GrYaB6QcW6Vj+/9ZPgKKs6G10YZai/l/eJ4SLwbzqNTBsAqt5hSLVF47TgsiBxV1P6eAU0GYRH3YRuQU9V3A==
less@^4.2.1: less@^4.2.2:
version "4.2.1" version "4.2.2"
resolved "https://registry.yarnpkg.com/less/-/less-4.2.1.tgz#fe4c9848525ab44614c0cf2c00abd8d031bb619a" resolved "https://registry.yarnpkg.com/less/-/less-4.2.2.tgz#4b59ede113933b58ab152190edf9180fc36846d8"
integrity sha512-CasaJidTIhWmjcqv0Uj5vccMI7pJgfD9lMkKtlnTHAdJdYK/7l8pM9tumLyJ0zhbD4KJLo/YvTj+xznQd5NBhg== integrity sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==
dependencies: dependencies:
copy-anything "^2.0.1" copy-anything "^2.0.1"
parse-node-version "^1.0.1" parse-node-version "^1.0.1"
@ -8391,7 +8391,7 @@ rc-menu@~9.16.0:
rc-overflow "^1.3.1" rc-overflow "^1.3.1"
rc-util "^5.27.0" rc-util "^5.27.0"
rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.4: rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.5:
version "2.9.5" version "2.9.5"
resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c" resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c"
integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA== integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==
@ -8459,7 +8459,7 @@ rc-rate@~2.13.0:
classnames "^2.2.5" classnames "^2.2.5"
rc-util "^5.0.1" rc-util "^5.0.1"
rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0, rc-resize-observer@^1.4.1: rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0, rc-resize-observer@^1.4.3:
version "1.4.3" version "1.4.3"
resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz#4fd41fa561ba51362b5155a07c35d7c89a1ea569" resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz#4fd41fa561ba51362b5155a07c35d7c89a1ea569"
integrity sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ== integrity sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==
@ -8595,10 +8595,10 @@ rc-upload@~4.8.1:
classnames "^2.2.5" classnames "^2.2.5"
rc-util "^5.2.0" rc-util "^5.2.0"
rc-util@^5.0.1, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.41.0, rc-util@^5.43.0, rc-util@^5.44.0, rc-util@^5.44.1, rc-util@^5.44.2: rc-util@^5.0.1, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.41.0, rc-util@^5.43.0, rc-util@^5.44.0, rc-util@^5.44.1, rc-util@^5.44.3:
version "5.44.2" version "5.44.3"
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.2.tgz#6bc5db0e96ebdb515eb5977a7371887e5413a6f8" resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.3.tgz#9eca5039906446113c4032859f88c15234547961"
integrity sha512-uGSk3hpPBLa3/0QAcKhCjgl4SFnhQCJDLvvpoLdbR6KgDuXrujG+dQaUeUvBJr2ZWak1O/9n+cYbJiWmmk95EQ== integrity sha512-q6KCcOFk3rv/zD3MckhJteZxb0VjAIFuf622B7ElK4vfrZdAzs16XR5p3VTdy3+U5jfJU5ACz4QnhLSuAGe5dA==
dependencies: dependencies:
"@babel/runtime" "^7.18.3" "@babel/runtime" "^7.18.3"
react-is "^18.2.0" react-is "^18.2.0"

View File

@ -1,9 +1,9 @@
dependencies: dependencies:
- name: postgresql - name: postgresql
repository: https://charts.bitnami.com/bitnami repository: oci://registry-1.docker.io/bitnamicharts
version: 12.1.6 version: 12.1.6
- name: redis - name: redis
repository: https://charts.bitnami.com/bitnami repository: oci://registry-1.docker.io/bitnamicharts
version: 17.9.4 version: 17.9.4
digest: sha256:98b6c107066652e40c242a6b0e3c573a4eedf9f64bae0d60f884f0f5cfa1c01a digest: sha256:9588e2a9f15d875a95763ed7da8e92b5b48a8d13cbacd66b775eacba3e8cebcd
generated: "2023-04-17T10:38:41.966779+03:00" generated: "2024-12-29T12:19:15.365763+09:00"

View File

@ -29,13 +29,13 @@ maintainers:
- name: craig-rueda - name: craig-rueda
email: craig@craigrueda.com email: craig@craigrueda.com
url: https://github.com/craig-rueda url: https://github.com/craig-rueda
version: 0.13.5 version: 0.14.0
dependencies: dependencies:
- name: postgresql - name: postgresql
version: 12.1.6 version: 12.1.6
repository: https://charts.bitnami.com/bitnami repository: oci://registry-1.docker.io/bitnamicharts
condition: postgresql.enabled condition: postgresql.enabled
- name: redis - name: redis
version: 17.9.4 version: 17.9.4
repository: https://charts.bitnami.com/bitnami repository: oci://registry-1.docker.io/bitnamicharts
condition: redis.enabled condition: redis.enabled

View File

@ -23,7 +23,7 @@ NOTE: This file is generated by helm-docs: https://github.com/norwoodj/helm-docs
# superset # superset
![Version: 0.13.5](https://img.shields.io/badge/Version-0.13.5-informational?style=flat-square) ![Version: 0.14.0](https://img.shields.io/badge/Version-0.14.0-informational?style=flat-square)
Apache Superset is a modern, enterprise-ready business intelligence web application Apache Superset is a modern, enterprise-ready business intelligence web application
@ -50,8 +50,8 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
| Repository | Name | Version | | Repository | Name | Version |
|------------|------|---------| |------------|------|---------|
| https://charts.bitnami.com/bitnami | postgresql | 12.1.6 | | oci://registry-1.docker.io/bitnamicharts | postgresql | 12.1.6 |
| https://charts.bitnami.com/bitnami | redis | 17.9.4 | | oci://registry-1.docker.io/bitnamicharts | redis | 17.9.4 |
## Values ## Values
@ -66,12 +66,12 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
| envFromSecret | string | `"{{ template \"superset.fullname\" . }}-env"` | The name of the secret which we will use to populate env vars in deployed pods This can be useful for secret keys, etc. | | envFromSecret | string | `"{{ template \"superset.fullname\" . }}-env"` | The name of the secret which we will use to populate env vars in deployed pods This can be useful for secret keys, etc. |
| envFromSecrets | list | `[]` | This can be a list of templated strings | | envFromSecrets | list | `[]` | This can be a list of templated strings |
| extraConfigMountPath | string | `"/app/configs"` | | | extraConfigMountPath | string | `"/app/configs"` | |
| extraConfigs | object | `{}` | Extra files to mount on `/app/pythonpath` | | extraConfigs | object | `{}` | Extra files to be mounted as ConfigMap on the path specified in `extraConfigMountPath` |
| extraEnv | object | `{}` | Extra environment variables that will be passed into pods | | extraEnv | object | `{}` | Extra environment variables that will be passed into pods |
| extraEnvRaw | list | `[]` | Extra environment variables in RAW format that will be passed into pods | | extraEnvRaw | list | `[]` | Extra environment variables in RAW format that will be passed into pods |
| extraLabels | object | `{}` | Labels to be added to all resources | | extraLabels | object | `{}` | Labels to be added to all resources |
| extraSecretEnv | object | `{}` | Extra environment variables to pass as secrets | | extraSecretEnv | object | `{}` | Extra environment variables to pass as secrets |
| extraSecrets | object | `{}` | Extra files to mount on `/app/pythonpath` as secrets | | extraSecrets | object | `{}` | Extra files to be mounted as Secrets on the path specified in `configMountPath` |
| extraVolumeMounts | list | `[]` | | | extraVolumeMounts | list | `[]` | |
| extraVolumes | list | `[]` | | | extraVolumes | list | `[]` | |
| fullnameOverride | string | `nil` | Provide a name to override the full names of resources | | fullnameOverride | string | `nil` | Provide a name to override the full names of resources |

View File

@ -106,7 +106,7 @@ extraSecretEnv: {}
# # Generate your own secret key for encryption. Use openssl rand -base64 42 to generate a good key # # Generate your own secret key for encryption. Use openssl rand -base64 42 to generate a good key
# SUPERSET_SECRET_KEY: 'CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET' # SUPERSET_SECRET_KEY: 'CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET'
# -- Extra files to mount on `/app/pythonpath` # -- Extra files to be mounted as ConfigMap on the path specified in `extraConfigMountPath`
extraConfigs: {} extraConfigs: {}
# import_datasources.yaml: | # import_datasources.yaml: |
# databases: # databases:
@ -120,7 +120,7 @@ extraConfigs: {}
# sqlalchemy_uri: example://example-db.local # sqlalchemy_uri: example://example-db.local
# tables: [] # tables: []
# -- Extra files to mount on `/app/pythonpath` as secrets # -- Extra files to be mounted as Secrets on the path specified in `configMountPath`
extraSecrets: {} extraSecrets: {}
extraVolumes: [] extraVolumes: []
@ -277,7 +277,7 @@ supersetNode:
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; /usr/bin/run-server.sh" - ". {{ .Values.configMountPath }}/superset_bootstrap.sh; /usr/bin/run-server.sh"
connections: connections:
# -- Change in case of bringing your own redis and then also set redis.enabled:false # -- Change in case of bringing your own redis and then also set redis.enabled:false
redis_host: '{{ .Release.Name }}-redis-headless' redis_host: "{{ .Release.Name }}-redis-headless"
redis_port: "6379" redis_port: "6379"
redis_user: "" redis_user: ""
# redis_password: superset # redis_password: superset
@ -289,7 +289,7 @@ supersetNode:
enabled: false enabled: false
ssl_cert_reqs: CERT_NONE ssl_cert_reqs: CERT_NONE
# You need to change below configuration incase bringing own PostgresSQL instance and also set postgresql.enabled:false # You need to change below configuration incase bringing own PostgresSQL instance and also set postgresql.enabled:false
db_host: '{{ .Release.Name }}-postgresql' db_host: "{{ .Release.Name }}-postgresql"
db_port: "5432" db_port: "5432"
db_user: superset db_user: superset
db_pass: superset db_pass: superset

View File

@ -24,13 +24,12 @@ name = "apache-superset"
description = "A modern, enterprise-ready business intelligence web application" description = "A modern, enterprise-ready business intelligence web application"
readme = "README.md" readme = "README.md"
dynamic = ["version", "scripts", "entry-points"] dynamic = ["version", "scripts", "entry-points"]
requires-python = ">=3.9" requires-python = ">=3.10"
license = { file="LICENSE.txt" } license = { file="LICENSE.txt" }
authors = [ authors = [
{ name = "Apache Software Foundation", email = "dev@superset.apache.org" }, { name = "Apache Software Foundation", email = "dev@superset.apache.org" },
] ]
classifiers = [ classifiers = [
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
] ]
@ -45,7 +44,7 @@ dependencies = [
"cryptography>=42.0.4, <44.0.0", "cryptography>=42.0.4, <44.0.0",
"deprecation>=2.1.0, <2.2.0", "deprecation>=2.1.0, <2.2.0",
"flask>=2.2.5, <3.0.0", "flask>=2.2.5, <3.0.0",
"flask-appbuilder>=4.5.0, <5.0.0", "flask-appbuilder>=4.5.3, <5.0.0",
"flask-caching>=2.1.0, <3", "flask-caching>=2.1.0, <3",
"flask-compress>=1.13, <2.0", "flask-compress>=1.13, <2.0",
"flask-talisman>=1.0.0, <2.0", "flask-talisman>=1.0.0, <2.0",
@ -67,7 +66,7 @@ dependencies = [
"markdown>=3.0", "markdown>=3.0",
"msgpack>=1.0.0, <1.1", "msgpack>=1.0.0, <1.1",
"nh3>=0.2.11, <0.3", "nh3>=0.2.11, <0.3",
"numpy==1.23.5", "numpy>1.23.5, <2",
"packaging", "packaging",
# -------------------------- # --------------------------
# pandas and related (wanting pandas[performance] without numba as it's 100+MB and not needed) # pandas and related (wanting pandas[performance] without numba as it's 100+MB and not needed)
@ -86,7 +85,7 @@ dependencies = [
"pyyaml>=6.0.0, <7.0.0", "pyyaml>=6.0.0, <7.0.0",
"PyJWT>=2.4.0, <3.0", "PyJWT>=2.4.0, <3.0",
"redis>=4.6.0, <5.0", "redis>=4.6.0, <5.0",
"selenium>=3.141.0, <4.10.0", "selenium>=4.14.0, <5.0",
"shillelagh[gsheetsapi]>=1.2.18, <2.0", "shillelagh[gsheetsapi]>=1.2.18, <2.0",
"shortid", "shortid",
"sshtunnel>=0.4.0, <0.5", "sshtunnel>=0.4.0, <0.5",
@ -94,9 +93,7 @@ dependencies = [
"slack_sdk>=3.19.0, <4", "slack_sdk>=3.19.0, <4",
"sqlalchemy>=1.4, <2", "sqlalchemy>=1.4, <2",
"sqlalchemy-utils>=0.38.3, <0.39", "sqlalchemy-utils>=0.38.3, <0.39",
# known breaking changes in sqlglot 25.25.0 "sqlglot>=26.1.3, <27",
#https://github.com/tobymao/sqlglot/blob/main/CHANGELOG.md#v25250---2024-10-14
"sqlglot>=25.24.0,<25.25.0",
"sqlparse>=0.5.0", "sqlparse>=0.5.0",
"tabulate>=0.8.9, <0.9", "tabulate>=0.8.9, <0.9",
"typing-extensions>=4, <5", "typing-extensions>=4, <5",
@ -159,6 +156,7 @@ ocient = [
"geojson", "geojson",
] ]
oracle = ["cx-Oracle>8.0.0, <8.1"] oracle = ["cx-Oracle>8.0.0, <8.1"]
parseable = ["sqlalchemy-parseable>=0.1.3,<0.2.0"]
pinot = ["pinotdb>=5.0.0, <6.0.0"] pinot = ["pinotdb>=5.0.0, <6.0.0"]
playwright = ["playwright>=1.37.0, <2"] playwright = ["playwright>=1.37.0, <2"]
postgres = ["psycopg2-binary==2.9.6"] postgres = ["psycopg2-binary==2.9.6"]
@ -175,6 +173,10 @@ spark = [
"tableschema", "tableschema",
"thrift>=0.14.1, <1", "thrift>=0.14.1, <1",
] ]
tdengine = [
"taospy>=2.7.21",
"taos-ws-py>=0.3.8"
]
teradata = ["teradatasql>=16.20.0.23"] teradata = ["teradatasql>=16.20.0.23"]
thumbnails = ["Pillow>=10.0.1, <11"] thumbnails = ["Pillow>=10.0.1, <11"]
vertica = ["sqlalchemy-vertica-python>=0.5.9, < 0.6"] vertica = ["sqlalchemy-vertica-python>=0.5.9, < 0.6"]
@ -275,8 +277,8 @@ exclude = [
line-length = 88 line-length = 88
indent-width = 4 indent-width = 4
# Assume Python 3.9 # Assume Python 3.10
target-version = "py39" target-version = "py310"
[tool.ruff.lint] [tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.

View File

@ -23,8 +23,3 @@ numexpr>=2.9.0
# 5.0.0 has a sensitive deprecation used in other libs # 5.0.0 has a sensitive deprecation used in other libs
# -> https://github.com/aio-libs/async-timeout/blob/master/CHANGES.rst#500-2024-10-31 # -> https://github.com/aio-libs/async-timeout/blob/master/CHANGES.rst#500-2024-10-31
async_timeout>=4.0.0,<5.0.0 async_timeout>=4.0.0,<5.0.0
# playwright requires greenlet==3.0.3
# submitted a PR to relax deps in 11/2024
# https://github.com/microsoft/playwright-python/pull/2669
greenlet==3.0.3

View File

@ -9,14 +9,14 @@ apispec==6.3.0
apsw==3.46.0.0 apsw==3.46.0.0
# via shillelagh # via shillelagh
async-timeout==4.0.3 async-timeout==4.0.3
# via # via -r requirements/base.in
# -r requirements/base.in
# redis
attrs==24.2.0 attrs==24.2.0
# via # via
# cattrs # cattrs
# jsonschema # jsonschema
# outcome
# requests-cache # requests-cache
# trio
babel==2.16.0 babel==2.16.0
# via flask-babel # via flask-babel
backoff==2.2.1 backoff==2.2.1
@ -42,7 +42,9 @@ cattrs==24.1.2
celery==5.4.0 celery==5.4.0
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
certifi==2024.8.30 certifi==2024.8.30
# via requests # via
# requests
# selenium
cffi==1.17.1 cffi==1.17.1
# via # via
# cryptography # cryptography
@ -92,8 +94,6 @@ email-validator==2.2.0
# via flask-appbuilder # via flask-appbuilder
et-xmlfile==2.0.0 et-xmlfile==2.0.0
# via openpyxl # via openpyxl
exceptiongroup==1.2.2
# via cattrs
flask==2.3.3 flask==2.3.3
# via # via
# apache-superset (pyproject.toml) # apache-superset (pyproject.toml)
@ -108,7 +108,7 @@ flask==2.3.3
# flask-session # flask-session
# flask-sqlalchemy # flask-sqlalchemy
# flask-wtf # flask-wtf
flask-appbuilder==4.5.2 flask-appbuilder==4.5.3
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
flask-babel==2.0.0 flask-babel==2.0.0
# via flask-appbuilder # via flask-appbuilder
@ -146,11 +146,12 @@ google-auth==2.36.0
# via shillelagh # via shillelagh
greenlet==3.0.3 greenlet==3.0.3
# via # via
# -r requirements/base.in
# apache-superset (pyproject.toml) # apache-superset (pyproject.toml)
# shillelagh # shillelagh
gunicorn==23.0.0 gunicorn==23.0.0
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
h11==0.14.0
# via wsproto
hashids==1.3.1 hashids==1.3.1
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
holidays==0.25 holidays==0.25
@ -161,6 +162,7 @@ idna==3.10
# via # via
# email-validator # email-validator
# requests # requests
# trio
importlib-metadata==8.5.0 importlib-metadata==8.5.0
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
importlib-resources==6.4.5 importlib-resources==6.4.5
@ -215,7 +217,7 @@ nh3==0.2.19
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
numexpr==2.10.2 numexpr==2.10.2
# via -r requirements/base.in # via -r requirements/base.in
numpy==1.23.5 numpy==1.26.4
# via # via
# apache-superset (pyproject.toml) # apache-superset (pyproject.toml)
# bottleneck # bottleneck
@ -228,6 +230,8 @@ openpyxl==3.1.5
# via pandas # via pandas
ordered-set==4.1.0 ordered-set==4.1.0
# via flask-limiter # via flask-limiter
outcome==1.3.0.post0
# via trio
packaging==24.2 packaging==24.2
# via # via
# apache-superset (pyproject.toml) # apache-superset (pyproject.toml)
@ -283,6 +287,8 @@ pyparsing==3.2.0
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
pyrsistent==0.20.0 pyrsistent==0.20.0
# via jsonschema # via jsonschema
pysocks==1.7.1
# via urllib3
python-dateutil==2.9.0.post0 python-dateutil==2.9.0.post0
# via # via
# apache-superset (pyproject.toml) # apache-superset (pyproject.toml)
@ -319,7 +325,7 @@ rich==13.9.4
# via flask-limiter # via flask-limiter
rsa==4.9 rsa==4.9
# via google-auth # via google-auth
selenium==3.141.0 selenium==4.27.1
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
shillelagh==1.2.18 shillelagh==1.2.18
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
@ -335,6 +341,10 @@ six==1.16.0
# wtforms-json # wtforms-json
slack-sdk==3.33.4 slack-sdk==3.33.4
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
sniffio==1.3.1
# via trio
sortedcontainers==2.4.0
# via trio
sqlalchemy==1.4.54 sqlalchemy==1.4.54
# via # via
# apache-superset (pyproject.toml) # apache-superset (pyproject.toml)
@ -348,7 +358,7 @@ sqlalchemy-utils==0.38.3
# via # via
# apache-superset (pyproject.toml) # apache-superset (pyproject.toml)
# flask-appbuilder # flask-appbuilder
sqlglot==25.24.5 sqlglot==26.1.3
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
sqlparse==0.5.2 sqlparse==0.5.2
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
@ -356,14 +366,19 @@ sshtunnel==0.4.0
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
tabulate==0.8.10 tabulate==0.8.10
# via apache-superset (pyproject.toml) # via apache-superset (pyproject.toml)
trio==0.28.0
# via
# selenium
# trio-websocket
trio-websocket==0.11.1
# via selenium
typing-extensions==4.12.2 typing-extensions==4.12.2
# via # via
# apache-superset (pyproject.toml) # apache-superset (pyproject.toml)
# alembic # alembic
# cattrs
# flask-limiter # flask-limiter
# limits # limits
# rich # selenium
# shillelagh # shillelagh
tzdata==2024.2 tzdata==2024.2
# via # via
@ -385,6 +400,8 @@ vine==5.1.0
# kombu # kombu
wcwidth==0.2.13 wcwidth==0.2.13
# via prompt-toolkit # via prompt-toolkit
websocket-client==1.8.0
# via selenium
werkzeug==3.1.3 werkzeug==3.1.3
# via # via
# -r requirements/base.in # -r requirements/base.in
@ -394,6 +411,8 @@ werkzeug==3.1.3
# flask-login # flask-login
wrapt==1.17.0 wrapt==1.17.0
# via deprecated # via deprecated
wsproto==1.2.0
# via trio-websocket
wtforms==3.2.1 wtforms==3.2.1
# via # via
# apache-superset (pyproject.toml) # apache-superset (pyproject.toml)

View File

@ -18,16 +18,14 @@ apsw==3.46.0.0
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# shillelagh # shillelagh
async-timeout==4.0.3
# via
# -c requirements/base.txt
# redis
attrs==24.2.0 attrs==24.2.0
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# cattrs # cattrs
# jsonschema # jsonschema
# outcome
# requests-cache # requests-cache
# trio
babel==2.16.0 babel==2.16.0
# via # via
# -c requirements/base.txt # -c requirements/base.txt
@ -77,6 +75,7 @@ certifi==2024.8.30
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# requests # requests
# selenium
cffi==1.17.1 cffi==1.17.1
# via # via
# -c requirements/base.txt # -c requirements/base.txt
@ -172,11 +171,6 @@ et-xmlfile==2.0.0
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# openpyxl # openpyxl
exceptiongroup==1.2.2
# via
# -c requirements/base.txt
# cattrs
# pytest
filelock==3.12.2 filelock==3.12.2
# via virtualenv # via virtualenv
flask==2.3.3 flask==2.3.3
@ -196,7 +190,7 @@ flask==2.3.3
# flask-sqlalchemy # flask-sqlalchemy
# flask-testing # flask-testing
# flask-wtf # flask-wtf
flask-appbuilder==4.5.2 flask-appbuilder==4.5.3
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# apache-superset # apache-superset
@ -323,6 +317,10 @@ gunicorn==23.0.0
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# apache-superset # apache-superset
h11==0.14.0
# via
# -c requirements/base.txt
# wsproto
hashids==1.3.1 hashids==1.3.1
# via # via
# -c requirements/base.txt # -c requirements/base.txt
@ -343,6 +341,7 @@ idna==3.10
# -c requirements/base.txt # -c requirements/base.txt
# email-validator # email-validator
# requests # requests
# trio
importlib-metadata==8.5.0 importlib-metadata==8.5.0
# via # via
# -c requirements/base.txt # -c requirements/base.txt
@ -448,7 +447,7 @@ nh3==0.2.19
# apache-superset # apache-superset
nodeenv==1.8.0 nodeenv==1.8.0
# via pre-commit # via pre-commit
numpy==1.23.5 numpy==1.26.4
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# apache-superset # apache-superset
@ -479,6 +478,10 @@ ordered-set==4.1.0
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# flask-limiter # flask-limiter
outcome==1.3.0.post0
# via
# -c requirements/base.txt
# trio
packaging==24.2 packaging==24.2
# via # via
# -c requirements/base.txt # -c requirements/base.txt
@ -629,6 +632,10 @@ pyrsistent==0.20.0
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# jsonschema # jsonschema
pysocks==1.7.1
# via
# -c requirements/base.txt
# urllib3
pytest==7.4.4 pytest==7.4.4
# via # via
# apache-superset # apache-superset
@ -716,7 +723,7 @@ rsa==4.9
# google-auth # google-auth
ruff==0.8.0 ruff==0.8.0
# via apache-superset # via apache-superset
selenium==3.141.0 selenium==4.27.1
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# apache-superset # apache-superset
@ -751,6 +758,14 @@ slack-sdk==3.33.4
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# apache-superset # apache-superset
sniffio==1.3.1
# via
# -c requirements/base.txt
# trio
sortedcontainers==2.4.0
# via
# -c requirements/base.txt
# trio
sqlalchemy==1.4.54 sqlalchemy==1.4.54
# via # via
# -c requirements/base.txt # -c requirements/base.txt
@ -769,7 +784,7 @@ sqlalchemy-utils==0.38.3
# -c requirements/base.txt # -c requirements/base.txt
# apache-superset # apache-superset
# flask-appbuilder # flask-appbuilder
sqlglot==25.24.5 sqlglot==26.1.3
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# apache-superset # apache-superset
@ -789,25 +804,29 @@ tabulate==0.8.10
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# apache-superset # apache-superset
tomli==2.2.1
# via
# coverage
# pytest
tqdm==4.67.1 tqdm==4.67.1
# via # via
# cmdstanpy # cmdstanpy
# prophet # prophet
trino==0.330.0 trino==0.330.0
# via apache-superset # via apache-superset
trio==0.28.0
# via
# -c requirements/base.txt
# selenium
# trio-websocket
trio-websocket==0.11.1
# via
# -c requirements/base.txt
# selenium
typing-extensions==4.12.2 typing-extensions==4.12.2
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# alembic # alembic
# apache-superset # apache-superset
# cattrs
# flask-limiter # flask-limiter
# limits # limits
# rich # selenium
# shillelagh # shillelagh
tzdata==2024.2 tzdata==2024.2
# via # via
@ -840,6 +859,10 @@ wcwidth==0.2.13
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# prompt-toolkit # prompt-toolkit
websocket-client==1.8.0
# via
# -c requirements/base.txt
# selenium
werkzeug==3.1.3 werkzeug==3.1.3
# via # via
# -c requirements/base.txt # -c requirements/base.txt
@ -851,6 +874,10 @@ wrapt==1.17.0
# via # via
# -c requirements/base.txt # -c requirements/base.txt
# deprecated # deprecated
wsproto==1.2.0
# via
# -c requirements/base.txt
# trio-websocket
wtforms==3.2.1 wtforms==3.2.1
# via # via
# -c requirements/base.txt # -c requirements/base.txt

View File

@ -40,7 +40,7 @@ PATTERNS = {
], ],
"docker": [ "docker": [
r"^Dockerfile$", r"^Dockerfile$",
r"^docker/", r"^docker.*",
], ],
"docs": [ "docs": [
r"^docs/", r"^docs/",

View File

@ -20,7 +20,7 @@ import os
import subprocess import subprocess
from datetime import datetime from datetime import datetime
XVFB_PRE_CMD = "xvfb-run --auto-servernum --server-args='-screen 0, 1024x768x24' " XVFB_PRE_CMD = "xvfb-run --auto-servernum --server-args='-screen 0, 1280x1024x24' "
REPO = os.getenv("GITHUB_REPOSITORY") or "apache/superset" REPO = os.getenv("GITHUB_REPOSITORY") or "apache/superset"
GITHUB_EVENT_NAME = os.getenv("GITHUB_EVENT_NAME") or "push" GITHUB_EVENT_NAME = os.getenv("GITHUB_EVENT_NAME") or "push"
CYPRESS_RECORD_KEY = os.getenv("CYPRESS_RECORD_KEY") or "" CYPRESS_RECORD_KEY = os.getenv("CYPRESS_RECORD_KEY") or ""
@ -55,7 +55,9 @@ def run_cypress_for_test_file(
group_id = f"matrix{group}-file{i}-{attempt}" group_id = f"matrix{group}-file{i}-{attempt}"
cmd = ( cmd = (
f"{XVFB_PRE_CMD} " f"{XVFB_PRE_CMD} "
f'{cypress_cmd} --spec "{test_file}" --browser {browser} ' f'{cypress_cmd} --spec "{test_file}" '
f"--config numTestsKeptInMemory=0 "
f"--browser {browser} "
f"--record --group {group_id} --tag {REPO},{GITHUB_EVENT_NAME} " f"--record --group {group_id} --tag {REPO},{GITHUB_EVENT_NAME} "
f"--ci-build-id {build_id} " f"--ci-build-id {build_id} "
f"-- {chrome_flags}" f"-- {chrome_flags}"
@ -64,7 +66,9 @@ def run_cypress_for_test_file(
os.environ.pop("CYPRESS_RECORD_KEY", None) os.environ.pop("CYPRESS_RECORD_KEY", None)
cmd = ( cmd = (
f"{XVFB_PRE_CMD} " f"{XVFB_PRE_CMD} "
f"{cypress_cmd} --browser {browser} " f"{cypress_cmd} "
f"--browser {browser} "
f"--config numTestsKeptInMemory=0 "
f'--spec "{test_file}" ' f'--spec "{test_file}" '
f"-- {chrome_flags}" f"-- {chrome_flags}"
) )

View File

@ -193,6 +193,12 @@ module.exports = {
message: message:
'Default React import is not required due to automatic JSX runtime in React 16.4', 'Default React import is not required due to automatic JSX runtime in React 16.4',
}, },
{
// this disallows wildcard imports from modules (but allows them for local files with `./` or `src/`)
selector:
'ImportNamespaceSpecifier[parent.source.value!=/^(\\.|src)/]',
message: 'Wildcard imports are not allowed',
},
], ],
}, },
settings: { settings: {
@ -325,7 +331,7 @@ module.exports = {
'no-prototype-builtins': 0, 'no-prototype-builtins': 0,
'no-restricted-properties': 0, 'no-restricted-properties': 0,
'no-restricted-imports': [ 'no-restricted-imports': [
'warn', 'error',
{ {
paths: [ paths: [
{ {
@ -333,6 +339,11 @@ module.exports = {
message: message:
'Please import Ant components from the index of src/components', 'Please import Ant components from the index of src/components',
}, },
{
name: 'antd-v5',
message:
'Please import Ant v5 components from the index of src/components',
},
{ {
name: '@superset-ui/core', name: '@superset-ui/core',
importNames: ['supersetTheme'], importNames: ['supersetTheme'],

View File

@ -39,7 +39,6 @@ module.exports = {
{ {
development: process.env.BABEL_ENV === 'development', development: process.env.BABEL_ENV === 'development',
runtime: 'automatic', runtime: 'automatic',
importSource: '@emotion/react',
}, },
], ],
'@babel/preset-typescript', '@babel/preset-typescript',
@ -74,12 +73,23 @@ module.exports = {
corejs: 3, corejs: 3,
loose: true, loose: true,
shippedProposals: true, shippedProposals: true,
modules: 'commonjs', modules: 'auto',
targets: { node: 'current' }, targets: { node: 'current' },
}, },
], ],
[
'@babel/preset-react',
{
development: process.env.BABEL_ENV === 'development',
runtime: 'automatic',
},
],
'@babel/preset-typescript',
],
plugins: [
'babel-plugin-dynamic-import-node',
'@babel/plugin-transform-modules-commonjs',
], ],
plugins: ['babel-plugin-dynamic-import-node'],
}, },
// build instrumented code for testing code coverage with Cypress // build instrumented code for testing code coverage with Cypress
instrumented: { instrumented: {

View File

@ -26,8 +26,9 @@ export default eyesPlugin(
defineConfig({ defineConfig({
chromeWebSecurity: false, chromeWebSecurity: false,
defaultCommandTimeout: 8000, defaultCommandTimeout: 8000,
numTestsKeptInMemory: 0, numTestsKeptInMemory: 3,
experimentalFetchPolyfill: true, // Disabled after realizing this MESSES UP rison encoding in intricate ways
experimentalFetchPolyfill: false,
experimentalMemoryManagement: true, experimentalMemoryManagement: true,
requestTimeout: 10000, requestTimeout: 10000,
video: false, video: false,
@ -57,11 +58,20 @@ export default eyesPlugin(
}); });
launchOptions.args.push( launchOptions.args.push(
...['--disable-dev-shm-usage', '--disable-gpu'], '--disable-dev-shm-usage',
'--disable-gpu',
'--no-sandbox',
'--disable-software-rasterizer',
'--memory-pressure-off',
'--js-flags=--max-old-space-size=4096',
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding',
); );
} }
return launchOptions; return launchOptions;
}); });
// eslint-disable-next-line global-require // eslint-disable-next-line global-require
require('@cypress/code-coverage/task')(on, config); require('@cypress/code-coverage/task')(on, config);
on('task', verifyDownloadTasks); on('task', verifyDownloadTasks);
@ -70,6 +80,7 @@ export default eyesPlugin(
}, },
baseUrl: 'http://localhost:8088', baseUrl: 'http://localhost:8088',
excludeSpecPattern: [], excludeSpecPattern: [],
experimentalRunAllSpecs: true,
specPattern: [ specPattern: [
'cypress/e2e/**/*.{js,jsx,ts,tsx}', 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
'cypress/applitools/**/*.{js,jsx,ts,tsx}', 'cypress/applitools/**/*.{js,jsx,ts,tsx}',

View File

@ -31,7 +31,10 @@ describe('explore view', () => {
}); });
it('should load Explore', () => { it('should load Explore', () => {
const LINE_CHART_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'line' }; const LINE_CHART_DEFAULTS = {
...FORM_DATA_DEFAULTS,
viz_type: 'echarts_timeseries_line',
};
const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] }; const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
cy.visitChartByParams(formData); cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });

View File

@ -26,6 +26,7 @@ import {
visitSampleChartFromList, visitSampleChartFromList,
saveChartToDashboard, saveChartToDashboard,
interceptFiltering, interceptFiltering,
interceptFavoriteStatus,
} from '../explore/utils'; } from '../explore/utils';
import { interceptGet as interceptDashboardGet } from '../dashboard/utils'; import { interceptGet as interceptDashboardGet } from '../dashboard/utils';
@ -49,8 +50,10 @@ function confirmDelete() {
function visitChartList() { function visitChartList() {
interceptFiltering(); interceptFiltering();
interceptFavoriteStatus();
cy.visit(CHART_LIST); cy.visit(CHART_LIST);
cy.wait('@filtering'); cy.wait('@filtering');
cy.wait('@favoriteStatus');
} }
describe('Charts list', () => { describe('Charts list', () => {
@ -78,20 +81,15 @@ describe('Charts list', () => {
cy.wait('@get'); cy.wait('@get');
}); });
it('should show the newly added dashboards in a tooltip', () => { it.only('should show the newly added dashboards in a tooltip', () => {
interceptDashboardGet(); interceptDashboardGet();
visitSampleChartFromList('1 - Sample chart'); visitSampleChartFromList('1 - Sample chart');
saveChartToDashboard('1 - Sample dashboard'); saveChartToDashboard('1 - Sample dashboard');
saveChartToDashboard('2 - Sample dashboard'); saveChartToDashboard('2 - Sample dashboard');
saveChartToDashboard('3 - Sample dashboard'); saveChartToDashboard('3 - Sample dashboard');
visitChartList(); visitChartList();
cy.getBySel('count-crosslinks').should('be.visible'); cy.getBySel('count-crosslinks').should('be.visible');
cy.getBySel('crosslinks').first().trigger('mouseover');
cy.get('.antd5-tooltip')
.contains('3 - Sample dashboard')
.invoke('removeAttr', 'target')
.click();
cy.wait('@get');
}); });
}); });
@ -116,7 +114,7 @@ describe('Charts list', () => {
it('should sort correctly in list mode', () => { it('should sort correctly in list mode', () => {
cy.getBySel('sort-header').eq(1).click(); cy.getBySel('sort-header').eq(1).click();
cy.getBySel('table-row').first().contains('% Rural'); cy.getBySel('table-row').first().contains('Area Chart');
cy.getBySel('sort-header').eq(1).click(); cy.getBySel('sort-header').eq(1).click();
cy.getBySel('table-row').first().contains("World's Population"); cy.getBySel('table-row').first().contains("World's Population");
cy.getBySel('sort-header').eq(1).click(); cy.getBySel('sort-header').eq(1).click();

View File

@ -47,12 +47,12 @@ describe.skip('Dashboard top-level controls', () => {
// Solution: pause the network before clicking, assert, then unpause network. // Solution: pause the network before clicking, assert, then unpause network.
cy.get('[data-test="refresh-chart-menu-item"]').should( cy.get('[data-test="refresh-chart-menu-item"]').should(
'have.class', 'have.class',
'ant-dropdown-menu-item-disabled', 'antd5-dropdown-menu-item-disabled',
); );
waitForChartLoad(mapSpec); waitForChartLoad(mapSpec);
cy.get('[data-test="refresh-chart-menu-item"]').should( cy.get('[data-test="refresh-chart-menu-item"]').should(
'not.have.class', 'not.have.class',
'ant-dropdown-menu-item-disabled', 'antd5-dropdown-menu-item-disabled',
); );
}); });
}); });
@ -65,7 +65,7 @@ describe.skip('Dashboard top-level controls', () => {
cy.get('[aria-label="more-horiz"]').click(); cy.get('[aria-label="more-horiz"]').click();
cy.get('[data-test="refresh-dashboard-menu-item"]').should( cy.get('[data-test="refresh-dashboard-menu-item"]').should(
'not.have.class', 'not.have.class',
'ant-dropdown-menu-item-disabled', 'antd5-dropdown-menu-item-disabled',
); );
cy.get('[data-test="refresh-dashboard-menu-item"]').click({ cy.get('[data-test="refresh-dashboard-menu-item"]').click({
@ -73,7 +73,7 @@ describe.skip('Dashboard top-level controls', () => {
}); });
cy.get('[data-test="refresh-dashboard-menu-item"]').should( cy.get('[data-test="refresh-dashboard-menu-item"]').should(
'have.class', 'have.class',
'ant-dropdown-menu-item-disabled', 'antd5-dropdown-menu-item-disabled',
); );
// wait all charts force refreshed. // wait all charts force refreshed.
@ -94,7 +94,7 @@ describe.skip('Dashboard top-level controls', () => {
cy.get('[aria-label="more-horiz"]').click(); cy.get('[aria-label="more-horiz"]').click();
cy.get('[data-test="refresh-dashboard-menu-item"]').and( cy.get('[data-test="refresh-dashboard-menu-item"]').and(
'not.have.class', 'not.have.class',
'ant-dropdown-menu-item-disabled', 'antd5-dropdown-menu-item-disabled',
); );
}); });
}); });

View File

@ -54,17 +54,22 @@ const drillBy = (targetDrillByColumn: string, isLegacy = false) => {
interceptV1ChartData(); interceptV1ChartData();
} }
cy.get('.ant-dropdown:not(.ant-dropdown-hidden)') cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
.first() .should('be.visible')
.find("[role='menu'] [role='menuitem'] [title='Drill by']") .find("[role='menu'] [role='menuitem']")
.contains(/^Drill by$/)
.trigger('mouseover', { force: true }); .trigger('mouseover', { force: true });
cy.get( cy.get(
'.ant-dropdown-menu-submenu:not(.ant-dropdown-menu-hidden) [data-test="drill-by-submenu"]', '.antd5-dropdown-menu-submenu:not(.antd5-dropdown-menu-submenu-hidden) [data-test="drill-by-submenu"]',
) )
.should('be.visible')
.find('[role="menuitem"]') .find('[role="menuitem"]')
.contains(new RegExp(`^${targetDrillByColumn}$`)) .then($el => {
.first() cy.wrap($el)
.click({ force: true }); .contains(new RegExp(`^${targetDrillByColumn}$`))
.trigger('keydown', { keyCode: 13, which: 13, force: true });
});
if (isLegacy) { if (isLegacy) {
return cy.wait('@legacyData'); return cy.wait('@legacyData');
@ -524,8 +529,8 @@ describe('Drill by modal', () => {
]); ]);
}); });
it('Bar Chart V2', () => { it('Bar Chart', () => {
testEchart('echarts_timeseries_bar', 'Bar Chart V2', [ testEchart('echarts_timeseries_bar', 'Bar Chart', [
[70, 94], [70, 94],
[362, 68], [362, 68],
]); ]);
@ -593,7 +598,7 @@ describe('Drill by modal', () => {
]); ]);
}); });
it('Radar Chart', () => { it.skip('Radar Chart', () => {
testEchart('radar', 'Radar Chart', [ testEchart('radar', 'Radar Chart', [
[182, 49], [182, 49],
[423, 91], [423, 91],

View File

@ -34,8 +34,8 @@ function openModalFromMenu(chartType: string) {
cy.get( cy.get(
`[data-test-viz-type='${chartType}'] [aria-label='More Options']`, `[data-test-viz-type='${chartType}'] [aria-label='More Options']`,
).click(); ).click();
cy.get('.ant-dropdown') cy.get('.antd5-dropdown')
.not('.ant-dropdown-hidden') .not('.antd5-dropdown-hidden')
.find("[role='menu'] [role='menuitem']") .find("[role='menu'] [role='menuitem']")
.eq(5) .eq(5)
.should('contain', 'Drill to detail') .should('contain', 'Drill to detail')
@ -43,37 +43,45 @@ function openModalFromMenu(chartType: string) {
cy.wait('@samples'); cy.wait('@samples');
} }
function openModalFromChartContext(targetMenuItem: string) { function drillToDetail(targetMenuItem: string) {
interceptSamples(); interceptSamples();
if (targetMenuItem.startsWith('Drill to detail by')) { cy.get('.antd5-dropdown')
cy.get('.ant-dropdown') .not('.antd5-dropdown-hidden')
.not('.ant-dropdown-hidden') .first()
.should('be.visible') .find("[role='menu'] [role='menuitem']")
.first() .contains(new RegExp(`^${targetMenuItem}$`))
.find("[role='menu'] [role='menuitem'] [title='Drill to detail by']") .first()
.trigger('mouseover'); .trigger('keydown', { keyCode: 13, which: 13, force: true });
cy.get('[data-test="drill-to-detail-by-submenu"]')
.should('be.visible')
.not('.ant-dropdown-menu-hidden [data-test="drill-to-detail-by-submenu"]')
.should('be.visible')
.find('[role="menuitem"]')
.contains(new RegExp(`^${targetMenuItem}$`))
.first()
.click();
} else {
cy.get('.ant-dropdown')
.not('.ant-dropdown-hidden')
.first()
.find("[role='menu'] [role='menuitem']")
.contains(new RegExp(`^${targetMenuItem}$`))
.first()
.click();
}
cy.getBySel('metadata-bar').should('be.visible'); cy.getBySel('metadata-bar').should('be.visible');
cy.wait('@samples'); cy.wait('@samples');
} }
const drillToDetailBy = (targetDrill: string) => {
interceptSamples();
cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
.should('be.visible')
.find("[role='menu'] [role='menuitem']")
.contains(/^Drill to detail by$/)
.trigger('mouseover', { force: true });
cy.get(
'.antd5-dropdown-menu-submenu:not(.antd5-dropdown-menu-submenu-hidden) [data-test="drill-to-detail-by-submenu"]',
)
.should('be.visible')
.find('[role="menuitem"]')
.then($el => {
cy.wrap($el)
.contains(new RegExp(`^${targetDrill}$`))
.trigger('keydown', { keyCode: 13, which: 13, force: true });
});
cy.getBySel('metadata-bar').should('be.visible');
return cy.wait('@samples');
};
function closeModal() { function closeModal() {
cy.get('body').then($body => { cy.get('body').then($body => {
if ($body.find('[data-test="close-drilltodetail-modal"]').length) { if ($body.find('[data-test="close-drilltodetail-modal"]').length) {
@ -90,7 +98,7 @@ function testTimeChart(vizType: string) {
cy.wrap($canvas).trigger('mousemove', 70, 93); cy.wrap($canvas).trigger('mousemove', 70, 93);
cy.wrap($canvas).rightclick(70, 93); cy.wrap($canvas).rightclick(70, 93);
openModalFromChartContext('Drill to detail by 1965'); drillToDetailBy('Drill to detail by 1965');
cy.getBySel('filter-val').should('contain', '1965'); cy.getBySel('filter-val').should('contain', '1965');
closeModal(); closeModal();
@ -98,7 +106,7 @@ function testTimeChart(vizType: string) {
cy.wrap($canvas).trigger('mousemove', 70, 93); cy.wrap($canvas).trigger('mousemove', 70, 93);
cy.wrap($canvas).rightclick(70, 93); cy.wrap($canvas).rightclick(70, 93);
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
closeModal(); closeModal();
@ -106,24 +114,10 @@ function testTimeChart(vizType: string) {
cy.wrap($canvas).trigger('mousemove', 70, 93); cy.wrap($canvas).trigger('mousemove', 70, 93);
cy.wrap($canvas).rightclick(70, 93); cy.wrap($canvas).rightclick(70, 93);
openModalFromChartContext('Drill to detail by all'); drillToDetailBy('Drill to detail by all');
cy.getBySel('filter-val').first().should('contain', '1965'); cy.getBySel('filter-val').first().should('contain', '1965');
cy.getBySel('filter-val').eq(1).should('contain', 'boy'); cy.getBySel('filter-val').eq(1).should('contain', 'boy');
closeModal(); closeModal();
cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).trigger('mousemove', 70, 145);
cy.wrap($canvas).rightclick(70, 145);
openModalFromChartContext('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl');
closeModal();
cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).trigger('mousemove', 70, 145);
cy.wrap($canvas).rightclick(70, 145);
openModalFromChartContext('Drill to detail by all');
cy.getBySel('filter-val').first().should('contain', '1965');
cy.getBySel('filter-val').eq(1).should('contain', 'girl');
}); });
} }
@ -208,7 +202,7 @@ describe('Drill to detail modal', () => {
"[data-test-viz-type='big_number_total'] .header-line", "[data-test-viz-type='big_number_total'] .header-line",
).rightclick(); ).rightclick();
openModalFromChartContext('Drill to detail'); drillToDetail('Drill to detail');
cy.getBySel('filter-val').should('not.exist'); cy.getBySel('filter-val').should('not.exist');
}); });
@ -224,7 +218,7 @@ describe('Drill to detail modal', () => {
).scrollIntoView(); ).scrollIntoView();
cy.get("[data-test-viz-type='big_number'] .header-line").rightclick(); cy.get("[data-test-viz-type='big_number'] .header-line").rightclick();
openModalFromChartContext('Drill to detail'); drillToDetail('Drill to detail');
cy.getBySel('filter-val').should('not.exist'); cy.getBySel('filter-val').should('not.exist');
@ -236,7 +230,7 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).trigger('mousemove', 1, 14); cy.wrap($canvas).trigger('mousemove', 1, 14);
cy.wrap($canvas).rightclick(1, 14); cy.wrap($canvas).rightclick(1, 14);
openModalFromChartContext('Drill to detail by 1965'); drillToDetailBy('Drill to detail by 1965');
// checking the filter // checking the filter
cy.getBySel('filter-val').should('contain', '1965'); cy.getBySel('filter-val').should('contain', '1965');
@ -255,7 +249,7 @@ describe('Drill to detail modal', () => {
cy.get("[data-test-viz-type='table']").contains('boy').scrollIntoView(); cy.get("[data-test-viz-type='table']").contains('boy').scrollIntoView();
cy.get("[data-test-viz-type='table']").contains('boy').rightclick(); cy.get("[data-test-viz-type='table']").contains('boy').rightclick();
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
@ -267,7 +261,7 @@ describe('Drill to detail modal', () => {
cy.get("[data-test-viz-type='table']").scrollIntoView(); cy.get("[data-test-viz-type='table']").scrollIntoView();
cy.get("[data-test-viz-type='table']").contains('girl').rightclick(); cy.get("[data-test-viz-type='table']").contains('girl').rightclick();
openModalFromChartContext('Drill to detail by girl'); drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl'); cy.getBySel('filter-val').should('contain', 'girl');
}); });
@ -283,7 +277,7 @@ describe('Drill to detail modal', () => {
.first() .first()
.rightclick(); .rightclick();
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
closeModal(); closeModal();
@ -294,7 +288,7 @@ describe('Drill to detail modal', () => {
.first() .first()
.rightclick(); .rightclick();
openModalFromChartContext('Drill to detail by CA'); drillToDetailBy('Drill to detail by CA');
cy.getBySel('filter-val').should('contain', 'CA'); cy.getBySel('filter-val').should('contain', 'CA');
closeModal(); closeModal();
@ -305,7 +299,7 @@ describe('Drill to detail modal', () => {
.eq(3) .eq(3)
.rightclick(); .rightclick();
openModalFromChartContext('Drill to detail by girl'); drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl'); cy.getBySel('filter-val').should('contain', 'girl');
closeModal(); closeModal();
@ -316,7 +310,7 @@ describe('Drill to detail modal', () => {
.eq(3) .eq(3)
.rightclick(); .rightclick();
openModalFromChartContext('Drill to detail by FL'); drillToDetailBy('Drill to detail by FL');
cy.getBySel('filter-val').should('contain', 'FL'); cy.getBySel('filter-val').should('contain', 'FL');
closeModal(); closeModal();
@ -327,7 +321,7 @@ describe('Drill to detail modal', () => {
.eq(3) .eq(3)
.rightclick(); .rightclick();
openModalFromChartContext('Drill to detail by all'); drillToDetailBy('Drill to detail by all');
cy.getBySel('filter-val').first().should('contain', 'girl'); cy.getBySel('filter-val').first().should('contain', 'girl');
cy.getBySel('filter-val').eq(1).should('contain', 'FL'); cy.getBySel('filter-val').eq(1).should('contain', 'FL');
@ -340,7 +334,7 @@ describe('Drill to detail modal', () => {
}); });
}); });
describe('Bar Chart', () => { describe.skip('Bar Chart', () => {
it('opens the modal with the correct filters', () => { it('opens the modal with the correct filters', () => {
interceptSamples(); interceptSamples();
@ -349,21 +343,21 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(70, 100); cy.wrap($canvas).rightclick(70, 100);
openModalFromChartContext('Drill to detail by 1965'); drillToDetailBy('Drill to detail by 1965');
cy.getBySel('filter-val').should('contain', '1965'); cy.getBySel('filter-val').should('contain', '1965');
closeModal(); closeModal();
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(70, 100); cy.wrap($canvas).rightclick(70, 100);
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
closeModal(); closeModal();
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(70, 100); cy.wrap($canvas).rightclick(70, 100);
openModalFromChartContext('Drill to detail by all'); drillToDetailBy('Drill to detail by all');
cy.getBySel('filter-val').first().should('contain', '1965'); cy.getBySel('filter-val').first().should('contain', '1965');
cy.getBySel('filter-val').eq(1).should('contain', 'boy'); cy.getBySel('filter-val').eq(1).should('contain', 'boy');
closeModal(); closeModal();
@ -371,14 +365,14 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(72, 200); cy.wrap($canvas).rightclick(72, 200);
openModalFromChartContext('Drill to detail by girl'); drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl'); cy.getBySel('filter-val').should('contain', 'girl');
}, },
); );
}); });
}); });
describe('Area Chart', () => { describe.skip('Area Chart', () => {
it('opens the modal with the correct filters', () => { it('opens the modal with the correct filters', () => {
testTimeChart('echarts_area'); testTimeChart('echarts_area');
}); });
@ -399,51 +393,38 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(130, 150); cy.wrap($canvas).rightclick(130, 150);
openModalFromChartContext('Drill to detail by girl'); drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl'); cy.getBySel('filter-val').should('contain', 'girl');
closeModal(); closeModal();
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(230, 190); cy.wrap($canvas).rightclick(230, 190);
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
}); });
}); });
}); });
describe('World Map', () => { describe.skip('World Map', () => {
it('opens the modal with the correct filters', () => { it('opens the modal with the correct filters', () => {
interceptSamples(); interceptSamples();
cy.get("[data-test-viz-type='world_map'] svg").then($canvas => { cy.get("[data-test-viz-type='world_map'] svg").then($canvas => {
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(70, 150); cy.wrap($canvas).rightclick(70, 150);
openModalFromChartContext('Drill to detail by USA'); drillToDetailBy('Drill to detail by USA');
cy.getBySel('filter-val').should('contain', 'USA'); cy.getBySel('filter-val').should('contain', 'USA');
closeModal(); closeModal();
}); });
cy.get("[data-test-viz-type='world_map'] svg").then($canvas => { cy.get("[data-test-viz-type='world_map'] svg").then($canvas => {
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(200, 140); cy.wrap($canvas).rightclick(200, 140);
openModalFromChartContext('Drill to detail by SRB'); drillToDetailBy('Drill to detail by SRB');
cy.getBySel('filter-val').should('contain', 'SRB'); cy.getBySel('filter-val').should('contain', 'SRB');
}); });
}); });
}); });
describe('Bar Chart', () => {
it('opens the modal for unsupported chart without filters', () => {
interceptSamples();
cy.get("[data-test-viz-type='dist_bar'] svg").then($canvas => {
cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(70, 150);
openModalFromChartContext('Drill to detail');
cy.getBySel('filter-val').should('not.exist');
});
});
});
}); });
describe('Tier 2 charts', () => { describe('Tier 2 charts', () => {
@ -469,7 +450,7 @@ describe('Drill to detail modal', () => {
force: true, force: true,
}); });
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
// checking the filter // checking the filter
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
@ -505,7 +486,7 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).trigger('mousemove', 135, 275); cy.wrap($canvas).trigger('mousemove', 135, 275);
cy.wrap($canvas).rightclick(135, 275); cy.wrap($canvas).rightclick(135, 275);
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
closeModal(); closeModal();
@ -513,7 +494,7 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).trigger('mousemove', 270, 280); cy.wrap($canvas).trigger('mousemove', 270, 280);
cy.wrap($canvas).rightclick(270, 280); cy.wrap($canvas).rightclick(270, 280);
openModalFromChartContext('Drill to detail by girl'); drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl'); cy.getBySel('filter-val').should('contain', 'girl');
}); });
}); });
@ -545,14 +526,14 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(170, 90); cy.wrap($canvas).rightclick(170, 90);
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
closeModal(); closeModal();
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(190, 250); cy.wrap($canvas).rightclick(190, 250);
openModalFromChartContext('Drill to detail by girl'); drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl'); cy.getBySel('filter-val').should('contain', 'girl');
}); });
}); });
@ -566,14 +547,14 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(135, 95); cy.wrap($canvas).rightclick(135, 95);
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
closeModal(); closeModal();
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(95, 135); cy.wrap($canvas).rightclick(95, 135);
openModalFromChartContext('Drill to detail by girl'); drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl'); cy.getBySel('filter-val').should('contain', 'girl');
}); });
}); });
@ -585,7 +566,7 @@ describe('Drill to detail modal', () => {
}); });
}); });
describe('Radar Chart', () => { describe.skip('Radar Chart', () => {
it('opens the modal with the correct filters', () => { it('opens the modal with the correct filters', () => {
interceptSamples(); interceptSamples();
@ -593,14 +574,14 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(180, 45); cy.wrap($canvas).rightclick(180, 45);
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
closeModal(); closeModal();
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(180, 85); cy.wrap($canvas).rightclick(180, 85);
openModalFromChartContext('Drill to detail by girl'); drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl'); cy.getBySel('filter-val').should('contain', 'girl');
}); });
}); });
@ -614,14 +595,14 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(100, 30); cy.wrap($canvas).rightclick(100, 30);
openModalFromChartContext('Drill to detail by boy'); drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy'); cy.getBySel('filter-val').should('contain', 'boy');
closeModal(); closeModal();
cy.wrap($canvas).scrollIntoView(); cy.wrap($canvas).scrollIntoView();
cy.wrap($canvas).rightclick(150, 250); cy.wrap($canvas).rightclick(150, 250);
openModalFromChartContext('Drill to detail by girl'); drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl'); cy.getBySel('filter-val').should('contain', 'girl');
}); });
}); });

View File

@ -16,13 +16,9 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { import { SAMPLE_DASHBOARD_1, TABBED_DASHBOARD } from 'cypress/utils/urls';
SAMPLE_DASHBOARD_1,
SUPPORTED_CHARTS_DASHBOARD,
TABBED_DASHBOARD,
} from 'cypress/utils/urls';
import { drag, resize, waitForChartLoad } from 'cypress/utils'; import { drag, resize, waitForChartLoad } from 'cypress/utils';
import * as ace from 'brace'; import { edit } from 'brace';
import { import {
interceptExploreUpdate, interceptExploreUpdate,
interceptGet, interceptGet,
@ -30,7 +26,7 @@ import {
openTab, openTab,
} from './utils'; } from './utils';
import { import {
interceptExploreJson, interceptV1ChartData,
interceptFiltering as interceptCharts, interceptFiltering as interceptCharts,
} from '../explore/utils'; } from '../explore/utils';
@ -55,14 +51,6 @@ function openProperties() {
}); });
} }
function openExploreProperties() {
cy.getBySel('actions-trigger').click({ force: true });
cy.get('.ant-dropdown-menu')
.contains('Edit chart properties')
.click({ force: true });
cy.get('.antd5-modal-body').should('be.visible');
}
function assertMetadata(text: string) { function assertMetadata(text: string) {
const regex = new RegExp(text); const regex = new RegExp(text);
cy.get('#json_metadata') cy.get('#json_metadata')
@ -72,7 +60,7 @@ function assertMetadata(text: string) {
// cypress can read this locally, but not in ci // cypress can read this locally, but not in ci
// so we have to use the ace module directly to fetch the value // so we have to use the ace module directly to fetch the value
expect(ace.edit(metadata).getValue()).to.match(regex); expect(edit(metadata).getValue()).to.match(regex);
}); });
} }
@ -225,19 +213,20 @@ function writeMetadata(metadata: string) {
} }
function openExploreWithDashboardContext(chartName: string) { function openExploreWithDashboardContext(chartName: string) {
interceptExploreJson(); interceptV1ChartData();
interceptGet(); interceptGet();
cy.get( cy.get(
`[data-test-chart-name='${chartName}'] [aria-label='More Options']`, `[data-test-chart-name='${chartName}'] [aria-label='More Options']`,
).click(); ).click();
cy.get('.ant-dropdown') cy.get(`[data-test-edit-chart-name='${chartName}']`)
.not('.ant-dropdown-hidden') .should('be.visible')
.find("[role='menu'] [role='menuitem']") .trigger('keydown', {
.eq(2) keyCode: 13,
.should('contain', 'Edit chart') which: 13,
.click(); force: true,
cy.wait('@getJson'); });
cy.wait('@v1Data');
cy.get('.chart-container').should('exist'); cy.get('.chart-container').should('exist');
} }
@ -255,13 +244,16 @@ function saveExploreColorScheme(
cy.wait('@chartUpdate'); cy.wait('@chartUpdate');
} }
// FIXME: Skipping some tests as ECharts are rendered using Canvas and we cannot inspect the elements
// to verify the colors. We should revisit these tests once we have a solution to verify ECharts.
describe('Dashboard edit', () => { describe('Dashboard edit', () => {
describe('Color consistency', () => { describe('Color consistency', () => {
beforeEach(() => { beforeEach(() => {
resetDashboardColors(); resetDashboardColors();
}); });
it('should not allow to change color scheme of a chart when dashboard has one', () => { it.skip('should not allow to change color scheme of a chart when dashboard has one', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
selectColorScheme('blueToGreen'); selectColorScheme('blueToGreen');
@ -272,11 +264,14 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
openExploreWithDashboardContext('Top 10 California Names Timeseries'); openExploreWithDashboardContext('Top 10 California Names Timeseries');
// hover over canvas elements
cy.get('canvas').trigger('mouseover', { force: true });
// label Anthony // label Anthony
cy.get('[data-test="chart-container"] .line .nv-legend-symbol') cy.get('[data-test="chart-container"] .line .nv-legend-symbol')
.first() .first()
@ -287,21 +282,21 @@ describe('Dashboard edit', () => {
cy.get('[aria-label="Select color scheme"]').should('be.disabled'); cy.get('[aria-label="Select color scheme"]').should('be.disabled');
}); });
it('should not allow to change color scheme of a chart when dashboard has no scheme but chart has shared labels', () => { it.skip('should not allow to change color scheme of a chart when dashboard has no scheme but chart has shared labels', () => {
visit(TABBED_DASHBOARD); visit(TABBED_DASHBOARD);
// open nested tab // open nested tab
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// open second top tab to catch shared labels // open second top tab to catch shared labels
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Trends', name: 'Trends',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
openTab(0, 0); openTab(0, 0);
@ -317,7 +312,7 @@ describe('Dashboard edit', () => {
cy.get('[aria-label="Select color scheme"]').should('be.disabled'); cy.get('[aria-label="Select color scheme"]').should('be.disabled');
}); });
it('should allow to change color scheme of a chart when dashboard has no scheme but only custom label colors', () => { it.skip('should allow to change color scheme of a chart when dashboard has no scheme but only custom label colors', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
openAdvancedProperties(); openAdvancedProperties();
@ -330,7 +325,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -371,14 +366,14 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(0, 116, 241)'); .should('have.css', 'fill', 'rgb(0, 116, 241)');
}); });
it('should allow to change color scheme of a chart when dashboard has no scheme and show the change', () => { it.skip('should allow to change color scheme of a chart when dashboard has no scheme and show the change', () => {
visit(TABBED_DASHBOARD); visit(TABBED_DASHBOARD);
// open nested tab // open nested tab
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -414,7 +409,7 @@ describe('Dashboard edit', () => {
saveExploreColorScheme(); saveExploreColorScheme();
}); });
it('should allow to change color scheme of a chart when dashboard has no scheme but custom label colors and show the change', () => { it.skip('should allow to change color scheme of a chart when dashboard has no scheme but custom label colors and show the change', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
openAdvancedProperties(); openAdvancedProperties();
@ -427,7 +422,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -473,14 +468,14 @@ describe('Dashboard edit', () => {
saveExploreColorScheme(); saveExploreColorScheme();
}); });
it('should not change colors on refreshes with no color scheme set', () => { it.skip('should not change colors on refreshes with no color scheme set', () => {
visit(TABBED_DASHBOARD); visit(TABBED_DASHBOARD);
// open nested tab // open nested tab
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -492,7 +487,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab // open 2nd main tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Andrew // label Andrew
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -505,7 +500,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -517,7 +512,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab // open 2nd main tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Andrew // label Andrew
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -525,7 +520,7 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(69, 78, 124)'); .should('have.css', 'fill', 'rgb(69, 78, 124)');
}); });
it('should not change colors on refreshes with color scheme set', () => { it.skip('should not change colors on refreshes with color scheme set', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
selectColorScheme('blueToGreen'); selectColorScheme('blueToGreen');
@ -536,7 +531,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -548,7 +543,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab // open 2nd main tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Andrew // label Andrew
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -561,7 +556,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -573,7 +568,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab // open 2nd main tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Andrew // label Andrew
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -581,14 +576,14 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(0, 76, 218)'); .should('have.css', 'fill', 'rgb(0, 76, 218)');
}); });
it('should respect chart color scheme when none is set for the dashboard', () => { it.skip('should respect chart color scheme when none is set for the dashboard', () => {
visit(TABBED_DASHBOARD); visit(TABBED_DASHBOARD);
// open nested tab // open nested tab
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -599,7 +594,7 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(31, 168, 201)'); .should('have.css', 'fill', 'rgb(31, 168, 201)');
}); });
it('should apply same color to same labels with color scheme set on refresh', () => { it.skip('should apply same color to same labels with color scheme set on refresh', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
selectColorScheme('blueToGreen'); selectColorScheme('blueToGreen');
@ -610,7 +605,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -622,7 +617,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab // open 2nd main tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Anthony // label Anthony
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -634,7 +629,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -646,7 +641,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab // open 2nd main tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Anthony // label Anthony
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -654,14 +649,14 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(50, 0, 167)'); .should('have.css', 'fill', 'rgb(50, 0, 167)');
}); });
it('should apply same color to same labels with no color scheme set on refresh', () => { it.skip('should apply same color to same labels with no color scheme set on refresh', () => {
visit(TABBED_DASHBOARD); visit(TABBED_DASHBOARD);
// open nested tab // open nested tab
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -673,7 +668,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab // open 2nd main tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Anthony // label Anthony
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -686,7 +681,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -698,7 +693,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab // open 2nd main tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Anthony // label Anthony
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -706,7 +701,7 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(31, 168, 201)'); .should('have.css', 'fill', 'rgb(31, 168, 201)');
}); });
it('custom label colors should take the precedence in nested tabs', () => { it.skip('custom label colors should take the precedence in nested tabs', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
openAdvancedProperties(); openAdvancedProperties();
@ -721,7 +716,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
cy.get( cy.get(
'[data-test-chart-name="Top 10 California Names Timeseries"] .line .nv-legend-symbol', '[data-test-chart-name="Top 10 California Names Timeseries"] .line .nv-legend-symbol',
@ -731,19 +726,19 @@ describe('Dashboard edit', () => {
// open another nested tab // open another nested tab
openTab(2, 1); openTab(2, 1);
waitForChartLoad({ name: 'Growth Rate', viz: 'line' }); waitForChartLoad({ name: 'Growth Rate', viz: 'echarts_timeseries_line' });
cy.get('[data-test-chart-name="Growth Rate"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Growth Rate"] .line .nv-legend-symbol')
.first() .first()
.should('have.css', 'fill', 'rgb(255, 0, 0)'); .should('have.css', 'fill', 'rgb(255, 0, 0)');
}); });
it('label colors should take the precedence for rendered charts in nested tabs', () => { it.skip('label colors should take the precedence for rendered charts in nested tabs', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
// open the tab first time and let chart load // open the tab first time and let chart load
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// go to previous tab // go to previous tab
@ -766,7 +761,7 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(255, 0, 0)'); .should('have.css', 'fill', 'rgb(255, 0, 0)');
}); });
it('should re-apply original color after removing custom label color with color scheme set', () => { it.skip('should re-apply original color after removing custom label color with color scheme set', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
openAdvancedProperties(); openAdvancedProperties();
@ -809,13 +804,13 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(41, 171, 226)'); .should('have.css', 'fill', 'rgb(41, 171, 226)');
}); });
it('should re-apply original color after removing custom label color with no color scheme set', () => { it.skip('should re-apply original color after removing custom label color with no color scheme set', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
// open nested tab // open nested tab
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
cy.get( cy.get(
'[data-test-chart-name="Top 10 California Names Timeseries"] .line .nv-legend-symbol', '[data-test-chart-name="Top 10 California Names Timeseries"] .line .nv-legend-symbol',
@ -873,7 +868,7 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(90, 193, 137)'); .should('have.css', 'fill', 'rgb(90, 193, 137)');
}); });
it('should show the same colors in Explore', () => { it.skip('should show the same colors in Explore', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
openAdvancedProperties(); openAdvancedProperties();
@ -888,7 +883,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -906,7 +901,7 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(255, 0, 0)'); .should('have.css', 'fill', 'rgb(255, 0, 0)');
}); });
it('should change color scheme multiple times', () => { it.skip('should change color scheme multiple times', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
selectColorScheme('blueToGreen'); selectColorScheme('blueToGreen');
@ -917,7 +912,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// label Anthony // label Anthony
@ -929,7 +924,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab // open 2nd main tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Anthony // label Anthony
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -959,7 +954,7 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(0, 128, 246)'); .should('have.css', 'fill', 'rgb(0, 128, 246)');
}); });
it('should apply the color scheme across main tabs', () => { it.skip('should apply the color scheme across main tabs', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
selectColorScheme('blueToGreen'); selectColorScheme('blueToGreen');
@ -968,14 +963,14 @@ describe('Dashboard edit', () => {
// go to second tab // go to second tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
.first() .first()
.should('have.css', 'fill', 'rgb(50, 0, 167)'); .should('have.css', 'fill', 'rgb(50, 0, 167)');
}); });
it('should apply the color scheme across main tabs for rendered charts', () => { it.skip('should apply the color scheme across main tabs for rendered charts', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
waitForChartLoad({ name: 'Treemap', viz: 'treemap_v2' }); waitForChartLoad({ name: 'Treemap', viz: 'treemap_v2' });
openProperties(); openProperties();
@ -985,7 +980,7 @@ describe('Dashboard edit', () => {
// go to second tab // go to second tab
openTab(0, 1); openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' }); waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
.first() .first()
@ -1003,7 +998,7 @@ describe('Dashboard edit', () => {
.should('have.css', 'fill', 'rgb(0, 128, 246)'); .should('have.css', 'fill', 'rgb(0, 128, 246)');
}); });
it('should apply the color scheme in nested tabs', () => { it.skip('should apply the color scheme in nested tabs', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
openProperties(); openProperties();
selectColorScheme('blueToGreen'); selectColorScheme('blueToGreen');
@ -1014,7 +1009,7 @@ describe('Dashboard edit', () => {
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
cy.get( cy.get(
'[data-test-chart-name="Top 10 California Names Timeseries"] .line .nv-legend-symbol', '[data-test-chart-name="Top 10 California Names Timeseries"] .line .nv-legend-symbol',
@ -1024,19 +1019,19 @@ describe('Dashboard edit', () => {
// open another nested tab // open another nested tab
openTab(2, 1); openTab(2, 1);
waitForChartLoad({ name: 'Growth Rate', viz: 'line' }); waitForChartLoad({ name: 'Growth Rate', viz: 'echarts_timeseries_line' });
cy.get('[data-test-chart-name="Growth Rate"] .line .nv-legend-symbol') cy.get('[data-test-chart-name="Growth Rate"] .line .nv-legend-symbol')
.first() .first()
.should('have.css', 'fill', 'rgb(50, 0, 167)'); .should('have.css', 'fill', 'rgb(50, 0, 167)');
}); });
it('should apply a valid color scheme for rendered charts in nested tabs', () => { it.skip('should apply a valid color scheme for rendered charts in nested tabs', () => {
visitEdit(TABBED_DASHBOARD); visitEdit(TABBED_DASHBOARD);
// open the tab first time and let chart load // open the tab first time and let chart load
openTab(1, 1); openTab(1, 1);
waitForChartLoad({ waitForChartLoad({
name: 'Top 10 California Names Timeseries', name: 'Top 10 California Names Timeseries',
viz: 'line', viz: 'echarts_timeseries_line',
}); });
// go to previous tab // go to previous tab

View File

@ -57,16 +57,16 @@ function setFilterBarOrientation(orientation: 'vertical' | 'horizontal') {
.trigger('mouseover'); .trigger('mouseover');
if (orientation === 'vertical') { if (orientation === 'vertical') {
cy.get('.ant-dropdown-menu-item-selected') cy.get('.antd5-dropdown-menu-item-selected')
.contains('Horizontal (Top)') .contains('Horizontal (Top)')
.should('exist'); .should('exist');
cy.get('.ant-dropdown-menu-item').contains('Vertical (Left)').click(); cy.get('.antd5-dropdown-menu-item').contains('Vertical (Left)').click();
cy.getBySel('dashboard-filters-panel').should('exist'); cy.getBySel('dashboard-filters-panel').should('exist');
} else { } else {
cy.get('.ant-dropdown-menu-item-selected') cy.get('.antd5-dropdown-menu-item-selected')
.contains('Vertical (Left)') .contains('Vertical (Left)')
.should('exist'); .should('exist');
cy.get('.ant-dropdown-menu-item').contains('Horizontal (Top)').click(); cy.get('.antd5-dropdown-menu-item').contains('Horizontal (Top)').click();
cy.getBySel('loading-indicator').should('exist'); cy.getBySel('loading-indicator').should('exist');
cy.getBySel('filter-bar').should('exist'); cy.getBySel('filter-bar').should('exist');
cy.getBySel('dashboard-filters-panel').should('not.exist'); cy.getBySel('dashboard-filters-panel').should('not.exist');
@ -176,7 +176,7 @@ describe('Horizontal FilterBar', () => {
validateFilterNameOnDashboard(testItems.topTenChart.filterColumn); validateFilterNameOnDashboard(testItems.topTenChart.filterColumn);
}); });
it('should spot changes in "more filters" and apply their values', () => { it.skip('should spot changes in "more filters" and apply their values', () => {
cy.intercept(`/api/v1/chart/data?form_data=**`).as('chart'); cy.intercept(`/api/v1/chart/data?form_data=**`).as('chart');
prepareDashboardFilters([ prepareDashboardFilters([
{ name: 'test_1', column: 'country_name', datasetId: 2 }, { name: 'test_1', column: 'country_name', datasetId: 2 },
@ -204,7 +204,7 @@ describe('Horizontal FilterBar', () => {
); );
}); });
it('should focus filter and open "more filters" programmatically', () => { it.skip('should focus filter and open "more filters" programmatically', () => {
prepareDashboardFilters([ prepareDashboardFilters([
{ name: 'test_1', column: 'country_name', datasetId: 2 }, { name: 'test_1', column: 'country_name', datasetId: 2 },
{ name: 'test_2', column: 'country_code', datasetId: 2 }, { name: 'test_2', column: 'country_code', datasetId: 2 },
@ -231,7 +231,7 @@ describe('Horizontal FilterBar', () => {
cy.get('.ant-select-focused').should('be.visible'); cy.get('.ant-select-focused').should('be.visible');
}); });
it('should show tag count and one plain tag on focus and only count on blur in select ', () => { it.skip('should show tag count and one plain tag on focus and only count on blur in select ', () => {
prepareDashboardFilters([ prepareDashboardFilters([
{ name: 'test_1', column: 'country_name', datasetId: 2 }, { name: 'test_1', column: 'country_name', datasetId: 2 },
]); ]);

View File

@ -25,7 +25,7 @@ import { TABBED_DASHBOARD } from 'cypress/utils/urls';
import { expandFilterOnLeftPanel } from './utils'; import { expandFilterOnLeftPanel } from './utils';
const TREEMAP = { name: 'Treemap', viz: 'treemap_v2' }; const TREEMAP = { name: 'Treemap', viz: 'treemap_v2' };
const LINE_CHART = { name: 'Growth Rate', viz: 'line' }; const LINE_CHART = { name: 'Growth Rate', viz: 'echarts_timeseries_line' };
const BOX_PLOT = { name: 'Box plot', viz: 'box_plot' }; const BOX_PLOT = { name: 'Box plot', viz: 'box_plot' };
const BIG_NUMBER = { name: 'Number of Girls', viz: 'big_number_total' }; const BIG_NUMBER = { name: 'Number of Girls', viz: 'big_number_total' };
const TABLE = { name: 'Names Sorted by Num in California', viz: 'table' }; const TABLE = { name: 'Names Sorted by Num in California', viz: 'table' };
@ -64,9 +64,8 @@ describe('Dashboard tabs', () => {
cy.get('@top-level-tabs') cy.get('@top-level-tabs')
.last() .last()
.should('not.have.class', 'ant-tabs-tab-active'); .should('not.have.class', 'ant-tabs-tab-active');
cy.get('[data-test-chart-name="Box plot"]').should('not.exist');
cy.getBySel('grid-container').find('.box_plot').should('not.exist'); cy.get('[data-test-chart-name="Trends"]').should('not.exist');
cy.getBySel('grid-container').find('.line').should('not.exist');
cy.get('@top-level-tabs').last().click(); cy.get('@top-level-tabs').last().click();
cy.get('@top-level-tabs') cy.get('@top-level-tabs')
@ -76,7 +75,8 @@ describe('Dashboard tabs', () => {
.first() .first()
.should('not.have.class', 'ant-tabs-tab-active'); .should('not.have.class', 'ant-tabs-tab-active');
waitForChartLoad(BOX_PLOT); waitForChartLoad(BOX_PLOT);
cy.getBySel('grid-container').find('.box_plot').should('be.visible');
cy.get('[data-test-chart-name="Box plot"]').should('exist');
resetTabs(); resetTabs();
@ -88,7 +88,7 @@ describe('Dashboard tabs', () => {
cy.get('@row-level-tabs').last().click(); cy.get('@row-level-tabs').last().click();
waitForChartLoad(LINE_CHART); waitForChartLoad(LINE_CHART);
cy.getBySel('grid-container').find('.line').should('be.visible'); cy.get('[data-test-chart-name="Trends"]').should('exist');
cy.get('@row-level-tabs').first().click(); cy.get('@row-level-tabs').first().click();
}); });

View File

@ -24,9 +24,9 @@ export const WORLD_HEALTH_CHARTS = [
{ name: '% Rural', viz: 'world_map' }, { name: '% Rural', viz: 'world_map' },
{ name: 'Most Populated Countries', viz: 'table' }, { name: 'Most Populated Countries', viz: 'table' },
{ name: "World's Population", viz: 'big_number' }, { name: "World's Population", viz: 'big_number' },
{ name: 'Growth Rate', viz: 'line' }, { name: 'Growth Rate', viz: 'echarts_timeseries_line' },
{ name: 'Rural Breakdown', viz: 'sunburst_v2' }, { name: 'Rural Breakdown', viz: 'sunburst_v2' },
{ name: "World's Pop Growth", viz: 'area' }, { name: "World's Pop Growth", viz: 'echarts_area' },
{ name: 'Life Expectancy VS Rural %', viz: 'bubble' }, { name: 'Life Expectancy VS Rural %', viz: 'bubble' },
{ name: 'Treemap', viz: 'treemap_v2' }, { name: 'Treemap', viz: 'treemap_v2' },
{ name: 'Box plot', viz: 'box_plot' }, { name: 'Box plot', viz: 'box_plot' },
@ -41,7 +41,7 @@ export const SUPPORTED_TIER1_CHARTS = [
{ name: 'Line Chart', viz: 'echarts_timeseries_line' }, { name: 'Line Chart', viz: 'echarts_timeseries_line' },
{ name: 'Area Chart', viz: 'echarts_area' }, { name: 'Area Chart', viz: 'echarts_area' },
{ name: 'Scatter Chart', viz: 'echarts_timeseries_scatter' }, { name: 'Scatter Chart', viz: 'echarts_timeseries_scatter' },
{ name: 'Bar Chart V2', viz: 'echarts_timeseries_bar' }, { name: 'Bar Chart', viz: 'echarts_timeseries_bar' },
] as ChartSpec[]; ] as ChartSpec[];
export const SUPPORTED_TIER2_CHARTS = [ export const SUPPORTED_TIER2_CHARTS = [

View File

@ -40,9 +40,25 @@ function openMenu() {
cy.get('[aria-label="more-vert"]').first().click(); cy.get('[aria-label="more-vert"]').first().click();
} }
function confirmDelete() { function confirmDelete(bulk = false) {
cy.getBySel('delete-modal-input').type('DELETE'); interceptDelete();
cy.getBySel('modal-confirm-button').click(); interceptBulkDelete();
// Wait for modal dialog to be present and visible
cy.get('[role="dialog"][aria-modal="true"]').should('be.visible');
cy.getBySel('delete-modal-input')
.should('be.visible')
.then($input => {
cy.wrap($input).clear();
cy.wrap($input).type('DELETE');
});
cy.getBySel('modal-confirm-button').should('be.visible').click();
if (bulk) {
cy.wait('@bulkDelete');
} else {
cy.wait('@delete');
}
} }
describe('Dashboards list', () => { describe('Dashboards list', () => {
@ -157,7 +173,6 @@ describe('Dashboards list', () => {
}); });
it('should bulk delete correctly', () => { it('should bulk delete correctly', () => {
interceptBulkDelete();
toggleBulkSelect(); toggleBulkSelect();
// bulk deletes in card-view // bulk deletes in card-view
@ -167,8 +182,7 @@ describe('Dashboards list', () => {
cy.getBySel('styled-card').eq(0).contains('1 - Sample dashboard').click(); cy.getBySel('styled-card').eq(0).contains('1 - Sample dashboard').click();
cy.getBySel('styled-card').eq(1).contains('2 - Sample dashboard').click(); cy.getBySel('styled-card').eq(1).contains('2 - Sample dashboard').click();
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click(); cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
confirmDelete(); confirmDelete(true);
cy.wait('@bulkDelete');
cy.getBySel('styled-card') cy.getBySel('styled-card')
.eq(0) .eq(0)
.should('not.contain', '1 - Sample dashboard'); .should('not.contain', '1 - Sample dashboard');
@ -183,8 +197,7 @@ describe('Dashboards list', () => {
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(0).click(); cy.get('[data-test="table-row"] input[type="checkbox"]').eq(0).click();
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(1).click(); cy.get('[data-test="table-row"] input[type="checkbox"]').eq(1).click();
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click(); cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
confirmDelete(); confirmDelete(true);
cy.wait('@bulkDelete');
cy.getBySel('table-row') cy.getBySel('table-row')
.eq(0) .eq(0)
.should('not.contain', '3 - Sample dashboard'); .should('not.contain', '3 - Sample dashboard');
@ -193,31 +206,36 @@ describe('Dashboards list', () => {
.should('not.contain', '4 - Sample dashboard'); .should('not.contain', '4 - Sample dashboard');
}); });
it('should delete correctly', () => { it.skip('should delete correctly in list mode', () => {
interceptDelete(); // deletes in list-view
setGridMode('list');
cy.getBySel('table-row')
.eq(0)
.contains('4 - Sample dashboard')
.should('exist');
cy.getBySel('dashboard-list-trash-icon').eq(0).click();
confirmDelete();
cy.getBySel('table-row')
.eq(0)
.should('not.contain', '4 - Sample dashboard');
});
it('should delete correctly in card mode', () => {
// deletes in card-view // deletes in card-view
setGridMode('card'); setGridMode('card');
orderAlphabetical(); orderAlphabetical();
cy.getBySel('styled-card').eq(0).contains('1 - Sample dashboard'); cy.getBySel('styled-card')
.eq(0)
.contains('1 - Sample dashboard')
.should('exist');
openMenu(); openMenu();
cy.getBySel('dashboard-card-option-delete-button').click(); cy.getBySel('dashboard-card-option-delete-button').click();
confirmDelete(); confirmDelete();
cy.wait('@delete');
cy.getBySel('styled-card') cy.getBySel('styled-card')
.eq(0) .eq(0)
.should('not.contain', '1 - Sample dashboard'); .should('not.contain', '1 - Sample dashboard');
// deletes in list-view
setGridMode('list');
cy.getBySel('table-row').eq(0).contains('2 - Sample dashboard');
cy.getBySel('dashboard-list-trash-icon').eq(0).click();
confirmDelete();
cy.wait('@delete');
cy.getBySel('table-row')
.eq(0)
.should('not.contain', '2 - Sample dashboard');
}); });
it('should edit correctly', () => { it('should edit correctly', () => {

View File

@ -16,12 +16,13 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { interceptChart } from 'cypress/utils';
describe('AdhocMetrics', () => { describe('AdhocMetrics', () => {
beforeEach(() => { beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('postJson'); interceptChart({ legacy: false }).as('chartData');
cy.intercept('GET', '/superset/explore_json/**').as('getJson');
cy.visitChartByName('Num Births Trend'); cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
}); });
it('Clear metric and set simple adhoc metric', () => { it('Clear metric and set simple adhoc metric', () => {
@ -52,9 +53,8 @@ describe('AdhocMetrics', () => {
cy.get('button[data-test="run-query-button"]').click(); cy.get('button[data-test="run-query-button"]').click();
cy.verifySliceSuccess({ cy.verifySliceSuccess({
waitAlias: '@postJson', waitAlias: '@chartData',
querySubstring: `${metric} AS "${metricName}"`, // SQL statement querySubstring: `${metric} AS "${metricName}"`, // SQL statement
chartSelector: 'svg',
}); });
}); });
@ -88,9 +88,8 @@ describe('AdhocMetrics', () => {
const metric = 'SUM(num)/COUNT(DISTINCT name)'; const metric = 'SUM(num)/COUNT(DISTINCT name)';
cy.verifySliceSuccess({ cy.verifySliceSuccess({
waitAlias: '@postJson', waitAlias: '@chartData',
querySubstring: `${metric} AS "${metric}"`, querySubstring: `${metric} AS "${metric}"`,
chartSelector: 'svg',
}); });
}); });
@ -117,9 +116,8 @@ describe('AdhocMetrics', () => {
const metric = 'SUM(num)'; const metric = 'SUM(num)';
cy.verifySliceSuccess({ cy.verifySliceSuccess({
waitAlias: '@postJson', waitAlias: '@chartData',
querySubstring: `${metric} AS "${metric}"`, querySubstring: `${metric} AS "${metric}"`,
chartSelector: 'svg',
}); });
}); });
}); });

View File

@ -16,20 +16,21 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { interceptV1ChartData } from './utils';
describe('Advanced analytics', () => { describe('Advanced analytics', () => {
beforeEach(() => { beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('postJson'); interceptV1ChartData();
cy.intercept('GET', '/superset/explore_json/**').as('getJson');
cy.intercept('PUT', '/api/v1/explore/**').as('putExplore'); cy.intercept('PUT', '/api/v1/explore/**').as('putExplore');
cy.intercept('GET', '/explore/**').as('getExplore'); cy.intercept('GET', '/explore/**').as('getExplore');
}); });
it('Create custom time compare', () => { it('Create custom time compare', () => {
cy.visitChartByName('Num Births Trend'); cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@v1Data' });
cy.get('.ant-collapse-header') cy.get('.ant-collapse-header')
.contains('Advanced Analytics') .contains('Advanced analytics')
.click({ force: true }); .click({ force: true });
cy.get('[data-test=time_compare]').find('.ant-select').click(); cy.get('[data-test=time_compare]').find('.ant-select').click();
@ -43,17 +44,16 @@ describe('Advanced analytics', () => {
.type('1 year{enter}'); .type('1 year{enter}');
cy.get('button[data-test="run-query-button"]').click(); cy.get('button[data-test="run-query-button"]').click();
cy.wait('@postJson'); cy.wait('@v1Data');
cy.wait('@putExplore'); cy.wait('@putExplore');
cy.reload(); cy.reload();
cy.verifySliceSuccess({ cy.verifySliceSuccess({
waitAlias: '@postJson', waitAlias: '@v1Data',
chartSelector: 'svg',
}); });
cy.wait('@getExplore'); cy.wait('@getExplore');
cy.get('.ant-collapse-header') cy.get('.ant-collapse-header')
.contains('Advanced Analytics') .contains('Advanced analytics')
.click({ force: true }); .click({ force: true });
cy.get('[data-test=time_compare]') cy.get('[data-test=time_compare]')
.find('.ant-select-selector') .find('.ant-select-selector')

View File

@ -16,18 +16,22 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
describe('Annotations', () => { import { interceptChart } from 'cypress/utils';
describe.skip('Annotations', () => {
beforeEach(() => { beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('postJson'); interceptChart({ legacy: false }).as('chartData');
cy.intercept('GET', '/superset/explore_json/**').as('getJson');
}); });
it('Create formula annotation y-axis goal line', () => { it('Create formula annotation y-axis goal line', () => {
cy.visitChartByName('Num Births Trend'); cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
const layerLabel = 'Goal line'; const layerLabel = 'Goal line';
// get by text Annotations and Layers
cy.get('span').contains('Annotations and Layers').click();
cy.get('[data-test=annotation_layers]').click(); cy.get('[data-test=annotation_layers]').click();
cy.get('[data-test="popover-content"]').within(() => { cy.get('[data-test="popover-content"]').within(() => {
@ -39,10 +43,6 @@ describe('Annotations', () => {
cy.get('button[data-test="run-query-button"]').click(); cy.get('button[data-test="run-query-button"]').click();
cy.get('[data-test=annotation_layers]').contains(layerLabel); cy.get('[data-test=annotation_layers]').contains(layerLabel);
cy.verifySliceSuccess({ cy.verifySliceSuccess({ waitAlias: '@chartData' });
waitAlias: '@postJson',
chartSelector: 'svg',
});
cy.get('.nv-legend-text').should('have.length', 2);
}); });
}); });

View File

@ -21,6 +21,7 @@ import { interceptGet as interceptDashboardGet } from 'cypress/e2e/dashboard/uti
import { FORM_DATA_DEFAULTS, NUM_METRIC } from './visualizations/shared.helper'; import { FORM_DATA_DEFAULTS, NUM_METRIC } from './visualizations/shared.helper';
import { import {
interceptFiltering, interceptFiltering,
interceptV1ChartData,
saveChartToDashboard, saveChartToDashboard,
visitSampleChartFromList, visitSampleChartFromList,
} from './utils'; } from './utils';
@ -30,35 +31,35 @@ const SAMPLE_DASHBOARDS_INDEXES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
function openDashboardsAddedTo() { function openDashboardsAddedTo() {
cy.getBySel('actions-trigger').click(); cy.getBySel('actions-trigger').click();
cy.get('.ant-dropdown-menu-submenu-title') cy.get('.antd5-dropdown-menu-submenu-title')
.contains('On dashboards') .contains('On dashboards')
.trigger('mouseover', { force: true }); .trigger('mouseover', { force: true });
} }
function closeDashboardsAddedTo() { function closeDashboardsAddedTo() {
cy.get('.ant-dropdown-menu-submenu-title') cy.get('.antd5-dropdown-menu-submenu-title')
.contains('On dashboards') .contains('On dashboards')
.trigger('mouseout', { force: true }); .trigger('mouseout', { force: true });
cy.getBySel('actions-trigger').click(); cy.getBySel('actions-trigger').click();
} }
function verifyDashboardsSubmenuItem(dashboardName) { function verifyDashboardsSubmenuItem(dashboardName) {
cy.get('.ant-dropdown-menu-submenu-popup').contains(dashboardName); cy.get('.antd5-dropdown-menu-submenu-popup').contains(dashboardName);
closeDashboardsAddedTo(); closeDashboardsAddedTo();
} }
function verifyDashboardSearch() { function verifyDashboardSearch() {
openDashboardsAddedTo(); openDashboardsAddedTo();
cy.get('.ant-dropdown-menu-submenu-popup').trigger('mouseover'); cy.get('.antd5-dropdown-menu-submenu-popup').trigger('mouseover');
cy.get('.ant-dropdown-menu-submenu-popup') cy.get('.antd5-dropdown-menu-submenu-popup')
.find('input[placeholder="Search"]') .find('input[placeholder="Search"]')
.type('1'); .type('1');
cy.get('.ant-dropdown-menu-submenu-popup').contains('1 - Sample dashboard'); cy.get('.antd5-dropdown-menu-submenu-popup').contains('1 - Sample dashboard');
cy.get('.ant-dropdown-menu-submenu-popup') cy.get('.antd5-dropdown-menu-submenu-popup')
.find('input[placeholder="Search"]') .find('input[placeholder="Search"]')
.type('Blahblah'); .type('Blahblah');
cy.get('.ant-dropdown-menu-submenu-popup').contains('No results found'); cy.get('.antd5-dropdown-menu-submenu-popup').contains('No results found');
cy.get('.ant-dropdown-menu-submenu-popup') cy.get('.antd5-dropdown-menu-submenu-popup')
.find('[aria-label="close-circle"]') .find('[aria-label="close-circle"]')
.click(); .click();
closeDashboardsAddedTo(); closeDashboardsAddedTo();
@ -67,8 +68,8 @@ function verifyDashboardSearch() {
function verifyDashboardLink() { function verifyDashboardLink() {
interceptDashboardGet(); interceptDashboardGet();
openDashboardsAddedTo(); openDashboardsAddedTo();
cy.get('.ant-dropdown-menu-submenu-popup').trigger('mouseover'); cy.get('.antd5-dropdown-menu-submenu-popup').trigger('mouseover');
cy.get('.ant-dropdown-menu-submenu-popup a') cy.get('.antd5-dropdown-menu-submenu-popup a')
.first() .first()
.invoke('removeAttr', 'target') .invoke('removeAttr', 'target')
.click(); .click();
@ -124,14 +125,14 @@ describe('Cross-referenced dashboards', () => {
describe('No Results', () => { describe('No Results', () => {
beforeEach(() => { beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('getJson'); interceptV1ChartData();
}); });
it('No results message shows up', () => { it('No results message shows up', () => {
const formData = { const formData = {
...FORM_DATA_DEFAULTS, ...FORM_DATA_DEFAULTS,
metrics: [NUM_METRIC], metrics: [NUM_METRIC],
viz_type: 'line', viz_type: 'echarts_timeseries_line',
adhoc_filters: [ adhoc_filters: [
{ {
expressionType: 'SIMPLE', expressionType: 'SIMPLE',
@ -145,7 +146,7 @@ describe('No Results', () => {
}; };
cy.visitChartByParams(formData); cy.visitChartByParams(formData);
cy.wait('@getJson').its('response.statusCode').should('eq', 200); cy.wait('@v1Data').its('response.statusCode').should('eq', 200);
cy.get('div.chart-container').contains( cy.get('div.chart-container').contains(
'No results were returned for this query', 'No results were returned for this query',
); );

View File

@ -20,13 +20,12 @@
// Tests for setting controls in the UI // Tests for setting controls in the UI
// *********************************************** // ***********************************************
import { interceptChart } from 'cypress/utils'; import { interceptChart } from 'cypress/utils';
import { FORM_DATA_DEFAULTS, NUM_METRIC } from './visualizations/shared.helper';
describe('Datasource control', () => { describe('Datasource control', () => {
const newMetricName = `abc${Date.now()}`; const newMetricName = `abc${Date.now()}`;
it('should allow edit dataset', () => { it('should allow edit dataset', () => {
interceptChart({ legacy: true }).as('chartData'); interceptChart({ legacy: false }).as('chartData');
cy.visitChartByName('Num Births Trend'); cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@chartData' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
@ -52,8 +51,8 @@ describe('Datasource control', () => {
) )
.first() .first()
.focus(); .focus();
cy.focused().clear(); cy.focused().clear({ force: true });
cy.focused().type(`${newMetricName}{enter}`); cy.focused().type(`${newMetricName}{enter}`, { force: true });
cy.get('[data-test="datasource-modal-save"]').click(); cy.get('[data-test="datasource-modal-save"]').click();
cy.get('.antd5-modal-confirm-btns button').contains('OK').click(); cy.get('.antd5-modal-confirm-btns button').contains('OK').click();
@ -85,7 +84,7 @@ describe('Datasource control', () => {
describe('Color scheme control', () => { describe('Color scheme control', () => {
beforeEach(() => { beforeEach(() => {
interceptChart({ legacy: true }).as('chartData'); interceptChart({ legacy: false }).as('chartData');
cy.visitChartByName('Num Births Trend'); cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@chartData' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
@ -137,7 +136,7 @@ describe('VizType control', () => {
describe('Test datatable', () => { describe('Test datatable', () => {
beforeEach(() => { beforeEach(() => {
interceptChart({ legacy: false }).as('tableChartData'); interceptChart({ legacy: false }).as('tableChartData');
interceptChart({ legacy: true }).as('lineChartData'); interceptChart({ legacy: false }).as('lineChartData');
cy.visitChartByName('Daily Totals'); cy.visitChartByName('Daily Totals');
}); });
it('Data Pane opens and loads results', () => { it('Data Pane opens and loads results', () => {
@ -157,107 +156,9 @@ describe('Test datatable', () => {
}); });
}); });
describe('Time range filter', () => {
beforeEach(() => {
interceptChart({ legacy: true }).as('chartData');
});
it('Advanced time_range params', () => {
const formData = {
...FORM_DATA_DEFAULTS,
viz_type: 'line',
time_range: '100 years ago : now',
metrics: [NUM_METRIC],
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=time-range-trigger]').click();
cy.get('.footer').find('button').its('length').should('eq', 2);
cy.get('.ant-popover-content').within(() => {
cy.get('input[value="100 years ago"]');
cy.get('input[value="now"]');
});
cy.get('[data-test=cancel-button]').click();
cy.wait(500);
cy.get('.ant-popover').should('not.exist');
});
it('Common time_range params', () => {
const formData = {
...FORM_DATA_DEFAULTS,
viz_type: 'line',
metrics: [NUM_METRIC],
time_range: 'Last year',
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=time-range-trigger]').click();
cy.get('.ant-radio-group').children().its('length').should('eq', 5);
cy.get('.ant-radio-checked + span').contains('Last year');
cy.get('[data-test=cancel-button]').click();
});
it('Previous time_range params', () => {
const formData = {
...FORM_DATA_DEFAULTS,
viz_type: 'line',
metrics: [NUM_METRIC],
time_range: 'previous calendar month',
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=time-range-trigger]').click();
cy.get('.ant-radio-group').children().its('length').should('eq', 3);
cy.get('.ant-radio-checked + span').contains('previous calendar month');
cy.get('[data-test=cancel-button]').click();
});
it('Custom time_range params', () => {
const formData = {
...FORM_DATA_DEFAULTS,
viz_type: 'line',
metrics: [NUM_METRIC],
time_range: 'DATEADD(DATETIME("today"), -7, day) : today',
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=time-range-trigger]').click();
cy.get('[data-test=custom-frame]').then(() => {
cy.get('.antd5-input-number-input-wrap > input')
.invoke('attr', 'value')
.should('eq', '7');
});
cy.get('[data-test=cancel-button]').click();
});
it('No filter time_range params', () => {
const formData = {
...FORM_DATA_DEFAULTS,
viz_type: 'line',
metrics: [NUM_METRIC],
time_range: 'No filter',
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=time-range-trigger]').click();
cy.get('[data-test=no-filter]').should('exist');
cy.get('[data-test=cancel-button]').click();
});
});
describe('Groupby control', () => { describe('Groupby control', () => {
it('Set groupby', () => { it('Set groupby', () => {
interceptChart({ legacy: true }).as('chartData'); interceptChart({ legacy: false }).as('chartData');
cy.visitChartByName('Num Births Trend'); cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@chartData' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
@ -271,6 +172,6 @@ describe('Groupby control', () => {
cy.get('[data-test="ColumnEdit#save"]').contains('Save').click(); cy.get('[data-test="ColumnEdit#save"]').contains('Save').click();
cy.get('button[data-test="run-query-button"]').click(); cy.get('button[data-test="run-query-button"]').click();
cy.verifySliceSuccess({ waitAlias: '@chartData', chartSelector: 'svg' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
}); });
}); });

View File

@ -30,7 +30,7 @@ const apiURL = (endpoint: string, queryObject: Record<string, unknown>) =>
describe('Test explore links', () => { describe('Test explore links', () => {
beforeEach(() => { beforeEach(() => {
interceptChart({ legacy: true }).as('chartData'); interceptChart({ legacy: false }).as('chartData');
}); });
it('Open and close view query modal', () => { it('Open and close view query modal', () => {
@ -52,11 +52,10 @@ describe('Test explore links', () => {
cy.verifySliceSuccess({ waitAlias: '@chartData' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[aria-label="Menu actions trigger"]').click(); cy.get('[aria-label="Menu actions trigger"]').click();
cy.get('div[title="Share"]').trigger('mouseover'); cy.get('div[role="menuitem"]').within(() => {
// need to use [id= syntax, otherwise error gets triggered because of special character in id cy.contains('Share').parent().click();
cy.get('[id="share_submenu$Menu"]').within(() => {
cy.contains('Embed code').parent().click();
}); });
cy.getBySel('embed-code-button').click();
cy.get('#embed-code-popover').within(() => { cy.get('#embed-code-popover').within(() => {
cy.get('textarea[name=embedCode]').contains('iframe'); cy.get('textarea[name=embedCode]').contains('iframe');
}); });

View File

@ -31,6 +31,10 @@ export function interceptDelete() {
cy.intercept('DELETE', `/api/v1/chart/*`).as('delete'); cy.intercept('DELETE', `/api/v1/chart/*`).as('delete');
} }
export function interceptFavoriteStatus() {
cy.intercept('GET', '/api/v1/chart/favorite_status/*').as('favoriteStatus');
}
export function interceptUpdate() { export function interceptUpdate() {
cy.intercept('PUT', `/api/v1/chart/*`).as('update'); cy.intercept('PUT', `/api/v1/chart/*`).as('update');
} }
@ -68,7 +72,10 @@ export function saveChartToDashboard(dashboardName: string) {
interceptUpdate(); interceptUpdate();
interceptExploreGet(); interceptExploreGet();
cy.getBySel('query-save-button').click(); cy.getBySel('query-save-button')
.should('be.enabled')
.should('not.be.disabled')
.click();
cy.getBySelLike('chart-modal').should('be.visible'); cy.getBySelLike('chart-modal').should('be.visible');
cy.get( cy.get(
'[data-test="save-chart-modal-select-dashboard-form"] [aria-label="Select a dashboard"]', '[data-test="save-chart-modal-select-dashboard-form"] [aria-label="Select a dashboard"]',
@ -77,7 +84,7 @@ export function saveChartToDashboard(dashboardName: string) {
.click(); .click();
cy.get( cy.get(
'.ant-select-selection-search-input[aria-label="Select a dashboard"]', '.ant-select-selection-search-input[aria-label="Select a dashboard"]',
).type(dashboardName.slice(0, 3), { force: true }); ).type(dashboardName, { force: true });
cy.get(`.ant-select-item-option[title="${dashboardName}"]`).click(); cy.get(`.ant-select-item-option[title="${dashboardName}"]`).click();
cy.getBySel('btn-modal-save').click(); cy.getBySel('btn-modal-save').click();

View File

@ -1,120 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
describe('Visualization > Area', () => {
beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
});
const AREA_FORM_DATA = {
datasource: '2__table',
viz_type: 'area',
slice_id: 48,
granularity_sqla: 'year',
time_grain_sqla: 'P1D',
time_range: '1960-01-01 : now',
metrics: ['sum__SP_POP_TOTL'],
adhoc_filters: [],
groupby: [],
limit: '25',
order_desc: true,
contribution: false,
row_limit: 50000,
show_brush: 'auto',
show_legend: true,
line_interpolation: 'linear',
stacked_style: 'stack',
color_scheme: 'bnbColors',
rich_tooltip: true,
show_controls: false,
x_axis_label: '',
bottom_margin: 'auto',
x_ticks_layout: 'auto',
x_axis_format: 'smart_date',
x_axis_showminmax: false,
y_axis_format: '.3s',
y_log_scale: false,
rolling_type: 'None',
comparison_type: 'values',
annotation_layers: [],
};
function verify(formData) {
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
}
it('should work without groupby', () => {
verify(AREA_FORM_DATA);
cy.get('.nv-area').should('have.length', 1);
});
it('should work with group by', () => {
verify({
...AREA_FORM_DATA,
groupby: ['region'],
});
cy.get('.nv-area').should('have.length', 7);
});
it('should work with groupby and filter', () => {
cy.visitChartByParams({
...AREA_FORM_DATA,
groupby: ['region'],
adhoc_filters: [
{
expressionType: 'SIMPLE',
subject: 'region',
operator: 'IN',
comparator: ['South Asia', 'North America'],
clause: 'WHERE',
sqlExpression: null,
filterOptionName: 'filter_txje2ikiv6_wxmn0qwd1xo',
},
],
});
cy.wait('@getJson').then(async ({ response }) => {
const responseBody = response?.body;
// Make sure data is sorted correctly
const firstRow = responseBody.data[0].values;
const secondRow = responseBody.data[1].values;
expect(firstRow[firstRow.length - 1].y).to.be.greaterThan(
secondRow[secondRow.length - 1].y,
);
cy.verifySliceContainer('svg');
});
cy.get('.nv-area').should('have.length', 2);
});
it('should allow type to search color schemes and apply the scheme', () => {
verify(AREA_FORM_DATA);
cy.get('#controlSections-tab-display').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('supersetColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="supersetColors"]',
).should('exist');
cy.get('.area .nv-legend .nv-legend-symbol')
.first()
.should('have.css', 'fill', 'rgb(31, 168, 201)');
});
});

View File

@ -1,90 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { FORM_DATA_DEFAULTS, NUM_METRIC } from './shared.helper';
describe('Visualization > Distribution bar chart', () => {
beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
});
const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'dist_bar' };
const DISTBAR_FORM_DATA = {
...VIZ_DEFAULTS,
metrics: NUM_METRIC,
groupby: ['state'],
};
it('should work with adhoc metric', () => {
cy.visitChartByParams(DISTBAR_FORM_DATA);
cy.verifySliceSuccess({
waitAlias: '@getJson',
querySubstring: NUM_METRIC.label,
chartSelector: 'svg',
});
});
it('should work with series', () => {
const formData = {
...VIZ_DEFAULTS,
metrics: NUM_METRIC,
groupby: ['state'],
columns: ['gender'],
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with row limit', () => {
const formData = {
...VIZ_DEFAULTS,
metrics: NUM_METRIC,
groupby: ['state'],
row_limit: 10,
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with contribution', () => {
const formData = {
...VIZ_DEFAULTS,
metrics: NUM_METRIC,
groupby: ['state'],
columns: ['gender'],
contribution: true,
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should allow type to search color schemes and apply the scheme', () => {
cy.visitChartByParams(DISTBAR_FORM_DATA);
cy.get('#controlSections-tab-display').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('bnbColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="bnbColors"]',
).should('exist');
});
});

View File

@ -18,8 +18,11 @@
*/ */
import { FORM_DATA_DEFAULTS, NUM_METRIC } from './shared.helper'; import { FORM_DATA_DEFAULTS, NUM_METRIC } from './shared.helper';
describe('Download Chart > Distribution bar chart', () => { describe('Download Chart > Bar chart', () => {
const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'dist_bar' }; const VIZ_DEFAULTS = {
...FORM_DATA_DEFAULTS,
viz_type: 'echarts_timeseries_bar',
};
beforeEach(() => { beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('getJson'); cy.intercept('POST', '/superset/explore_json/**').as('getJson');
@ -33,10 +36,10 @@ describe('Download Chart > Distribution bar chart', () => {
}; };
cy.visitChartByParams(formData); cy.visitChartByParams(formData);
cy.get('.header-with-actions .ant-dropdown-trigger').click(); cy.get('.header-with-actions .antd5-dropdown-trigger').click();
cy.get(':nth-child(3) > .ant-dropdown-menu-submenu-title').click(); cy.get(':nth-child(3) > .antd5-dropdown-menu-submenu-title').click();
cy.get( cy.get(
'.ant-dropdown-menu-submenu > .ant-dropdown-menu li:nth-child(3)', '.antd5-dropdown-menu-submenu > .antd5-dropdown-menu li:nth-child(3)',
).click(); ).click();
cy.verifyDownload('.jpg', { cy.verifyDownload('.jpg', {
contains: true, contains: true,

View File

@ -1,102 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { QueryFormData } from '@superset-ui/core';
describe('Visualization > Histogram', () => {
beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
});
const HISTOGRAM_FORM_DATA: QueryFormData = {
datasource: '3__table',
viz_type: 'histogram',
slice_id: 60,
granularity_sqla: 'ds',
time_grain_sqla: 'P1D',
time_range: '100 years ago : now',
all_columns_x: ['num'],
adhoc_filters: [],
row_limit: 50000,
groupby: [],
color_scheme: 'bnbColors',
link_length: 5, // number of bins
x_axis_label: 'Frequency',
y_axis_label: 'Num',
global_opacity: 1,
normalized: false,
};
function verify(formData: QueryFormData) {
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
}
it('should work without groupby', () => {
verify(HISTOGRAM_FORM_DATA);
cy.get('.chart-container svg .vx-bar').should(
'have.length',
HISTOGRAM_FORM_DATA.link_length,
);
});
it('should work with group by', () => {
verify({
...HISTOGRAM_FORM_DATA,
groupby: ['gender'],
});
cy.get('.chart-container svg .vx-bar').should(
'have.length',
HISTOGRAM_FORM_DATA.link_length * 2,
);
});
it('should work with filter and update num bins', () => {
const numBins = 2;
verify({
...HISTOGRAM_FORM_DATA,
link_length: numBins,
adhoc_filters: [
{
expressionType: 'SIMPLE',
clause: 'WHERE',
subject: 'state',
operator: '==',
comparator: 'CA',
},
],
});
cy.get('.chart-container svg .vx-bar').should('have.length', numBins);
});
it('should allow type to search color schemes and apply the scheme', () => {
verify(HISTOGRAM_FORM_DATA);
cy.get('#controlSections-tab-display').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('supersetColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="supersetColors"]',
).should('exist');
cy.get('.histogram .vx-legend .vx-legend-shape div')
.first()
.should('have.css', 'background')
.and('contains', 'rgb(31, 168, 201)');
});
});

View File

@ -1,302 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { FORM_DATA_DEFAULTS, NUM_METRIC, SIMPLE_FILTER } from './shared.helper';
describe('Visualization > Line', () => {
beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
});
const LINE_CHART_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'line' };
it('should show validator error when no metric', () => {
const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
cy.visitChartByParams(formData);
cy.get('.panel-body').contains(
`Add required control values to preview chart`,
);
});
it('should not show validator error when metric added', () => {
const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
cy.visitChartByParams(formData);
cy.get('.panel-body').contains(
`Add required control values to preview chart`,
);
cy.get('[data-test="metrics-header"]').contains('Metrics');
cy.get('[data-test="metrics-header"] [data-test="error-tooltip"]').should(
'exist',
);
cy.get('[data-test=metrics]')
.contains('Drop columns/metrics here or click')
.click();
// Title edit for saved metrics is disabled - switch to Simple
cy.get('[id="adhoc-metric-edit-tabs-tab-SIMPLE"]').click();
cy.get('input[aria-label="Select column"]').click();
cy.get('input[aria-label="Select column"]').type('num{enter}');
cy.get('input[aria-label="Select aggregate options"]').click();
cy.get('input[aria-label="Select aggregate options"]').type('sum{enter}');
cy.get('[data-test="AdhocMetricEdit#save"]').contains('Save').click();
cy.get('[data-test="metrics-header"]').contains('Metrics');
cy.get('[data-test="metrics-header"] [data-test="error-tooltip"]').should(
'not.exist',
);
cy.get('.antd5-alert-warning').should('not.exist');
});
it('should allow negative values in Y bounds', () => {
const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
cy.visitChartByParams(formData);
cy.get('#controlSections-tab-display').click();
cy.get('span').contains('Y Axis Bounds').scrollIntoView();
cy.get('input[placeholder="Min"]').type('-0.1', { delay: 100 });
cy.get('.antd5-alert-warning').should('not.exist');
});
it('should allow type to search color schemes and apply the scheme', () => {
cy.get('#controlSections-tab-display').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('bnbColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="bnbColors"]',
).should('exist');
cy.get('.line .nv-legend .nv-legend-symbol')
.first()
.should('have.css', 'fill', 'rgb(41, 105, 107)');
});
it('should work with adhoc metric', () => {
const formData = { ...LINE_CHART_DEFAULTS, metrics: [NUM_METRIC] };
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with groupby', () => {
const metrics = ['count'];
const groupby = ['gender'];
const formData = { ...LINE_CHART_DEFAULTS, metrics, groupby };
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with simple filter', () => {
const metrics = ['count'];
const filters = [SIMPLE_FILTER];
const formData = {
...LINE_CHART_DEFAULTS,
metrics,
adhoc_filters: filters,
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with series limit sort asc', () => {
const formData = {
...LINE_CHART_DEFAULTS,
metrics: [NUM_METRIC],
limit: 10,
groupby: ['name'],
timeseries_limit_metric: NUM_METRIC,
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with series limit sort desc', () => {
const formData = {
...LINE_CHART_DEFAULTS,
metrics: [NUM_METRIC],
limit: 10,
groupby: ['name'],
timeseries_limit_metric: NUM_METRIC,
order_desc: true,
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with rolling avg', () => {
const metrics = [NUM_METRIC];
const formData = {
...LINE_CHART_DEFAULTS,
metrics,
rolling_type: 'mean',
rolling_periods: 10,
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with time shift 1 year', () => {
const metrics = [NUM_METRIC];
const formData = {
...LINE_CHART_DEFAULTS,
metrics,
time_compare: ['1 year'],
comparison_type: 'values',
groupby: ['gender'],
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
// Offset color should match original line color
cy.get('.nv-legend-text')
.contains('boy')
.siblings()
.first()
.should('have.attr', 'style')
.then(style => {
cy.get('.nv-legend-text')
.contains('boy, 1 year offset')
.siblings()
.first()
.should('have.attr', 'style')
.and('eq', style);
});
cy.get('.nv-legend-text')
.contains('girl')
.siblings()
.first()
.should('have.attr', 'style')
.then(style => {
cy.get('.nv-legend-text')
.contains('girl, 1 year offset')
.siblings()
.first()
.should('have.attr', 'style')
.and('eq', style);
});
});
it('should work with time shift yoy', () => {
const metrics = [NUM_METRIC];
const formData = {
...LINE_CHART_DEFAULTS,
metrics,
time_compare: ['1 year'],
comparison_type: 'ratio',
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('should work with time shift percentage change', () => {
const metrics = [NUM_METRIC];
const formData = {
...LINE_CHART_DEFAULTS,
metrics,
time_compare: ['1 year'],
comparison_type: 'percentage',
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
});
it('Test verbose name shows up in legend', () => {
const formData = {
...LINE_CHART_DEFAULTS,
metrics: ['count'],
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
cy.get('text.nv-legend-text').contains('COUNT(*)');
});
it('Test hidden annotation', () => {
const formData = {
...LINE_CHART_DEFAULTS,
metrics: ['count'],
annotation_layers: [
{
name: 'Goal line',
annotationType: 'FORMULA',
sourceType: '',
value: 'y=140000',
overrides: { time_range: null },
show: false,
showLabel: false,
titleColumn: '',
descriptionColumns: [],
timeColumn: '',
intervalEndColumn: '',
color: null,
opacity: '',
style: 'solid',
width: 1,
showMarkers: false,
hideLine: false,
},
],
};
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
cy.get('.slice_container').within(() => {
// Goal line annotation doesn't show up in legend
cy.get('.nv-legend-text').should('have.length', 1);
});
});
it('Test event annotation time override', () => {
cy.request('/chart/api/read?_flt_3_slice_name=Daily+Totals').then(
response => {
const value = response.body.pks[0];
const formData = {
...LINE_CHART_DEFAULTS,
metrics: ['count'],
annotation_layers: [
{
name: 'Yearly date',
annotationType: 'EVENT',
sourceType: 'table',
value,
overrides: { time_range: null },
show: true,
showLabel: false,
titleColumn: 'ds',
descriptionColumns: ['ds'],
timeColumn: 'ds',
color: null,
opacity: '',
style: 'solid',
width: 1,
showMarkers: false,
hideLine: false,
},
],
};
cy.visitChartByParams(formData);
},
);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
cy.get('.slice_container').within(() => {
cy.get('.nv-event-annotation-layer-0')
.children()
.should('have.length', 44);
});
});
});

View File

@ -1,87 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
describe('Visualization > Sankey', () => {
beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
});
const SANKEY_FORM_DATA = {
datasource: '1__table',
viz_type: 'sankey',
slice_id: 1,
url_params: {},
granularity_sqla: null,
time_grain_sqla: 'P1D',
time_range: 'Last week',
groupby: ['source', 'target'],
metric: 'sum__value',
adhoc_filters: [],
row_limit: '5000',
color_scheme: 'bnbColors',
};
function verify(formData) {
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
}
it('should work', () => {
verify(SANKEY_FORM_DATA);
cy.get('.chart-container svg g.node rect').should('have.length', 41);
});
it('should work with filter', () => {
verify({
...SANKEY_FORM_DATA,
adhoc_filters: [
{
expressionType: 'SQL',
sqlExpression: 'SUM(value) > 0',
clause: 'HAVING',
subject: null,
operator: null,
comparator: null,
filterOptionName: 'filter_jbdwe0hayaj_h9jfer8fy58',
},
{
expressionType: 'SIMPLE',
subject: 'source',
operator: '==',
comparator: 'Energy',
clause: 'WHERE',
sqlExpression: null,
filterOptionName: 'filter_8e0otka9uif_vmqri4gmbqc',
},
],
});
cy.get('.chart-container svg g.node rect').should('have.length', 6);
});
it('should allow type to search color schemes', () => {
verify(SANKEY_FORM_DATA);
cy.get('#controlSections-tab-display').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('bnbColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="bnbColors"]',
).should('exist');
});
});

View File

@ -22,15 +22,22 @@
export const FORM_DATA_DEFAULTS = { export const FORM_DATA_DEFAULTS = {
datasource: '3__table', datasource: '3__table',
granularity_sqla: 'ds',
time_grain_sqla: null, time_grain_sqla: null,
time_range: '100 years ago : now', x_axis: 'ds',
adhoc_filters: [], adhoc_filters: [
{
clause: 'WHERE',
subject: 'ds',
operator: 'TEMPORAL_RANGE',
comparator: '100 years ago : now',
expressionType: 'SIMPLE',
},
],
groupby: [], groupby: [],
limit: null, limit: null,
timeseries_limit_metric: null, timeseries_limit_metric: null,
order_desc: false, order_desc: false,
contribution: false, contributionMode: null,
}; };
export const HEALTH_POP_FORM_DATA_DEFAULTS = { export const HEALTH_POP_FORM_DATA_DEFAULTS = {

View File

@ -80,7 +80,6 @@ describe('SqlLab query panel', () => {
it.skip('successfully saves a query', () => { it.skip('successfully saves a query', () => {
cy.intercept('api/v1/database/**/tables/**').as('getTables'); cy.intercept('api/v1/database/**/tables/**').as('getTables');
cy.intercept('savedqueryviewapi/**').as('getSavedQuery');
const query = const query =
'SELECT ds, gender, name, num FROM main.birth_names ORDER BY name LIMIT 3'; 'SELECT ds, gender, name, num FROM main.birth_names ORDER BY name LIMIT 3';
@ -142,10 +141,11 @@ describe('SqlLab query panel', () => {
}); });
}); });
it('Create a chart from a query', () => { it.skip('Create a chart from a query', () => {
cy.intercept('/api/v1/sqllab/execute/').as('queryFinished'); cy.intercept('/api/v1/sqllab/execute/').as('queryFinished');
cy.intercept('**/api/v1/explore/**').as('explore'); cy.intercept('**/api/v1/explore/**').as('explore');
cy.intercept('**/api/v1/chart/**').as('chart'); cy.intercept('**/api/v1/chart/**').as('chart');
cy.intercept('**/tabstateview/**').as('tabstateview');
// cypress doesn't handle opening a new tab, override window.open to open in the same tab // cypress doesn't handle opening a new tab, override window.open to open in the same tab
cy.window().then(win => { cy.window().then(win => {
@ -154,6 +154,7 @@ describe('SqlLab query panel', () => {
win.location.href = url; win.location.href = url;
}); });
}); });
cy.wait('@tabstateview');
const query = 'SELECT gender, name FROM birth_names'; const query = 'SELECT gender, name FROM birth_names';

View File

@ -80,9 +80,9 @@ describe('SqlLab query tabs', () => {
// configure some editor settings // configure some editor settings
cy.get(editorInput).type('some random query string', { force: true }); cy.get(editorInput).type('some random query string', { force: true });
cy.get(queryLimitSelector).parent().click({ force: true }); cy.get(queryLimitSelector).parent().click({ force: true });
cy.get('.ant-dropdown-menu') cy.get('.antd5-dropdown-menu')
.last() .last()
.find('.ant-dropdown-menu-item') .find('.antd5-dropdown-menu-item')
.first() .first()
.click({ force: true }); .click({ force: true });

View File

@ -3,40 +3,40 @@
"slice_name": "1 - Sample chart", "slice_name": "1 - Sample chart",
"description": "chart description", "description": "chart description",
"owners": [1], "owners": [1],
"viz_type": "line", "viz_type": "echarts_timeseries_line",
"cache_timeout": 1000, "cache_timeout": 1000,
"datasource_id": 2, "datasource_id": 2,
"datasource_type": "table", "datasource_type": "table",
"params": "{\"viz_type\":\"line\",\"metrics\":[\"count\"]}" "params": "{\"viz_type\":\"echarts_timeseries_line\",\"x_axis\":\"year\",\"metrics\":[\"count\"]}"
}, },
{ {
"slice_name": "2 - Sample chart", "slice_name": "2 - Sample chart",
"description": "chart description", "description": "chart description",
"owners": [1], "owners": [1],
"viz_type": "line", "viz_type": "echarts_timeseries_line",
"cache_timeout": 1000, "cache_timeout": 1000,
"datasource_id": 2, "datasource_id": 2,
"datasource_type": "table", "datasource_type": "table",
"params": "{\"viz_type\":\"line\",\"metrics\":[\"count\"]}" "params": "{\"viz_type\":\"echarts_timeseries_line\",\"x_axis\":\"year\",\"metrics\":[\"count\"]}"
}, },
{ {
"slice_name": "3 - Sample chart", "slice_name": "3 - Sample chart",
"description": "chart description", "description": "chart description",
"owners": [1], "owners": [1],
"viz_type": "line", "viz_type": "echarts_timeseries_line",
"cache_timeout": 1000, "cache_timeout": 1000,
"datasource_id": 2, "datasource_id": 2,
"datasource_type": "table", "datasource_type": "table",
"params": "{\"viz_type\":\"line\",\"metrics\":[\"count\"]}" "params": "{\"viz_type\":\"echarts_timeseries_line\",\"x_axis\":\"year\",\"metrics\":[\"count\"]}"
}, },
{ {
"slice_name": "4 - Sample chart", "slice_name": "4 - Sample chart",
"description": "chart description", "description": "chart description",
"owners": [1], "owners": [1],
"viz_type": "line", "viz_type": "echarts_timeseries_line",
"cache_timeout": 1000, "cache_timeout": 1000,
"datasource_id": 2, "datasource_id": 2,
"datasource_type": "table", "datasource_type": "table",
"params": "{\"viz_type\":\"line\",\"metrics\":[\"count\"]}" "params": "{\"viz_type\":\"echarts_timeseries_line\",\"x_axis\":\"year\",\"metrics\":[\"count\"]}"
} }
] ]

View File

@ -25,16 +25,16 @@ export function dataTestChartName(chartName: string): string {
export const pageHeader = { export const pageHeader = {
logo: '.navbar-brand > img', logo: '.navbar-brand > img',
headerNavigationItem: '.ant-menu-submenu-title', headerNavigationItem: '.antd5-menu-submenu-title',
headerNavigationDropdown: "[aria-label='triangle-down']", headerNavigationDropdown: "[aria-label='triangle-down']",
headerNavigationItemMenu: '.ant-menu-item-group-list', headerNavigationItemMenu: '.antd5-menu-item-group-list',
plusIcon: ':nth-child(2) > .ant-menu-submenu-title', plusIcon: ':nth-child(2) > .antd5-menu-submenu-title',
plusIconMenuOptions: { plusIconMenuOptions: {
sqlQueryOption: dataTestLocator('menu-item-SQL query'), sqlQueryOption: dataTestLocator('menu-item-SQL query'),
chartOption: dataTestLocator('menu-item-Chart'), chartOption: dataTestLocator('menu-item-Chart'),
dashboardOption: dataTestLocator('menu-item-Dashboard'), dashboardOption: dataTestLocator('menu-item-Dashboard'),
}, },
plusMenu: '.ant-menu-submenu-popup', plusMenu: '.antd5-menu-submenu-popup',
barButtons: '[role="presentation"]', barButtons: '[role="presentation"]',
sqlLabMenu: '[id="item_3$Menu"]', sqlLabMenu: '[id="item_3$Menu"]',
dataMenu: '[id="item_4$Menu"]', dataMenu: '[id="item_4$Menu"]',
@ -106,7 +106,7 @@ export const databasesPage = {
alertMessage: '.antd5-alert-message', alertMessage: '.antd5-alert-message',
errorField: '[role="alert"]', errorField: '[role="alert"]',
uploadJson: '[title="Upload JSON file"]', uploadJson: '[title="Upload JSON file"]',
chooseFile: '[class="ant-btn input-upload-btn"]', chooseFile: '[class="antd5-btn input-upload-btn"]',
additionalParameters: '[name="query_input"]', additionalParameters: '[name="query_input"]',
sqlAlchemyUriInput: dataTestLocator('sqlalchemy-uri-input'), sqlAlchemyUriInput: dataTestLocator('sqlalchemy-uri-input'),
advancedTab: '#rc-tabs-0-tab-2', advancedTab: '#rc-tabs-0-tab-2',
@ -148,7 +148,7 @@ export const sqlLabView = {
examplesMenuItem: '[title="examples"]', examplesMenuItem: '[title="examples"]',
tableInput: ':nth-child(4) > .select > :nth-child(1)', tableInput: ':nth-child(4) > .select > :nth-child(1)',
sqlEditor: '#brace-editor textarea', sqlEditor: '#brace-editor textarea',
saveAsButton: '.SaveQuery > .ant-btn', saveAsButton: '.SaveQuery > .antd5-btn',
saveAsModal: { saveAsModal: {
footer: '.antd5-modal-footer', footer: '.antd5-modal-footer',
queryNameInput: 'input[class^="ant-input"]', queryNameInput: 'input[class^="ant-input"]',
@ -158,10 +158,10 @@ export const sqlLabView = {
runButton: '.css-d3dxop', runButton: '.css-d3dxop',
}, },
rowsLimit: { rowsLimit: {
dropdown: '.ant-dropdown-menu', dropdown: '.antd5-dropdown-menu',
limitButton: '.ant-dropdown-menu-item', limitButton: '.antd5-dropdown-menu-item',
limitButtonText: '.css-151uxnz', limitButtonText: '.css-151uxnz',
limitTextWithValue: '[class="ant-dropdown-trigger"]', limitTextWithValue: '[class="antd5-dropdown-trigger"]',
}, },
renderedTableHeader: '.ReactVirtualized__Table__headerRow', renderedTableHeader: '.ReactVirtualized__Table__headerRow',
renderedTableRow: '.ReactVirtualized__Table__row', renderedTableRow: '.ReactVirtualized__Table__row',
@ -195,7 +195,7 @@ export const savedQuery = {
export const annotationLayersView = { export const annotationLayersView = {
emptyDescription: { emptyDescription: {
description: '.ant-empty-description', description: '.ant-empty-description',
addAnnotationLayerButton: '.ant-empty-footer > .ant-btn', addAnnotationLayerButton: '.ant-empty-footer > .antd5-btn',
}, },
modal: { modal: {
content: { content: {
@ -434,7 +434,7 @@ export const dashboardListView = {
newDashboardButton: '.css-yff34v', newDashboardButton: '.css-yff34v',
}, },
importModal: { importModal: {
selectFileButton: '.ant-upload > .ant-btn > span', selectFileButton: '.ant-upload > .antd5-btn > span',
importButton: dataTestLocator('modal-confirm-button'), importButton: dataTestLocator('modal-confirm-button'),
}, },
header: { header: {
@ -588,7 +588,7 @@ export const exploreView = {
rowsContainer: dataTestLocator('table-content-rows'), rowsContainer: dataTestLocator('table-content-rows'),
}, },
confirmModal: { confirmModal: {
okButton: '.antd5-modal-confirm-btns .ant-btn-primary', okButton: '.antd5-modal-confirm-btns .antd5-btn-primary',
}, },
}, },
visualizationTypeModal: { visualizationTypeModal: {
@ -633,7 +633,7 @@ export const dashboardView = {
refreshChart: dataTestLocator('refresh-chart-menu-item'), refreshChart: dataTestLocator('refresh-chart-menu-item'),
}, },
threeDotsMenuIcon: threeDotsMenuIcon:
'.header-with-actions .right-button-panel .ant-dropdown-trigger', '.header-with-actions .right-button-panel .antd5-dropdown-trigger',
threeDotsMenuDropdown: dataTestLocator('header-actions-menu'), threeDotsMenuDropdown: dataTestLocator('header-actions-menu'),
refreshDashboard: dataTestLocator('refresh-dashboard-menu-item'), refreshDashboard: dataTestLocator('refresh-dashboard-menu-item'),
saveAsMenuOption: dataTestLocator('save-as-menu-item'), saveAsMenuOption: dataTestLocator('save-as-menu-item'),

View File

@ -19,6 +19,8 @@
import '@cypress/code-coverage/support'; import '@cypress/code-coverage/support';
import '@applitools/eyes-cypress/commands'; import '@applitools/eyes-cypress/commands';
import failOnConsoleError from 'cypress-fail-on-console-error'; import failOnConsoleError from 'cypress-fail-on-console-error';
import { expect } from 'chai';
import rison from 'rison';
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
@ -26,6 +28,10 @@ require('cy-verify-downloads').addCustomCommand();
// fail on console error, allow config to override individual tests // fail on console error, allow config to override individual tests
// these exceptions are a little pile of tech debt // these exceptions are a little pile of tech debt
//
// DISABLING FOR NOW
/*
const { getConfig, setConfig } = failOnConsoleError({ const { getConfig, setConfig } = failOnConsoleError({
consoleMessages: [ consoleMessages: [
/\[webpack-dev-server\]/, /\[webpack-dev-server\]/,
@ -34,7 +40,9 @@ const { getConfig, setConfig } = failOnConsoleError({
'Error: Unknown Error', 'Error: Unknown Error',
/Unable to infer path to ace from script src/, /Unable to infer path to ace from script src/,
], ],
includeConsoleTypes: ['error'],
}); });
*/
// Set individual tests to allow certain console errors to NOT fail, e.g // Set individual tests to allow certain console errors to NOT fail, e.g
// cy.allowConsoleErrors(['foo', /^some bar-regex.*/]); // cy.allowConsoleErrors(['foo', /^some bar-regex.*/]);
@ -160,13 +168,28 @@ Cypress.Commands.add('login', () => {
url: '/login/', url: '/login/',
body: { username: 'admin', password: 'general' }, body: { username: 'admin', password: 'general' },
}).then(response => { }).then(response => {
expect(response.status).to.eq(200); if (response.status === 302) {
// If there's a redirect, follow it manually
const redirectUrl = response.headers.location;
cy.request({
method: 'GET',
url: redirectUrl,
}).then(finalResponse => {
expect(finalResponse.status).to.eq(200);
});
} else {
expect(response.status).to.eq(200);
}
}); });
}); });
Cypress.Commands.add('visitChartByName', name => { Cypress.Commands.add('visitChartByName', name => {
cy.request(`/chart/api/read?_flt_3_slice_name=${name}`).then(response => { const query = rison.encode({
cy.visit(`${BASE_EXPLORE_URL}{"slice_id": ${response.body.pks[0]}}`); columns: ['id'],
filters: [{ col: 'slice_name', opr: 'eq', value: name }],
});
cy.request(`/api/v1/chart?q=${query}`).then(response => {
cy.visit(`${BASE_EXPLORE_URL}{"slice_id": ${response.body.result[0].id}}`);
}); });
}); });

View File

@ -13,6 +13,7 @@
"@cypress/code-coverage": "^3.10.4", "@cypress/code-coverage": "^3.10.4",
"@superset-ui/core": "^2.1.0", "@superset-ui/core": "^2.1.0",
"brace": "^0.11.1", "brace": "^0.11.1",
"chai": "^4.5.0",
"cy-verify-downloads": "^0.2.5", "cy-verify-downloads": "^0.2.5",
"cypress-fail-on-console-error": "^4.0.3", "cypress-fail-on-console-error": "^4.0.3",
"nanoid": "^5.0.9", "nanoid": "^5.0.9",
@ -4317,6 +4318,7 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
"license": "MIT",
"engines": { "engines": {
"node": "*" "node": "*"
} }
@ -4790,22 +4792,32 @@
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
}, },
"node_modules/chai": { "node_modules/chai": {
"version": "4.3.7", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz",
"integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==",
"license": "MIT",
"dependencies": { "dependencies": {
"assertion-error": "^1.1.0", "assertion-error": "^1.1.0",
"check-error": "^1.0.2", "check-error": "^1.0.3",
"deep-eql": "^4.1.2", "deep-eql": "^4.1.3",
"get-func-name": "^2.0.0", "get-func-name": "^2.0.2",
"loupe": "^2.3.1", "loupe": "^2.3.6",
"pathval": "^1.1.1", "pathval": "^1.1.1",
"type-detect": "^4.0.5" "type-detect": "^4.1.0"
}, },
"engines": { "engines": {
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/chai/node_modules/type-detect": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz",
"integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/chainsaw": { "node_modules/chainsaw": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
@ -4851,9 +4863,13 @@
} }
}, },
"node_modules/check-error": { "node_modules/check-error": {
"version": "1.0.2", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
"integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
"license": "MIT",
"dependencies": {
"get-func-name": "^2.0.2"
},
"engines": { "engines": {
"node": "*" "node": "*"
} }
@ -5401,9 +5417,10 @@
} }
}, },
"node_modules/deep-eql": { "node_modules/deep-eql": {
"version": "4.1.3", "version": "4.1.4",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz",
"integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==",
"license": "MIT",
"dependencies": { "dependencies": {
"type-detect": "^4.0.0" "type-detect": "^4.0.0"
}, },
@ -6505,6 +6522,7 @@
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
"integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
"license": "MIT",
"engines": { "engines": {
"node": "*" "node": "*"
} }
@ -7739,11 +7757,12 @@
} }
}, },
"node_modules/loupe": { "node_modules/loupe": {
"version": "2.3.6", "version": "2.3.7",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
"integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
"license": "MIT",
"dependencies": { "dependencies": {
"get-func-name": "^2.0.0" "get-func-name": "^2.0.1"
} }
}, },
"node_modules/lowercase-keys": { "node_modules/lowercase-keys": {
@ -8803,6 +8822,7 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
"integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
"license": "MIT",
"engines": { "engines": {
"node": "*" "node": "*"
} }
@ -9715,6 +9735,7 @@
"version": "3.7.0", "version": "3.7.0",
"resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz",
"integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==",
"license": "(BSD-2-Clause OR WTFPL)",
"peerDependencies": { "peerDependencies": {
"chai": "^4.0.0", "chai": "^4.0.0",
"sinon": ">=4.0.0" "sinon": ">=4.0.0"
@ -14560,17 +14581,24 @@
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
}, },
"chai": { "chai": {
"version": "4.3.7", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz",
"integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==",
"requires": { "requires": {
"assertion-error": "^1.1.0", "assertion-error": "^1.1.0",
"check-error": "^1.0.2", "check-error": "^1.0.3",
"deep-eql": "^4.1.2", "deep-eql": "^4.1.3",
"get-func-name": "^2.0.0", "get-func-name": "^2.0.2",
"loupe": "^2.3.1", "loupe": "^2.3.6",
"pathval": "^1.1.1", "pathval": "^1.1.1",
"type-detect": "^4.0.5" "type-detect": "^4.1.0"
},
"dependencies": {
"type-detect": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz",
"integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw=="
}
} }
}, },
"chainsaw": { "chainsaw": {
@ -14607,9 +14635,12 @@
"integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="
}, },
"check-error": { "check-error": {
"version": "1.0.2", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
"integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==" "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
"requires": {
"get-func-name": "^2.0.2"
}
}, },
"check-more-types": { "check-more-types": {
"version": "2.24.0", "version": "2.24.0",
@ -15026,9 +15057,9 @@
} }
}, },
"deep-eql": { "deep-eql": {
"version": "4.1.3", "version": "4.1.4",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz",
"integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==",
"requires": { "requires": {
"type-detect": "^4.0.0" "type-detect": "^4.0.0"
} }
@ -16786,11 +16817,11 @@
} }
}, },
"loupe": { "loupe": {
"version": "2.3.6", "version": "2.3.7",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
"integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
"requires": { "requires": {
"get-func-name": "^2.0.0" "get-func-name": "^2.0.1"
} }
}, },
"lowercase-keys": { "lowercase-keys": {

View File

@ -20,12 +20,13 @@
"@cypress/code-coverage": "^3.10.4", "@cypress/code-coverage": "^3.10.4",
"@superset-ui/core": "^2.1.0", "@superset-ui/core": "^2.1.0",
"brace": "^0.11.1", "brace": "^0.11.1",
"chai": "^4.5.0",
"cy-verify-downloads": "^0.2.5", "cy-verify-downloads": "^0.2.5",
"cypress-fail-on-console-error": "^4.0.3", "cypress-fail-on-console-error": "^4.0.3",
"nanoid": "^5.0.9",
"querystringify": "^2.2.0", "querystringify": "^2.2.0",
"react-dom": "^16.13.0", "react-dom": "^16.13.0",
"rison": "^0.1.1", "rison": "^0.1.1"
"nanoid": "^5.0.9"
}, },
"devDependencies": { "devDependencies": {
"@types/querystringify": "^2.0.0", "@types/querystringify": "^2.0.0",

View File

@ -35,6 +35,7 @@ module.exports = {
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
modulePathIgnorePatterns: ['<rootDir>/packages/generator-superset'], modulePathIgnorePatterns: ['<rootDir>/packages/generator-superset'],
setupFilesAfterEnv: ['<rootDir>/spec/helpers/setup.ts'], setupFilesAfterEnv: ['<rootDir>/spec/helpers/setup.ts'],
snapshotSerializers: ['@emotion/jest/serializer'],
testEnvironmentOptions: { testEnvironmentOptions: {
url: 'http://localhost', url: 'http://localhost',
}, },
@ -53,11 +54,14 @@ module.exports = {
'dist/', 'dist/',
], ],
coverageReporters: ['lcov', 'json-summary', 'html', 'text'], coverageReporters: ['lcov', 'json-summary', 'html', 'text'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
snapshotSerializers: ['@emotion/jest/enzyme-serializer'],
transformIgnorePatterns: [ transformIgnorePatterns: [
'node_modules/(?!d3-(interpolate|color|time)|remark-gfm|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|@rjsf/*.|sinon|echarts|zrender|fetch-mock|pretty-ms|parse-ms)', 'node_modules/(?!d3-(interpolate|color|time)|remark-gfm|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|@rjsf/*.|sinon|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|jest-enzyme)',
], ],
preset: 'ts-jest',
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
globals: { globals: {
__DEV__: true, __DEV__: true,
caches: true, caches: true,
@ -71,4 +75,5 @@ module.exports = {
}, },
], ],
], ],
testTimeout: 10000,
}; };

File diff suppressed because it is too large Load Diff

View File

@ -97,20 +97,16 @@
"@superset-ui/legacy-plugin-chart-calendar": "file:./plugins/legacy-plugin-chart-calendar", "@superset-ui/legacy-plugin-chart-calendar": "file:./plugins/legacy-plugin-chart-calendar",
"@superset-ui/legacy-plugin-chart-chord": "file:./plugins/legacy-plugin-chart-chord", "@superset-ui/legacy-plugin-chart-chord": "file:./plugins/legacy-plugin-chart-chord",
"@superset-ui/legacy-plugin-chart-country-map": "file:./plugins/legacy-plugin-chart-country-map", "@superset-ui/legacy-plugin-chart-country-map": "file:./plugins/legacy-plugin-chart-country-map",
"@superset-ui/legacy-plugin-chart-event-flow": "file:./plugins/legacy-plugin-chart-event-flow",
"@superset-ui/legacy-plugin-chart-heatmap": "file:./plugins/legacy-plugin-chart-heatmap",
"@superset-ui/legacy-plugin-chart-histogram": "file:./plugins/legacy-plugin-chart-histogram",
"@superset-ui/legacy-plugin-chart-horizon": "file:./plugins/legacy-plugin-chart-horizon", "@superset-ui/legacy-plugin-chart-horizon": "file:./plugins/legacy-plugin-chart-horizon",
"@superset-ui/legacy-plugin-chart-map-box": "file:./plugins/legacy-plugin-chart-map-box", "@superset-ui/legacy-plugin-chart-map-box": "file:./plugins/legacy-plugin-chart-map-box",
"@superset-ui/legacy-plugin-chart-paired-t-test": "file:./plugins/legacy-plugin-chart-paired-t-test", "@superset-ui/legacy-plugin-chart-paired-t-test": "file:./plugins/legacy-plugin-chart-paired-t-test",
"@superset-ui/legacy-plugin-chart-parallel-coordinates": "file:./plugins/legacy-plugin-chart-parallel-coordinates", "@superset-ui/legacy-plugin-chart-parallel-coordinates": "file:./plugins/legacy-plugin-chart-parallel-coordinates",
"@superset-ui/legacy-plugin-chart-partition": "file:./plugins/legacy-plugin-chart-partition", "@superset-ui/legacy-plugin-chart-partition": "file:./plugins/legacy-plugin-chart-partition",
"@superset-ui/legacy-plugin-chart-rose": "file:./plugins/legacy-plugin-chart-rose", "@superset-ui/legacy-plugin-chart-rose": "file:./plugins/legacy-plugin-chart-rose",
"@superset-ui/legacy-plugin-chart-sankey": "file:./plugins/legacy-plugin-chart-sankey",
"@superset-ui/legacy-plugin-chart-sankey-loop": "file:./plugins/legacy-plugin-chart-sankey-loop",
"@superset-ui/legacy-plugin-chart-world-map": "file:./plugins/legacy-plugin-chart-world-map", "@superset-ui/legacy-plugin-chart-world-map": "file:./plugins/legacy-plugin-chart-world-map",
"@superset-ui/legacy-preset-chart-deckgl": "file:./plugins/legacy-preset-chart-deckgl", "@superset-ui/legacy-preset-chart-deckgl": "file:./plugins/legacy-preset-chart-deckgl",
"@superset-ui/legacy-preset-chart-nvd3": "file:./plugins/legacy-preset-chart-nvd3", "@superset-ui/legacy-preset-chart-nvd3": "file:./plugins/legacy-preset-chart-nvd3",
"@superset-ui/plugin-chart-cartodiagram": "file:./plugins/plugin-chart-cartodiagram",
"@superset-ui/plugin-chart-echarts": "file:./plugins/plugin-chart-echarts", "@superset-ui/plugin-chart-echarts": "file:./plugins/plugin-chart-echarts",
"@superset-ui/plugin-chart-handlebars": "file:./plugins/plugin-chart-handlebars", "@superset-ui/plugin-chart-handlebars": "file:./plugins/plugin-chart-handlebars",
"@superset-ui/plugin-chart-pivot-table": "file:./plugins/plugin-chart-pivot-table", "@superset-ui/plugin-chart-pivot-table": "file:./plugins/plugin-chart-pivot-table",
@ -125,27 +121,34 @@
"@visx/scale": "^3.5.0", "@visx/scale": "^3.5.0",
"@visx/tooltip": "^3.0.0", "@visx/tooltip": "^3.0.0",
"@visx/xychart": "^3.5.1", "@visx/xychart": "^3.5.1",
"abortcontroller-polyfill": "^1.7.5", "abortcontroller-polyfill": "^1.7.8",
"ace-builds": "^1.36.3", "ace-builds": "^1.36.3",
"ag-grid-community": "32.2.1",
"ag-grid-react": "32.2.1",
"antd": "4.10.3", "antd": "4.10.3",
"antd-v5": "npm:antd@^5.18.0", "antd-v5": "npm:antd@^5.18.0",
"babel-plugin-typescript-to-proptypes": "^2.0.0",
"bootstrap": "^3.4.1", "bootstrap": "^3.4.1",
"brace": "^0.11.1", "brace": "^0.11.1",
"chrono-node": "^2.7.6", "chrono-node": "^2.7.6",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"core-js": "^3.38.1", "core-js": "^3.38.1",
"d3-color": "^3.1.0",
"d3-scale": "^2.1.2", "d3-scale": "^2.1.2",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"dom-to-image-more": "^3.2.0", "dom-to-image-more": "^3.2.0",
"dom-to-pdf": "^0.3.2", "dom-to-pdf": "^0.3.2",
"echarts": "^5.6.0",
"emotion-rgba": "0.0.12", "emotion-rgba": "0.0.12",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"fs-extra": "^11.2.0", "fs-extra": "^11.2.0",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"geolib": "^2.0.24", "geolib": "^2.0.24",
"geostyler": "^12.0.2",
"geostyler-data": "^1.0.0",
"geostyler-openlayers-parser": "^4.3.0",
"geostyler-style": "^7.5.0",
"geostyler-wfs-parser": "^2.0.3",
"googleapis": "^130.0.0", "googleapis": "^130.0.0",
"html-webpack-plugin": "^5.3.2",
"immer": "^10.1.1", "immer": "^10.1.1",
"interweave": "^13.1.0", "interweave": "^13.1.0",
"jquery": "^3.7.1", "jquery": "^3.7.1",
@ -156,27 +159,26 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"luxon": "^3.5.0", "luxon": "^3.5.0",
"mapbox-gl": "^2.10.0", "mapbox-gl": "^2.10.0",
"markdown-to-jsx": "^7.4.7", "markdown-to-jsx": "^7.7.2",
"match-sorter": "^6.3.4", "match-sorter": "^6.3.4",
"memoize-one": "^5.2.1", "memoize-one": "^5.2.1",
"moment": "^2.30.1",
"moment-timezone": "^0.5.44",
"mousetrap": "^1.6.5", "mousetrap": "^1.6.5",
"mustache": "^4.2.0", "mustache": "^4.2.0",
"nanoid": "^5.0.7", "nanoid": "^5.0.9",
"ol": "^7.5.2",
"polished": "^4.3.1", "polished": "^4.3.1",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"query-string": "^6.13.7", "query-string": "^6.13.7",
"rc-trigger": "^5.3.4", "rc-trigger": "^5.3.4",
"re-resizable": "^6.10.1", "re-resizable": "^6.10.1",
"react": "^16.13.1", "react": "^17.0.2",
"react-ace": "^10.1.0", "react-ace": "^10.1.0",
"react-checkbox-tree": "^1.8.0", "react-checkbox-tree": "^1.8.0",
"react-color": "^2.13.8", "react-color": "^2.13.8",
"react-diff-viewer-continued": "^3.4.0", "react-diff-viewer-continued": "^3.4.0",
"react-dnd": "^11.1.3", "react-dnd": "^11.1.3",
"react-dnd-html5-backend": "^11.1.3", "react-dnd-html5-backend": "^11.1.3",
"react-dom": "^16.13.1", "react-dom": "^17.0.2",
"react-draggable": "^4.4.6", "react-draggable": "^4.4.6",
"react-hot-loader": "^4.13.1", "react-hot-loader": "^4.13.1",
"react-intersection-observer": "^9.10.2", "react-intersection-observer": "^9.10.2",
@ -193,9 +195,9 @@
"react-split": "^2.0.9", "react-split": "^2.0.9",
"react-syntax-highlighter": "^15.4.5", "react-syntax-highlighter": "^15.4.5",
"react-table": "^7.8.0", "react-table": "^7.8.0",
"react-transition-group": "^2.5.3", "react-transition-group": "^4.4.5",
"react-ultimate-pagination": "^1.3.2", "react-ultimate-pagination": "^1.3.2",
"react-virtualized-auto-sizer": "^1.0.24", "react-virtualized-auto-sizer": "^1.0.25",
"react-window": "^1.8.10", "react-window": "^1.8.10",
"redux": "^4.2.1", "redux": "^4.2.1",
"redux-localstorage": "^0.4.1", "redux-localstorage": "^0.4.1",
@ -217,7 +219,7 @@
"@applitools/eyes-storybook": "^3.50.9", "@applitools/eyes-storybook": "^3.50.9",
"@babel/cli": "^7.22.6", "@babel/cli": "^7.22.6",
"@babel/compat-data": "^7.22.6", "@babel/compat-data": "^7.22.6",
"@babel/core": "^7.23.9", "@babel/core": "^7.26.0",
"@babel/eslint-parser": "^7.25.9", "@babel/eslint-parser": "^7.25.9",
"@babel/node": "^7.22.6", "@babel/node": "^7.22.6",
"@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-properties": "^7.18.6",
@ -225,15 +227,19 @@
"@babel/plugin-proposal-optional-chaining": "^7.21.0", "@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@babel/plugin-proposal-private-methods": "^7.18.6", "@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.22.7", "@babel/plugin-transform-modules-commonjs": "^7.26.3",
"@babel/preset-env": "^7.22.7", "@babel/plugin-transform-runtime": "^7.25.9",
"@babel/preset-react": "^7.22.5", "@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@babel/register": "^7.23.7", "@babel/register": "^7.23.7",
"@babel/types": "^7.24.9", "@babel/runtime": "^7.26.0",
"@babel/runtime-corejs3": "^7.26.0",
"@babel/types": "^7.26.5",
"@cypress/react": "^8.0.2", "@cypress/react": "^8.0.2",
"@emotion/babel-plugin": "^11.11.0", "@emotion/babel-plugin": "^11.13.5",
"@emotion/jest": "^11.11.0", "@emotion/jest": "^11.13.0",
"@hot-loader/react-dom": "^16.14.0", "@hot-loader/react-dom": "^17.0.2",
"@istanbuljs/nyc-config-typescript": "^1.0.1", "@istanbuljs/nyc-config-typescript": "^1.0.1",
"@mihkeleidast/storybook-addon-source": "^1.0.1", "@mihkeleidast/storybook-addon-source": "^1.0.1",
"@storybook/addon-actions": "8.1.11", "@storybook/addon-actions": "8.1.11",
@ -254,7 +260,6 @@
"@types/classnames": "^2.2.10", "@types/classnames": "^2.2.10",
"@types/dom-to-image": "^2.6.7", "@types/dom-to-image": "^2.6.7",
"@types/enzyme": "^3.10.18", "@types/enzyme": "^3.10.18",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/fetch-mock": "^7.3.2", "@types/fetch-mock": "^7.3.2",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"@types/jquery": "^3.5.8", "@types/jquery": "^3.5.8",
@ -262,8 +267,8 @@
"@types/json-bigint": "^1.0.4", "@types/json-bigint": "^1.0.4",
"@types/math-expression-evaluator": "^1.3.3", "@types/math-expression-evaluator": "^1.3.3",
"@types/mousetrap": "^1.6.15", "@types/mousetrap": "^1.6.15",
"@types/react": "^16.9.53", "@types/react": "^17.0.83",
"@types/react-dom": "^16.9.8", "@types/react-dom": "^17.0.26",
"@types/react-gravatar": "^2.6.14", "@types/react-gravatar": "^2.6.14",
"@types/react-json-tree": "^0.6.11", "@types/react-json-tree": "^0.6.11",
"@types/react-loadable": "^5.5.11", "@types/react-loadable": "^5.5.11",
@ -271,7 +276,7 @@
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"@types/react-syntax-highlighter": "^15.5.13", "@types/react-syntax-highlighter": "^15.5.13",
"@types/react-table": "^7.7.20", "@types/react-table": "^7.7.20",
"@types/react-transition-group": "^4.4.10", "@types/react-transition-group": "^4.4.12",
"@types/react-ultimate-pagination": "^1.2.4", "@types/react-ultimate-pagination": "^1.2.4",
"@types/react-virtualized-auto-sizer": "^1.0.4", "@types/react-virtualized-auto-sizer": "^1.0.4",
"@types/react-window": "^1.8.8", "@types/react-window": "^1.8.8",
@ -283,24 +288,26 @@
"@types/yargs": "12 - 18", "@types/yargs": "12 - 18",
"@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/parser": "^5.62.0",
"@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
"babel-jest": "^29.7.0", "babel-jest": "^29.7.0",
"babel-loader": "^9.1.3", "babel-loader": "^9.1.3",
"babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-jsx-remove-data-test-id": "^3.0.0", "babel-plugin-jsx-remove-data-test-id": "^3.0.0",
"babel-plugin-lodash": "^3.3.4", "babel-plugin-lodash": "^3.3.4",
"babel-plugin-typescript-to-proptypes": "^2.0.0",
"cheerio": "1.0.0-rc.10",
"copy-webpack-plugin": "^12.0.2", "copy-webpack-plugin": "^12.0.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^6.8.1", "css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^5.0.1", "css-minimizer-webpack-plugin": "^7.0.0",
"enzyme": "^3.11.0", "enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.7",
"esbuild": "^0.20.0", "esbuild": "^0.20.0",
"esbuild-loader": "^4.2.2", "esbuild-loader": "^4.2.2",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^7.2.0", "eslint-config-prettier": "^7.2.0",
"eslint-import-resolver-typescript": "^3.7.0", "eslint-import-resolver-typescript": "^3.7.0",
"eslint-plugin-cypress": "^3.5.0", "eslint-plugin-cypress": "^3.6.0",
"eslint-plugin-file-progress": "^1.5.0", "eslint-plugin-file-progress": "^1.5.0",
"eslint-plugin-import": "^2.24.2", "eslint-plugin-import": "^2.24.2",
"eslint-plugin-jest": "^27.8.0", "eslint-plugin-jest": "^27.8.0",
@ -310,7 +317,7 @@
"eslint-plugin-no-only-tests": "^3.3.0", "eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.37.2", "eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-prefer-function-component": "^3.3.0", "eslint-plugin-react-prefer-function-component": "^3.3.0",
"eslint-plugin-storybook": "^0.8.0", "eslint-plugin-storybook": "^0.8.0",
"eslint-plugin-testing-library": "^6.4.0", "eslint-plugin-testing-library": "^6.4.0",
@ -320,6 +327,7 @@
"fetch-mock": "^7.7.3", "fetch-mock": "^7.7.3",
"fork-ts-checker-webpack-plugin": "^9.0.2", "fork-ts-checker-webpack-plugin": "^9.0.2",
"history": "^5.3.0", "history": "^5.3.0",
"html-webpack-plugin": "^5.6.3",
"ignore-styles": "^5.0.1", "ignore-styles": "^5.0.1",
"imports-loader": "^5.0.0", "imports-loader": "^5.0.0",
"jest": "^29.7.0", "jest": "^29.7.0",
@ -328,21 +336,19 @@
"jest-enzyme": "^7.1.2", "jest-enzyme": "^7.1.2",
"jest-html-reporter": "^3.10.2", "jest-html-reporter": "^3.10.2",
"jest-websocket-mock": "^2.5.0", "jest-websocket-mock": "^2.5.0",
"jsdom": "^24.1.1", "jsdom": "^26.0.0",
"lerna": "^8.1.7", "lerna": "^8.1.7",
"less": "^4.2.0", "less": "^4.2.0",
"less-loader": "^12.2.0", "less-loader": "^12.2.0",
"mini-css-extract-plugin": "^2.9.0", "mini-css-extract-plugin": "^2.9.0",
"mock-socket": "^9.3.1", "mock-socket": "^9.3.1",
"moment-locales-webpack-plugin": "^1.2.0",
"node-fetch": "^2.6.7",
"open-cli": "^8.0.0", "open-cli": "^8.0.0",
"po2json": "^0.4.5", "po2json": "^0.4.5",
"prettier": "3.3.3", "prettier": "3.3.3",
"prettier-plugin-packagejson": "^2.5.3", "prettier-plugin-packagejson": "^2.5.3",
"process": "^0.11.10", "process": "^0.11.10",
"react-resizable": "^3.0.5", "react-resizable": "^3.0.5",
"react-test-renderer": "^16.14.0", "react-test-renderer": "^17.0.2",
"redux-mock-store": "^1.5.4", "redux-mock-store": "^1.5.4",
"sinon": "^18.0.0", "sinon": "^18.0.0",
"source-map": "^0.7.4", "source-map": "^0.7.4",
@ -351,17 +357,17 @@
"storybook": "8.1.11", "storybook": "8.1.11",
"style-loader": "^4.0.0", "style-loader": "^4.0.0",
"thread-loader": "^4.0.4", "thread-loader": "^4.0.4",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.1", "ts-loader": "^9.5.1",
"typescript": "^4.8.4", "typescript": "5.1.6",
"vm-browserify": "^1.1.2", "vm-browserify": "^1.1.2",
"webpack": "^5.94.0", "webpack": "^5.97.1",
"webpack-bundle-analyzer": "^4.10.1", "webpack-bundle-analyzer": "^4.10.1",
"webpack-cli": "^4.10.0", "webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.15.1", "webpack-dev-server": "^4.15.1",
"webpack-manifest-plugin": "^5.0.0", "webpack-manifest-plugin": "^5.0.0",
"webpack-sources": "^3.2.3", "webpack-sources": "^3.2.3",
"webpack-visualizer-plugin2": "^1.1.0", "webpack-visualizer-plugin2": "^1.1.0"
"xdm": "^3.4.0"
}, },
"engines": { "engines": {
"node": "^20.16.0", "node": "^20.16.0",
@ -374,7 +380,6 @@
"ansi-regex": "^4.1.1" "ansi-regex": "^4.1.1"
}, },
"puppeteer": "^22.4.1", "puppeteer": "^22.4.1",
"@types/react": "^16.9.53",
"underscore": "^1.13.7", "underscore": "^1.13.7",
"fast-glob": { "fast-glob": {
"micromatch": "^4.0.6" "micromatch": "^4.0.6"

View File

@ -20,7 +20,7 @@
"@airbnb/config-babel": "^2.0.1", "@airbnb/config-babel": "^2.0.1",
"@superset-ui/chart-controls": "*", "@superset-ui/chart-controls": "*",
"@superset-ui/core": "*", "@superset-ui/core": "*",
"react": "^16.13.1" "react": "^17.0.2"
} }
}, },
"node_modules/@airbnb/config-babel": { "node_modules/@airbnb/config-babel": {

View File

@ -31,7 +31,7 @@
"@airbnb/config-babel": "^2.0.1", "@airbnb/config-babel": "^2.0.1",
"@superset-ui/chart-controls": "*", "@superset-ui/chart-controls": "*",
"@superset-ui/core": "*", "@superset-ui/core": "*",
"react": "^16.13.1" "react": "^17.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.16.0", "@babel/cli": "^7.16.0",

View File

@ -28,7 +28,7 @@
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest" "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest"
}, },
"dependencies": { "dependencies": {
"chalk": "^5.4.0", "chalk": "^5.4.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"yeoman-generator": "^7.4.0", "yeoman-generator": "^7.4.0",
"yosay": "^3.0.0" "yosay": "^3.0.0"

View File

@ -31,7 +31,7 @@
"prop-types": "^15.8.1" "prop-types": "^15.8.1"
}, },
"peerDependencies": { "peerDependencies": {
"@ant-design/icons": "^5.0.1", "@ant-design/icons": "^5.2.6",
"@emotion/react": "^11.4.1", "@emotion/react": "^11.4.1",
"@superset-ui/core": "*", "@superset-ui/core": "*",
"@testing-library/dom": "^8.20.1", "@testing-library/dom": "^8.20.1",
@ -43,9 +43,9 @@
"antd": "4.10.3", "antd": "4.10.3",
"brace": "^0.11.1", "brace": "^0.11.1",
"memoize-one": "^5.1.1", "memoize-one": "^5.1.1",
"react": "^16.13.1", "react": "^17.0.2",
"react-ace": "^10.1.0", "react-ace": "^10.1.0",
"react-dom": "^16.13.1" "react-dom": "^17.0.2"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

Some files were not shown because too many files have changed in this diff Show More