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
---
github:
del_branch_on_merge: true
description: "Apache Superset is a Data Visualization and Data Exploration Platform"
homepage: https://superset.apache.org/
labels:
@ -75,16 +76,13 @@ github:
- dependency-review
- frontend-build
- pre-commit (current)
- pre-commit (next)
- pre-commit (previous)
- test-mysql
- test-postgres (current)
- test-postgres (next)
- test-postgres-hive
- test-postgres-presto
- test-sqlite
- unit-tests (current)
- unit-tests (next)
required_pull_request_reviews:
dismiss_stale_reviews: false

View File

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

4
.github/CODEOWNERS vendored
View File

@ -12,7 +12,7 @@
# 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
@ -24,7 +24,7 @@
# 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

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
run: |
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
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
echo "PYTHON_VERSION=3.9" >> $GITHUB_ENV
echo "PYTHON_VERSION=3.10" >> $GITHUB_ENV
else
echo "PYTHON_VERSION=${{ inputs.python-version }}" >> $GITHUB_ENV
fi
@ -43,6 +44,7 @@ runs:
run: |
if [ "${{ inputs.install-superset }}" = "true" ]; then
sudo apt-get update && sudo apt-get -y install libldap2-dev libsasl2-dev
pip install --upgrade pip setuptools wheel uv
if [ "${{ inputs.requirements-type }}" = "dev" ]; then

View File

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

View File

@ -14,6 +14,7 @@ concurrency:
cancel-in-progress: true
jobs:
setup_matrix:
runs-on: ubuntu-24.04
outputs:
@ -36,6 +37,7 @@ jobs:
env:
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
IMAGE_TAG: apache/superset:GHA-${{ matrix.build_preset }}-${{ github.run_id }}
steps:
@ -71,35 +73,65 @@ jobs:
# Single platform builds in pull_request context to speed things up
if [ "${{ github.event_name }}" = "push" ]; then
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
PLATFORM_ARG="--platform linux/amd64"
PUSH_OR_LOAD="--load"
fi
supersetbot docker \
--push \
$PUSH_OR_LOAD \
--preset ${{ matrix.build_preset }} \
--context "$EVENT" \
--context-ref "$RELEASE" $FORCE_LATEST \
--extra-flags "--build-arg INCLUDE_CHROMIUM=false" \
--extra-flags "--build-arg INCLUDE_CHROMIUM=false --tag $IMAGE_TAG" \
$PLATFORM_ARG
# in the context of push (using multi-platform build), we need to pull the image locally
- name: Docker pull
if: steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker
run: docker pull apache/superset:GHA-${GITHUB_RUN_ID}
if: github.event_name == 'push' && (steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker)
run: docker pull $IMAGE_TAG
- name: Print docker stats
if: steps.check.outputs.python || steps.check.outputs.frontend || steps.check.outputs.docker
run: |
IMAGE_ID=$(docker images --filter "label=sha=${{ github.sha }}" --format "{{.ID}}" | head -n 1)
echo "SHA: ${{ github.sha }}"
echo "IMAGE: $IMAGE_ID"
docker images $IMAGE_ID
docker history $IMAGE_ID
echo "IMAGE: $IMAGE_TAG"
docker images $IMAGE_TAG
docker history $IMAGE_TAG
- 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')
shell: bash
run: |
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 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
# 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:
issue_comment:
types: [created]
pull_request_target:
types:
- labeled
workflow_dispatch:
inputs:
comment_body:
description: 'Comment body to simulate /testenv command'
label_name:
description: 'Label name to simulate label-based /testenv trigger'
required: true
default: '/testenv up'
default: 'testenv-up'
issue_number:
description: 'Issue or PR number'
required: true
jobs:
ephemeral-env-comment:
ephemeral-env-label:
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
name: Evaluate ephemeral env comment trigger (/testenv)
name: Evaluate ephemeral env label trigger
runs-on: ubuntu-24.04
permissions:
pull-requests: write
outputs:
slash-command: ${{ steps.eval-body.outputs.result }}
slash-command: ${{ steps.eval-label.outputs.result }}
feature-flags: ${{ steps.eval-feature-flags.outputs.result }}
sha: ${{ steps.get-sha.outputs.sha }}
env:
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
- name: Debug
run: |
echo "Comment on PR #${{ github.event.issue.number }} by ${{ github.event.issue.user.login }}, ${{ github.event.comment.author_association }}"
- name: Check for the "testenv-up" label
id: eval-label
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
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];
echo "Evaluating label: $LABEL_NAME"
- name: Looking for feature flags
uses: actions/github-script@v7
env:
COMMENT_BODY: ${{ github.event.inputs.comment_body || github.event.comment.body }}
id: eval-feature-flags
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;
if [[ "$LABEL_NAME" == "testenv-up" ]]; then
echo "result=up" >> $GITHUB_OUTPUT
else
echo "result=noop" >> $GITHUB_OUTPUT
fi
- name: Limit to committers
if: >
steps.eval-body.outputs.result != 'noop' &&
github.event_name == 'issue_comment' &&
github.event.comment.author_association != 'MEMBER' &&
github.event.comment.author_association != 'OWNER'
uses: actions/github-script@v7
with:
github-token: ${{ github.token }}
script: |
const errMsg = '@${{ github.event.comment.user.login }} Ephemeral environment creation is currently limited to committers.';
github.rest.issues.createComment({
issue_number: ${{ github.event.issue.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: errMsg
});
core.setFailed(errMsg);
- name: Get event SHA
id: get-sha
if: steps.eval-label.outputs.result == 'up'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
let prSha;
// If event is workflow_dispatch, use the issue_number from inputs
if (context.eventName === "workflow_dispatch") {
const prNumber = "${{ github.event.inputs.issue_number }}";
if (!prNumber) {
console.log("No PR number found.");
return;
}
// 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({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body,
});
}
else {
core.setFailed('No ephemeral environment action detected.');
}
ephemeral-docker-build:
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
needs: ephemeral-env-comment
if: needs.ephemeral-env-comment.outputs.slash-command == 'up'
needs: ephemeral-env-label
if: needs.ephemeral-env-label.outputs.slash-command == 'up'
name: ephemeral-docker-build
runs-on: ubuntu-24.04
steps:
- name: Get Info from comment
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}} )"
- name: "Checkout ${{ github.ref }} ( ${{ needs.ephemeral-env-label.outputs.sha }} : ${{steps.get-sha.outputs.sha}} )"
uses: actions/checkout@v4
with:
ref: ${{ steps.get-sha.outputs.sha }}
ref: ${{ needs.ephemeral-env-label.outputs.sha }}
persist-credentials: false
- name: Setup Docker Environment
@ -181,14 +189,14 @@ jobs:
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
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: |
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
ephemeral-env-up:
needs: [ephemeral-env-comment, ephemeral-docker-build]
if: needs.ephemeral-env-comment.outputs.slash-command == 'up'
needs: [ephemeral-env-label, ephemeral-docker-build]
if: needs.ephemeral-env-label.outputs.slash-command == 'up'
name: Spin up an ephemeral environment
runs-on: ubuntu-24.04
permissions:
@ -196,120 +204,122 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Check target image exists in ECR
id: check-image
continue-on-error: true
run: |
aws ecr describe-images \
--registry-id $(echo "${{ steps.login-ecr.outputs.registry }}" | grep -Eo "^[0-9]+") \
--repository-name superset-ci \
--image-ids imageTag=pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-ci
- name: Check target image exists in ECR
id: check-image
continue-on-error: true
run: |
aws ecr describe-images \
--registry-id $(echo "${{ steps.login-ecr.outputs.registry }}" | grep -Eo "^[0-9]+") \
--repository-name superset-ci \
--image-ids imageTag=pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-ci
- name: Fail on missing container image
if: steps.check-image.outcome == 'failure'
uses: actions/github-script@v7
with:
github-token: ${{ github.token }}
script: |
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({
issue_number: ${{ github.event.inputs.issue_number || github.event.issue.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: errMsg
});
core.setFailed(errMsg);
- name: Fail on missing container image
if: steps.check-image.outcome == 'failure'
uses: actions/github-script@v7
with:
github-token: ${{ github.token }}
script: |
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({
issue_number: ${{ github.event.inputs.issue_number || github.event.issue.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: errMsg
});
core.setFailed(errMsg);
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: .github/workflows/ecs-task-definition.json
container-name: superset-ci
image: ${{ steps.login-ecr.outputs.registry }}/superset-ci:pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-ci
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: .github/workflows/ecs-task-definition.json
container-name: superset-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
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 }}
- name: Update env vars in the Amazon ECS task definition
run: |
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
id: describe-services
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
- name: Create ECS service
id: create-service
if: steps.describe-services.outputs.active != 'true'
env:
ECR_SUBNETS: subnet-0e15a5034b4121710,subnet-0e8efef4a72224974
ECR_SECURITY_GROUP: sg-092ff3a6ae0574d91
run: |
aws ecs create-service \
--cluster superset-ci \
--service-name pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service \
--task-definition superset-ci \
--launch-type FARGATE \
--desired-count 1 \
--platform-version LATEST \
--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 }}
- name: Deploy Amazon ECS task definition
id: deploy-task
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service
cluster: superset-ci
wait-for-service-stability: true
wait-for-minutes: 10
- name: Describe ECS service
id: describe-services
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
- name: Create ECS service
id: create-service
if: steps.describe-services.outputs.active != 'true'
env:
ECR_SUBNETS: subnet-0e15a5034b4121710,subnet-0e8efef4a72224974
ECR_SECURITY_GROUP: sg-092ff3a6ae0574d91
run: |
aws ecs create-service \
--cluster superset-ci \
--service-name pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service \
--task-definition superset-ci \
--launch-type FARGATE \
--desired-count 1 \
--platform-version LATEST \
--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 }}
- name: Deploy Amazon ECS task definition
id: deploy-task
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: pr-${{ github.event.inputs.issue_number || github.event.issue.number }}-service
cluster: superset-ci
wait-for-service-stability: true
wait-for-minutes: 10
- name: List tasks
id: list-tasks
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
- name: Get network interface
id: get-eni
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
- name: Get public IP
id: get-ip
run: |
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)
if: ${{ success() }}
uses: actions/github-script@v7
with:
github-token: ${{github.token}}
script: |
github.rest.issues.createComment({
issue_number: ${{ github.event.inputs.issue_number || github.event.issue.number }},
owner: context.repo.owner,
repo: context.repo.repo,
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.'
})
- name: Comment (failure)
if: ${{ failure() }}
uses: actions/github-script@v7
with:
github-token: ${{github.token}}
script: |
github.rest.issues.createComment({
issue_number: ${{ github.event.inputs.issue_number || github.event.issue.number }},
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.'
})
- name: List tasks
id: list-tasks
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
- name: Get network interface
id: get-eni
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
- name: Get public IP
id: get-ip
run: |
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)
if: ${{ success() }}
uses: actions/github-script@v7
with:
github-token: ${{github.token}}
script: |
const issue_number = context.payload.inputs?.issue_number || context.issue.number;
github.rest.issues.createComment({
issue_number: issue_number,
owner: context.repo.owner,
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() }}
uses: actions/github-script@v7
with:
github-token: ${{github.token}}
script: |
const issue_number = context.payload.inputs?.issue_number || context.issue.number;
github.rest.issues.createComment({
issue_number: issue_number,
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
strategy:
matrix:
python-version: ["current", "next", "previous"]
python-version: ["current", "previous"]
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v4
@ -41,6 +41,8 @@ jobs:
- name: pre-commit
run: |
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
if [ $? -ne 0 ] || ! git diff --quiet --exit-code; then
echo "❌ Pre-commit check failed."

View File

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

View File

@ -138,7 +138,7 @@ jobs:
run: cypress-run-all ${{ env.USE_DASHBOARD }}
- name: Upload Artifacts
uses: actions/upload-artifact@v4
if: github.event_name == 'workflow_dispatch' && (steps.check.outputs.python || steps.check.outputs.frontend)
if: failure()
with:
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:
push:
@ -13,68 +13,168 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
env:
TAG: apache/superset:GHA-${{ github.run_id }}
jobs:
frontend-build:
runs-on: ubuntu-24.04
outputs:
should-run: ${{ steps.check.outputs.frontend }}
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- name: Checkout Code
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
- name: Check npm lock file version
run: ./scripts/ci_check_npm_lock_version.sh ./superset-frontend/package-lock.json
- name: Check for file changes
- name: Check for File Changes
id: check
uses: ./.github/actions/change-detector/
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
- name: Build Docker Image
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:
node-version: "20"
- name: Install dependencies
if: steps.check.outputs.frontend
uses: ./.github/actions/cached-dependencies
name: docker-image
path: docker-image.tar.gz
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:
run: npm-install
- name: eslint
if: steps.check.outputs.frontend
working-directory: ./superset-frontend
name: docker-image
- name: Load Docker Image
run: docker load < docker-image.tar.gz
- name: npm run test with coverage
run: |
npm run eslint -- . --quiet
- name: tsc
if: steps.check.outputs.frontend
working-directory: ./superset-frontend
run: |
npm run type
- name: Build plugins packages
if: steps.check.outputs.frontend
working-directory: ./superset-frontend
run: npm run plugins:build
- name: Build plugins Storybook
if: steps.check.outputs.frontend
working-directory: ./superset-frontend
run: npm run plugins:build-storybook
- name: superset-ui/core coverage
if: steps.check.outputs.frontend
working-directory: ./superset-frontend
run: |
npm run core:cover
- name: unit tests
if: steps.check.outputs.frontend
working-directory: ./superset-frontend
run: |
npm run test -- --coverage --silent
# todo: remove this step when fix generator as a project in root jest.config.js
- name: generator-superset unit tests
if: steps.check.outputs.frontend
working-directory: ./superset-frontend/packages/generator-superset
run: npm run test
- name: Upload code coverage
mkdir -p ${{ github.workspace }}/superset-frontend/coverage
docker run \
-v ${{ github.workspace }}/superset-frontend/coverage:/app/superset-frontend/coverage \
--rm $TAG \
bash -c \
"npm run test -- --coverage --shard=${{ matrix.shard }}/8 --coverageReporters=json-summary"
- name: Upload Coverage Artifact
uses: actions/upload-artifact@v4
with:
name: coverage-artifacts-${{ matrix.shard }}
path: superset-frontend/coverage
report-coverage:
needs: [sharded-jest-tests]
if: needs.frontend-build.outputs.should-run == 'true'
runs-on: ubuntu-24.04
steps:
- name: Download Coverage Artifacts
uses: actions/download-artifact@v4
with:
pattern: coverage-artifacts-*
path: coverage/
- name: Show Files
run: find coverage/
- name: Merge Code Coverage
run: npx nyc merge coverage/ merged-output/coverage-summary.json
- name: Upload Code Coverage
uses: codecov/codecov-action@v5
with:
flags: javascript
token: ${{ secrets.CODECOV_TOKEN }}
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
uses: azure/setup-helm@v4
with:
version: v3.5.4
version: v3.16.4
- name: Setup Python
uses: ./.github/actions/setup-backend/

View File

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

View File

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

7
.gitignore vendored
View File

@ -50,7 +50,6 @@ env
venv*
env_py3
envpy3
env36
local_config.py
/superset_config.py
/superset_text.yml
@ -66,7 +65,10 @@ superset-websocket/config.json
*.js.map
node_modules
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-frontend/**/esm/*
superset-frontend/**/lib/*
@ -122,3 +124,4 @@ docker/*local*
# Jest test report
test-report.html
superset/static/stats/statistics.html
.aider*

View File

@ -60,6 +60,14 @@ repos:
- prettier@3.3.3
args: ["--ignore-path=./superset-frontend/.prettierignore"]
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)
- repo: https://github.com/skorokithakis/blacklist-pre-commit-hook
rev: e2f070289d8eddcaec0b580d3bde29437e7c8221

View File

@ -18,16 +18,19 @@
######################################################################
# 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).
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
ARG BUILD_TRANSLATIONS="false" # Include translations in the final build
FROM --platform=${BUILDPLATFORM} node:20-bullseye-slim AS superset-node-ci
ARG BUILD_TRANSLATIONS
ENV BUILD_TRANSLATIONS=${BUILD_TRANSLATIONS}
ARG DEV_MODE="false" # Skip frontend build in dev mode
ENV DEV_MODE=${DEV_MODE}
@ -55,6 +58,10 @@ RUN mkdir -p /app/superset/static/assets \
/app/superset/translations
# 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 \
--mount=type=bind,source=./superset-frontend/package-lock.json,target=./package-lock.json \
--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
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
RUN --mount=type=cache,target=/app/superset-frontend/.temp_cache \
--mount=type=cache,target=/root/.npm \
RUN --mount=type=cache,target=/root/.npm \
if [ "$DEV_MODE" = "false" ]; then \
echo "Running 'npm run ${BUILD_CMD}'"; \
npm run ${BUILD_CMD}; \
@ -93,21 +104,14 @@ RUN if [ "$BUILD_TRANSLATIONS" = "true" ]; then \
# Base python layer
######################################################################
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 \
LC_ALL=C.UTF-8 \
SUPERSET_ENV=production \
FLASK_APP="superset.app:create_app()" \
PYTHONPATH="/app/pythonpath" \
SUPERSET_HOME="/app/superset_home" \
SUPERSET_PORT=8088
ARG SUPERSET_HOME="/app/superset_home"
ENV SUPERSET_HOME=${SUPERSET_HOME}
RUN useradd --user-group -d ${SUPERSET_HOME} -m --no-log-init --shell /bin/bash superset
RUN mkdir -p $SUPERSET_HOME
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
COPY --chmod=755 docker/*.sh /app/docker/
@ -118,28 +122,18 @@ RUN pip install --no-cache-dir --upgrade uv
RUN uv venv /app/.venv
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
######################################################################
FROM python-base AS python-translation-compiler
ARG BUILD_TRANSLATIONS
ENV BUILD_TRANSLATIONS=${BUILD_TRANSLATIONS}
# Install Python dependencies using docker/pip-install.sh
COPY requirements/translations.txt requirements/
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/
RUN if [ "$BUILD_TRANSLATIONS" = "true" ]; then \
@ -152,13 +146,20 @@ RUN if [ "$BUILD_TRANSLATIONS" = "true" ]; then \
# Python APP common layer
######################################################################
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 --chmod=755 docker/entrypoints /app/docker/entrypoints
WORKDIR /app
# Set up necessary directories and user
RUN mkdir -p \
${SUPERSET_HOME} \
${PYTHONPATH} \
superset/static \
requirements \
@ -167,6 +168,19 @@ RUN mkdir -p \
requirements \
&& 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 pyproject.toml setup.py MANIFEST.in README.md ./
COPY superset-frontend/package.json superset-frontend/
@ -209,12 +223,11 @@ FROM python-common AS lean
# Install Python dependencies using docker/pip-install.sh
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
# Install the superset package
RUN --mount=type=cache,target=/root/.cache/uv \
RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
uv pip install .
RUN python -m compileall /app/superset
USER superset
@ -233,12 +246,13 @@ RUN /app/docker/apt-install.sh \
# Copy development requirements and install them
COPY requirements/*.txt requirements/
# 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
# Install the superset package
RUN --mount=type=cache,target=/root/.cache/uv \
RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
uv pip install .
RUN uv pip install .[postgres]
RUN python -m compileall /app/superset
USER superset
@ -247,5 +261,7 @@ USER superset
# CI image...
######################################################################
FROM lean AS ci
USER root
RUN uv pip install .[postgres]
USER superset
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/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/tdengine.png" alt="TDengine" border="0" width="200" />
</p>
**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
# https://superset.apache.org/docs/installation/installing-superset-from-scratch
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
# https://nodejs.org/en/download/package-manager/
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; \
node --version;
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
ENV PATH=/home/superset/superset/bin:$PATH \
PYTHONPATH=/home/superset/superset/:$PYTHONPATH \
PYTHONPATH=/home/superset/superset/ \
SUPERSET_TESTENV=true
COPY from_tarball_entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -29,13 +29,16 @@ RUN apt-get install -y apt-transport-https apt-utils
# Install superset dependencies
# https://superset.apache.org/docs/installation/installing-superset-from-scratch
RUN apt-get install -y build-essential libssl-dev \
libffi-dev python3-dev libsasl2-dev libldap2-dev libxi-dev chromium
RUN apt-get install -y subversion build-essential libssl-dev \
libffi-dev python3-dev libsasl2-dev libldap2-dev libxi-dev chromium zstd
# Install nodejs for custom build
# https://nodejs.org/en/download/package-manager/
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - \
&& apt-get install -y nodejs
RUN set -eux; \
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 chown superset /home/superset
@ -46,14 +49,12 @@ ARG VERSION
# 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 tar -xvf *.tar.gz
WORKDIR apache-superset-$VERSION
WORKDIR /home/superset/apache-superset-$VERSION/superset-frontend
RUN cd superset-frontend \
&& npm ci \
RUN npm ci \
&& npm run build \
&& rm -rf node_modules
WORKDIR /home/superset/apache-superset-$VERSION
RUN pip install --upgrade setuptools pip \
&& pip install -r requirements/base.txt \
@ -62,6 +63,6 @@ RUN pip install --upgrade setuptools pip \
RUN flask fab babel-compile --target superset/translations
ENV PATH=/home/superset/superset/bin:$PATH \
PYTHONPATH=/home/superset/superset/:$PYTHONPATH
PYTHONPATH=/home/superset/superset/
COPY from_tarball_entrypoint.sh /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
- CACHE_IMPERSONATION
- CONFIRM_DASHBOARD_DIFF
- DRILL_TO_DETAIL
- 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)
- ESTIMATE_QUERY_COST
- 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"
### Flags on the path to feature launch and flag deprecation/removal
- DASHBOARD_VIRTUALIZATION
- DRILL_BY
- DISABLE_LEGACY_DATASOURCE_EDITOR
### Flags retained for runtime configuration
@ -79,6 +78,7 @@ independently. This new framework will also allow for non-boolean configurations
- ALLOW_ADHOC_SUBQUERY
- DASHBOARD_RBAC [(docs)](https://superset.apache.org/docs/using-superset/creating-your-first-dashboard#manage-access-to-dashboards)
- DATAPANEL_CLOSED_BY_DEFAULT
- DRILL_BY
- DRUID_JOINS
- EMBEDDABLE_CHARTS
- 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"
- AVOID_COLORS_COLLISION
- DASHBOARD_CROSS_FILTERS
- DRILL_TO_DETAIL
- ENABLE_JAVASCRIPT_CONTROLS
- KV_STORE

View File

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

View File

@ -24,13 +24,28 @@ assists people when migrating to a new version.
## 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".
- [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.
- [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.
- [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, ...
- [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

View File

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

View File

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

View File

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

View File

@ -15,6 +15,8 @@
# limitations under the License.
#
# Allowing python to print() in docker
PYTHONUNBUFFERED=1
COMPOSE_PROJECT_NAME=superset
DEV_MODE=true
@ -64,3 +66,4 @@ SUPERSET_SECRET_KEY=TEST_NON_DEV_SECRET
ENABLE_PLAYWRIGHT=false
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=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
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

View File

@ -18,24 +18,32 @@
set -eo pipefail
# UV may not be installed in older images
pip install uv
# Make python interactive
if [ "$DEV_MODE" == "true" ]; then
echo "Reinstalling the app in editable mode"
uv pip install -e .
if [ "$(whoami)" = "root" ] && command -v uv > /dev/null 2>&1; then
echo "Reinstalling the app in editable mode"
uv pip install -e .
fi
fi
REQUIREMENTS_LOCAL="/app/docker/requirements-local.txt"
PORT=${PORT:-8088}
# If Cypress run overwrite the password for admin and export env variables
if [ "$CYPRESS_CONFIG" == "true" ]; then
export SUPERSET_CONFIG=tests.integration_tests.superset_test_config
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
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"
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
#
# Make sure we have dev requirements installed
@ -60,7 +68,7 @@ case "${1}" in
;;
app)
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)
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
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
echo "Skipping frontend build steps - YOU NEED TO RUN IT MANUALLY ON THE HOST!"

View File

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

View File

@ -23,4 +23,4 @@
export SERVER_THREADS_AMOUNT=8
# 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 os
import sys
from celery.schedules import crontab
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
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
# 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:
- emails: `SMTP_*` settings
- 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
@ -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):
```python
from superset.tasks.types import ExecutorType
from superset.tasks.types import FixedExecutor
THUMBNAIL_SELENIUM_USER = 'admin'
ALERT_REPORTS_EXECUTE_AS = [ExecutorType.SELENIUM]
ALERT_REPORTS_EXECUTORS = [FixedExecutor("admin")]
```
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:
```python
from superset.tasks.types import ExecutorType
from superset.tasks.types import FixedExecutor
THUMBNAIL_SELENIUM_USER = "admin"
THUMBNAIL_EXECUTE_AS = [ExecutorType.SELENIUM]
THUMBNAIL_EXECUTORS = [FixedExecutor("admin")]
```
@ -130,8 +129,6 @@ def init_thumbnail_cache(app: Flask) -> S3Cache:
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

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>` |
| [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://` |
| [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>` |
| [Presto](/docs/configuration/databases#presto) | `pip install pyhive` | `presto://` |
| [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}` |
| 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://` |
| [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}` |
| [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}` |
@ -1074,6 +1076,23 @@ The connection string is formatted as follows:
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
@ -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/).
:::
#### 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
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
- `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
@ -108,6 +108,10 @@ running a custom auth postback endpoint), you can add the endpoints to `WTF_CSRF
## 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
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,

View File

@ -72,6 +72,7 @@ documentation.
configured to be secure.
:::
### Supported environment variables
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_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
- **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
[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.
### 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
@ -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
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
pip install -r requirements/development.txt
@ -626,71 +643,6 @@ To run a single test file:
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
#### Local

View File

@ -225,34 +225,37 @@ npm run test -- path/to/file.js
### e2e Integration Testing
For e2e testing, we recommend that you use a `docker-compose` backed-setup
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:
For e2e testing, we recommend that you use a `docker compose` backend
```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 init
superset load-examples --load-test-data
superset run --port 8081
CYPRESS_CONFIG=true docker compose up --build
```
`docker compose` will get to work and expose a Cypress-ready Superset app.
This app uses a different database schema (`superset_cypress`) to keep it isolated from
your other dev environmen(s)t, a specific set of examples, and a set of configurations that
aligns with the expectations within the end-to-end tests. Also note that it's served on a
different port than the default port for the backend (`8088`).
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
cd superset-frontend
npm run build-instrumented
cd cypress-base
# superset-frontend/cypress-base is the base folder for everything Cypress-related
# It's essentially its own npm app, with its own dependencies, configurations and utilities
cd superset-frontend/cypress-base
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
# 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
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).

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.).
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.

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
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
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.
:::

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.
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
bootstrapScript: |
#!/bin/bash
pip install psycopg2==2.9.6 \
sqlalchemy-bigquery==1.6.1 \
elasticsearch-dbapi==0.2.5 &&\
pip install .[postgres] \
.[bigquery] \
.[elasticsearch] &&\
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
```
- 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.
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",
"@superset-ui/style": "^0.14.23",
"@svgr/webpack": "^8.1.0",
"antd": "^5.22.5",
"antd": "^5.22.7",
"buffer": "^6.0.3",
"clsx": "^2.1.1",
"docusaurus-plugin-less": "^2.0.2",
"file-loader": "^6.2.0",
"less": "^4.2.1",
"less": "^4.2.2",
"less-loader": "^11.0.0",
"prism-react-renderer": "^2.4.1",
"react": "^18.3.1",

View File

@ -137,4 +137,9 @@ export const Databases = [
href: 'https://www.denodo.com/',
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"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
antd@^5.22.5:
version "5.22.5"
resolved "https://registry.yarnpkg.com/antd/-/antd-5.22.5.tgz#e959381faca86c984cc853a0ab5cb3f140178336"
integrity sha512-+0UP8w+ULVv2OIzCDVz7j6I0UfH6mMLHSWO6qzpBc+9psOoVQLRbyAE21XnZM/eGrt2MNsEDL5fmlhXL/V8JyQ==
antd@^5.22.7:
version "5.22.7"
resolved "https://registry.yarnpkg.com/antd/-/antd-5.22.7.tgz#90a0eb3e4cd65b7fc042318132d34b7c492783dd"
integrity sha512-koT5QMliDgXc21yNcs4Uyuq6TeB5AJbzGZ2qjNExzE7Tjr8yYIX6sJsQfunsEV80wC1mpF7m9ldKuNj+PafcFA==
dependencies:
"@ant-design/colors" "^7.1.0"
"@ant-design/cssinjs" "^1.21.1"
@ -3487,13 +3487,13 @@ antd@^5.22.5:
rc-input-number "~9.3.0"
rc-mentions "~2.17.0"
rc-menu "~9.16.0"
rc-motion "^2.9.4"
rc-motion "^2.9.5"
rc-notification "~5.6.2"
rc-pagination "~5.0.0"
rc-picker "~4.8.3"
rc-progress "~4.0.0"
rc-rate "~2.13.0"
rc-resize-observer "^1.4.1"
rc-resize-observer "^1.4.3"
rc-segmented "~2.5.0"
rc-select "~14.16.4"
rc-slider "~11.1.7"
@ -3506,7 +3506,7 @@ antd@^5.22.5:
rc-tree "~5.10.1"
rc-tree-select "~5.24.5"
rc-upload "~4.8.1"
rc-util "^5.44.2"
rc-util "^5.44.3"
scroll-into-view-if-needed "^3.1.0"
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"
integrity sha512-6/GrYaB6QcW6Vj+/9ZPgKKs6G10YZai/l/eJ4SLwbzqNTBsAqt5hSLVF47TgsiBxV1P6eAU0GYRH3YRuQU9V3A==
less@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/less/-/less-4.2.1.tgz#fe4c9848525ab44614c0cf2c00abd8d031bb619a"
integrity sha512-CasaJidTIhWmjcqv0Uj5vccMI7pJgfD9lMkKtlnTHAdJdYK/7l8pM9tumLyJ0zhbD4KJLo/YvTj+xznQd5NBhg==
less@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/less/-/less-4.2.2.tgz#4b59ede113933b58ab152190edf9180fc36846d8"
integrity sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==
dependencies:
copy-anything "^2.0.1"
parse-node-version "^1.0.1"
@ -8391,7 +8391,7 @@ rc-menu@~9.16.0:
rc-overflow "^1.3.1"
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"
resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c"
integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==
@ -8459,7 +8459,7 @@ rc-rate@~2.13.0:
classnames "^2.2.5"
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"
resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz#4fd41fa561ba51362b5155a07c35d7c89a1ea569"
integrity sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==
@ -8595,10 +8595,10 @@ rc-upload@~4.8.1:
classnames "^2.2.5"
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:
version "5.44.2"
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.2.tgz#6bc5db0e96ebdb515eb5977a7371887e5413a6f8"
integrity sha512-uGSk3hpPBLa3/0QAcKhCjgl4SFnhQCJDLvvpoLdbR6KgDuXrujG+dQaUeUvBJr2ZWak1O/9n+cYbJiWmmk95EQ==
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.3"
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.3.tgz#9eca5039906446113c4032859f88c15234547961"
integrity sha512-q6KCcOFk3rv/zD3MckhJteZxb0VjAIFuf622B7ElK4vfrZdAzs16XR5p3VTdy3+U5jfJU5ACz4QnhLSuAGe5dA==
dependencies:
"@babel/runtime" "^7.18.3"
react-is "^18.2.0"

View File

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

View File

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

View File

@ -23,7 +23,7 @@ NOTE: This file is generated by helm-docs: https://github.com/norwoodj/helm-docs
# 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
@ -50,8 +50,8 @@ On helm this can be set on `extraSecretEnv.SUPERSET_SECRET_KEY` or `configOverri
| Repository | Name | Version |
|------------|------|---------|
| https://charts.bitnami.com/bitnami | postgresql | 12.1.6 |
| https://charts.bitnami.com/bitnami | redis | 17.9.4 |
| oci://registry-1.docker.io/bitnamicharts | postgresql | 12.1.6 |
| oci://registry-1.docker.io/bitnamicharts | redis | 17.9.4 |
## 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. |
| envFromSecrets | list | `[]` | This can be a list of templated strings |
| 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 |
| extraEnvRaw | list | `[]` | Extra environment variables in RAW format that will be passed into pods |
| extraLabels | object | `{}` | Labels to be added to all resources |
| 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 | `[]` | |
| extraVolumes | list | `[]` | |
| 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
# 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: {}
# import_datasources.yaml: |
# databases:
@ -120,7 +120,7 @@ extraConfigs: {}
# sqlalchemy_uri: example://example-db.local
# tables: []
# -- Extra files to mount on `/app/pythonpath` as secrets
# -- Extra files to be mounted as Secrets on the path specified in `configMountPath`
extraSecrets: {}
extraVolumes: []
@ -277,7 +277,7 @@ supersetNode:
- ". {{ .Values.configMountPath }}/superset_bootstrap.sh; /usr/bin/run-server.sh"
connections:
# -- 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_user: ""
# redis_password: superset
@ -289,7 +289,7 @@ supersetNode:
enabled: false
ssl_cert_reqs: CERT_NONE
# 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_user: superset
db_pass: superset

View File

@ -24,13 +24,12 @@ name = "apache-superset"
description = "A modern, enterprise-ready business intelligence web application"
readme = "README.md"
dynamic = ["version", "scripts", "entry-points"]
requires-python = ">=3.9"
requires-python = ">=3.10"
license = { file="LICENSE.txt" }
authors = [
{ name = "Apache Software Foundation", email = "dev@superset.apache.org" },
]
classifiers = [
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
]
@ -45,7 +44,7 @@ dependencies = [
"cryptography>=42.0.4, <44.0.0",
"deprecation>=2.1.0, <2.2.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-compress>=1.13, <2.0",
"flask-talisman>=1.0.0, <2.0",
@ -67,7 +66,7 @@ dependencies = [
"markdown>=3.0",
"msgpack>=1.0.0, <1.1",
"nh3>=0.2.11, <0.3",
"numpy==1.23.5",
"numpy>1.23.5, <2",
"packaging",
# --------------------------
# 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",
"PyJWT>=2.4.0, <3.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",
"shortid",
"sshtunnel>=0.4.0, <0.5",
@ -94,9 +93,7 @@ dependencies = [
"slack_sdk>=3.19.0, <4",
"sqlalchemy>=1.4, <2",
"sqlalchemy-utils>=0.38.3, <0.39",
# known breaking changes in sqlglot 25.25.0
#https://github.com/tobymao/sqlglot/blob/main/CHANGELOG.md#v25250---2024-10-14
"sqlglot>=25.24.0,<25.25.0",
"sqlglot>=26.1.3, <27",
"sqlparse>=0.5.0",
"tabulate>=0.8.9, <0.9",
"typing-extensions>=4, <5",
@ -159,6 +156,7 @@ ocient = [
"geojson",
]
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"]
playwright = ["playwright>=1.37.0, <2"]
postgres = ["psycopg2-binary==2.9.6"]
@ -175,6 +173,10 @@ spark = [
"tableschema",
"thrift>=0.14.1, <1",
]
tdengine = [
"taospy>=2.7.21",
"taos-ws-py>=0.3.8"
]
teradata = ["teradatasql>=16.20.0.23"]
thumbnails = ["Pillow>=10.0.1, <11"]
vertica = ["sqlalchemy-vertica-python>=0.5.9, < 0.6"]
@ -275,8 +277,8 @@ exclude = [
line-length = 88
indent-width = 4
# Assume Python 3.9
target-version = "py39"
# Assume Python 3.10
target-version = "py310"
[tool.ruff.lint]
# 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
# -> https://github.com/aio-libs/async-timeout/blob/master/CHANGES.rst#500-2024-10-31
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
# via shillelagh
async-timeout==4.0.3
# via
# -r requirements/base.in
# redis
# via -r requirements/base.in
attrs==24.2.0
# via
# cattrs
# jsonschema
# outcome
# requests-cache
# trio
babel==2.16.0
# via flask-babel
backoff==2.2.1
@ -42,7 +42,9 @@ cattrs==24.1.2
celery==5.4.0
# via apache-superset (pyproject.toml)
certifi==2024.8.30
# via requests
# via
# requests
# selenium
cffi==1.17.1
# via
# cryptography
@ -92,8 +94,6 @@ email-validator==2.2.0
# via flask-appbuilder
et-xmlfile==2.0.0
# via openpyxl
exceptiongroup==1.2.2
# via cattrs
flask==2.3.3
# via
# apache-superset (pyproject.toml)
@ -108,7 +108,7 @@ flask==2.3.3
# flask-session
# flask-sqlalchemy
# flask-wtf
flask-appbuilder==4.5.2
flask-appbuilder==4.5.3
# via apache-superset (pyproject.toml)
flask-babel==2.0.0
# via flask-appbuilder
@ -146,11 +146,12 @@ google-auth==2.36.0
# via shillelagh
greenlet==3.0.3
# via
# -r requirements/base.in
# apache-superset (pyproject.toml)
# shillelagh
gunicorn==23.0.0
# via apache-superset (pyproject.toml)
h11==0.14.0
# via wsproto
hashids==1.3.1
# via apache-superset (pyproject.toml)
holidays==0.25
@ -161,6 +162,7 @@ idna==3.10
# via
# email-validator
# requests
# trio
importlib-metadata==8.5.0
# via apache-superset (pyproject.toml)
importlib-resources==6.4.5
@ -215,7 +217,7 @@ nh3==0.2.19
# via apache-superset (pyproject.toml)
numexpr==2.10.2
# via -r requirements/base.in
numpy==1.23.5
numpy==1.26.4
# via
# apache-superset (pyproject.toml)
# bottleneck
@ -228,6 +230,8 @@ openpyxl==3.1.5
# via pandas
ordered-set==4.1.0
# via flask-limiter
outcome==1.3.0.post0
# via trio
packaging==24.2
# via
# apache-superset (pyproject.toml)
@ -283,6 +287,8 @@ pyparsing==3.2.0
# via apache-superset (pyproject.toml)
pyrsistent==0.20.0
# via jsonschema
pysocks==1.7.1
# via urllib3
python-dateutil==2.9.0.post0
# via
# apache-superset (pyproject.toml)
@ -319,7 +325,7 @@ rich==13.9.4
# via flask-limiter
rsa==4.9
# via google-auth
selenium==3.141.0
selenium==4.27.1
# via apache-superset (pyproject.toml)
shillelagh==1.2.18
# via apache-superset (pyproject.toml)
@ -335,6 +341,10 @@ six==1.16.0
# wtforms-json
slack-sdk==3.33.4
# via apache-superset (pyproject.toml)
sniffio==1.3.1
# via trio
sortedcontainers==2.4.0
# via trio
sqlalchemy==1.4.54
# via
# apache-superset (pyproject.toml)
@ -348,7 +358,7 @@ sqlalchemy-utils==0.38.3
# via
# apache-superset (pyproject.toml)
# flask-appbuilder
sqlglot==25.24.5
sqlglot==26.1.3
# via apache-superset (pyproject.toml)
sqlparse==0.5.2
# via apache-superset (pyproject.toml)
@ -356,14 +366,19 @@ sshtunnel==0.4.0
# via apache-superset (pyproject.toml)
tabulate==0.8.10
# 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
# via
# apache-superset (pyproject.toml)
# alembic
# cattrs
# flask-limiter
# limits
# rich
# selenium
# shillelagh
tzdata==2024.2
# via
@ -385,6 +400,8 @@ vine==5.1.0
# kombu
wcwidth==0.2.13
# via prompt-toolkit
websocket-client==1.8.0
# via selenium
werkzeug==3.1.3
# via
# -r requirements/base.in
@ -394,6 +411,8 @@ werkzeug==3.1.3
# flask-login
wrapt==1.17.0
# via deprecated
wsproto==1.2.0
# via trio-websocket
wtforms==3.2.1
# via
# apache-superset (pyproject.toml)

View File

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

View File

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

View File

@ -20,7 +20,7 @@ import os
import subprocess
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"
GITHUB_EVENT_NAME = os.getenv("GITHUB_EVENT_NAME") or "push"
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}"
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"--ci-build-id {build_id} "
f"-- {chrome_flags}"
@ -64,7 +66,9 @@ def run_cypress_for_test_file(
os.environ.pop("CYPRESS_RECORD_KEY", None)
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"-- {chrome_flags}"
)

View File

@ -193,6 +193,12 @@ module.exports = {
message:
'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: {
@ -325,7 +331,7 @@ module.exports = {
'no-prototype-builtins': 0,
'no-restricted-properties': 0,
'no-restricted-imports': [
'warn',
'error',
{
paths: [
{
@ -333,6 +339,11 @@ module.exports = {
message:
'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',
importNames: ['supersetTheme'],

View File

@ -39,7 +39,6 @@ module.exports = {
{
development: process.env.BABEL_ENV === 'development',
runtime: 'automatic',
importSource: '@emotion/react',
},
],
'@babel/preset-typescript',
@ -74,12 +73,23 @@ module.exports = {
corejs: 3,
loose: true,
shippedProposals: true,
modules: 'commonjs',
modules: 'auto',
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
instrumented: {

View File

@ -26,8 +26,9 @@ export default eyesPlugin(
defineConfig({
chromeWebSecurity: false,
defaultCommandTimeout: 8000,
numTestsKeptInMemory: 0,
experimentalFetchPolyfill: true,
numTestsKeptInMemory: 3,
// Disabled after realizing this MESSES UP rison encoding in intricate ways
experimentalFetchPolyfill: false,
experimentalMemoryManagement: true,
requestTimeout: 10000,
video: false,
@ -57,11 +58,20 @@ export default eyesPlugin(
});
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;
});
// eslint-disable-next-line global-require
require('@cypress/code-coverage/task')(on, config);
on('task', verifyDownloadTasks);
@ -70,6 +80,7 @@ export default eyesPlugin(
},
baseUrl: 'http://localhost:8088',
excludeSpecPattern: [],
experimentalRunAllSpecs: true,
specPattern: [
'cypress/e2e/**/*.{js,jsx,ts,tsx}',
'cypress/applitools/**/*.{js,jsx,ts,tsx}',

View File

@ -31,7 +31,10 @@ describe('explore view', () => {
});
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] };
cy.visitChartByParams(formData);
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });

View File

@ -26,6 +26,7 @@ import {
visitSampleChartFromList,
saveChartToDashboard,
interceptFiltering,
interceptFavoriteStatus,
} from '../explore/utils';
import { interceptGet as interceptDashboardGet } from '../dashboard/utils';
@ -49,8 +50,10 @@ function confirmDelete() {
function visitChartList() {
interceptFiltering();
interceptFavoriteStatus();
cy.visit(CHART_LIST);
cy.wait('@filtering');
cy.wait('@favoriteStatus');
}
describe('Charts list', () => {
@ -78,20 +81,15 @@ describe('Charts list', () => {
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();
visitSampleChartFromList('1 - Sample chart');
saveChartToDashboard('1 - Sample dashboard');
saveChartToDashboard('2 - Sample dashboard');
saveChartToDashboard('3 - Sample dashboard');
visitChartList();
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', () => {
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('table-row').first().contains("World's Population");
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.
cy.get('[data-test="refresh-chart-menu-item"]').should(
'have.class',
'ant-dropdown-menu-item-disabled',
'antd5-dropdown-menu-item-disabled',
);
waitForChartLoad(mapSpec);
cy.get('[data-test="refresh-chart-menu-item"]').should(
'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('[data-test="refresh-dashboard-menu-item"]').should(
'not.have.class',
'ant-dropdown-menu-item-disabled',
'antd5-dropdown-menu-item-disabled',
);
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(
'have.class',
'ant-dropdown-menu-item-disabled',
'antd5-dropdown-menu-item-disabled',
);
// wait all charts force refreshed.
@ -94,7 +94,7 @@ describe.skip('Dashboard top-level controls', () => {
cy.get('[aria-label="more-horiz"]').click();
cy.get('[data-test="refresh-dashboard-menu-item"]').and(
'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();
}
cy.get('.ant-dropdown:not(.ant-dropdown-hidden)')
.first()
.find("[role='menu'] [role='menuitem'] [title='Drill by']")
cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
.should('be.visible')
.find("[role='menu'] [role='menuitem']")
.contains(/^Drill by$/)
.trigger('mouseover', { force: true });
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"]')
.contains(new RegExp(`^${targetDrillByColumn}$`))
.first()
.click({ force: true });
.then($el => {
cy.wrap($el)
.contains(new RegExp(`^${targetDrillByColumn}$`))
.trigger('keydown', { keyCode: 13, which: 13, force: true });
});
if (isLegacy) {
return cy.wait('@legacyData');
@ -524,8 +529,8 @@ describe('Drill by modal', () => {
]);
});
it('Bar Chart V2', () => {
testEchart('echarts_timeseries_bar', 'Bar Chart V2', [
it('Bar Chart', () => {
testEchart('echarts_timeseries_bar', 'Bar Chart', [
[70, 94],
[362, 68],
]);
@ -593,7 +598,7 @@ describe('Drill by modal', () => {
]);
});
it('Radar Chart', () => {
it.skip('Radar Chart', () => {
testEchart('radar', 'Radar Chart', [
[182, 49],
[423, 91],

View File

@ -34,8 +34,8 @@ function openModalFromMenu(chartType: string) {
cy.get(
`[data-test-viz-type='${chartType}'] [aria-label='More Options']`,
).click();
cy.get('.ant-dropdown')
.not('.ant-dropdown-hidden')
cy.get('.antd5-dropdown')
.not('.antd5-dropdown-hidden')
.find("[role='menu'] [role='menuitem']")
.eq(5)
.should('contain', 'Drill to detail')
@ -43,37 +43,45 @@ function openModalFromMenu(chartType: string) {
cy.wait('@samples');
}
function openModalFromChartContext(targetMenuItem: string) {
function drillToDetail(targetMenuItem: string) {
interceptSamples();
if (targetMenuItem.startsWith('Drill to detail by')) {
cy.get('.ant-dropdown')
.not('.ant-dropdown-hidden')
.should('be.visible')
.first()
.find("[role='menu'] [role='menuitem'] [title='Drill to detail by']")
.trigger('mouseover');
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.get('.antd5-dropdown')
.not('.antd5-dropdown-hidden')
.first()
.find("[role='menu'] [role='menuitem']")
.contains(new RegExp(`^${targetMenuItem}$`))
.first()
.trigger('keydown', { keyCode: 13, which: 13, force: true });
cy.getBySel('metadata-bar').should('be.visible');
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() {
cy.get('body').then($body => {
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).rightclick(70, 93);
openModalFromChartContext('Drill to detail by 1965');
drillToDetailBy('Drill to detail by 1965');
cy.getBySel('filter-val').should('contain', '1965');
closeModal();
@ -98,7 +106,7 @@ function testTimeChart(vizType: string) {
cy.wrap($canvas).trigger('mousemove', 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');
closeModal();
@ -106,24 +114,10 @@ function testTimeChart(vizType: string) {
cy.wrap($canvas).trigger('mousemove', 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').eq(1).should('contain', 'boy');
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",
).rightclick();
openModalFromChartContext('Drill to detail');
drillToDetail('Drill to detail');
cy.getBySel('filter-val').should('not.exist');
});
@ -224,7 +218,7 @@ describe('Drill to detail modal', () => {
).scrollIntoView();
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');
@ -236,7 +230,7 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).trigger('mousemove', 1, 14);
cy.wrap($canvas).rightclick(1, 14);
openModalFromChartContext('Drill to detail by 1965');
drillToDetailBy('Drill to detail by 1965');
// checking the filter
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').rightclick();
openModalFromChartContext('Drill to detail by boy');
drillToDetailBy('Drill to detail by 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']").contains('girl').rightclick();
openModalFromChartContext('Drill to detail by girl');
drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl');
});
@ -283,7 +277,7 @@ describe('Drill to detail modal', () => {
.first()
.rightclick();
openModalFromChartContext('Drill to detail by boy');
drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy');
closeModal();
@ -294,7 +288,7 @@ describe('Drill to detail modal', () => {
.first()
.rightclick();
openModalFromChartContext('Drill to detail by CA');
drillToDetailBy('Drill to detail by CA');
cy.getBySel('filter-val').should('contain', 'CA');
closeModal();
@ -305,7 +299,7 @@ describe('Drill to detail modal', () => {
.eq(3)
.rightclick();
openModalFromChartContext('Drill to detail by girl');
drillToDetailBy('Drill to detail by girl');
cy.getBySel('filter-val').should('contain', 'girl');
closeModal();
@ -316,7 +310,7 @@ describe('Drill to detail modal', () => {
.eq(3)
.rightclick();
openModalFromChartContext('Drill to detail by FL');
drillToDetailBy('Drill to detail by FL');
cy.getBySel('filter-val').should('contain', 'FL');
closeModal();
@ -327,7 +321,7 @@ describe('Drill to detail modal', () => {
.eq(3)
.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').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', () => {
interceptSamples();
@ -349,21 +343,21 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView();
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');
closeModal();
cy.wrap($canvas).scrollIntoView();
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');
closeModal();
cy.wrap($canvas).scrollIntoView();
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').eq(1).should('contain', 'boy');
closeModal();
@ -371,14 +365,14 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView();
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');
},
);
});
});
describe('Area Chart', () => {
describe.skip('Area Chart', () => {
it('opens the modal with the correct filters', () => {
testTimeChart('echarts_area');
});
@ -399,51 +393,38 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView();
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');
closeModal();
cy.wrap($canvas).scrollIntoView();
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');
});
});
});
describe('World Map', () => {
describe.skip('World Map', () => {
it('opens the modal with the correct filters', () => {
interceptSamples();
cy.get("[data-test-viz-type='world_map'] svg").then($canvas => {
cy.wrap($canvas).scrollIntoView();
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');
closeModal();
});
cy.get("[data-test-viz-type='world_map'] svg").then($canvas => {
cy.wrap($canvas).scrollIntoView();
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');
});
});
});
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', () => {
@ -469,7 +450,7 @@ describe('Drill to detail modal', () => {
force: true,
});
openModalFromChartContext('Drill to detail by boy');
drillToDetailBy('Drill to detail by boy');
// checking the filter
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).rightclick(135, 275);
openModalFromChartContext('Drill to detail by boy');
drillToDetailBy('Drill to detail by boy');
cy.getBySel('filter-val').should('contain', 'boy');
closeModal();
@ -513,7 +494,7 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).trigger('mousemove', 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');
});
});
@ -545,14 +526,14 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView();
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');
closeModal();
cy.wrap($canvas).scrollIntoView();
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');
});
});
@ -566,14 +547,14 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView();
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');
closeModal();
cy.wrap($canvas).scrollIntoView();
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');
});
});
@ -585,7 +566,7 @@ describe('Drill to detail modal', () => {
});
});
describe('Radar Chart', () => {
describe.skip('Radar Chart', () => {
it('opens the modal with the correct filters', () => {
interceptSamples();
@ -593,14 +574,14 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView();
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');
closeModal();
cy.wrap($canvas).scrollIntoView();
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');
});
});
@ -614,14 +595,14 @@ describe('Drill to detail modal', () => {
cy.wrap($canvas).scrollIntoView();
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');
closeModal();
cy.wrap($canvas).scrollIntoView();
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');
});
});

View File

@ -16,13 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
import {
SAMPLE_DASHBOARD_1,
SUPPORTED_CHARTS_DASHBOARD,
TABBED_DASHBOARD,
} from 'cypress/utils/urls';
import { SAMPLE_DASHBOARD_1, TABBED_DASHBOARD } from 'cypress/utils/urls';
import { drag, resize, waitForChartLoad } from 'cypress/utils';
import * as ace from 'brace';
import { edit } from 'brace';
import {
interceptExploreUpdate,
interceptGet,
@ -30,7 +26,7 @@ import {
openTab,
} from './utils';
import {
interceptExploreJson,
interceptV1ChartData,
interceptFiltering as interceptCharts,
} 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) {
const regex = new RegExp(text);
cy.get('#json_metadata')
@ -72,7 +60,7 @@ function assertMetadata(text: string) {
// cypress can read this locally, but not in ci
// 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) {
interceptExploreJson();
interceptV1ChartData();
interceptGet();
cy.get(
`[data-test-chart-name='${chartName}'] [aria-label='More Options']`,
).click();
cy.get('.ant-dropdown')
.not('.ant-dropdown-hidden')
.find("[role='menu'] [role='menuitem']")
.eq(2)
.should('contain', 'Edit chart')
.click();
cy.wait('@getJson');
cy.get(`[data-test-edit-chart-name='${chartName}']`)
.should('be.visible')
.trigger('keydown', {
keyCode: 13,
which: 13,
force: true,
});
cy.wait('@v1Data');
cy.get('.chart-container').should('exist');
}
@ -255,13 +244,16 @@ function saveExploreColorScheme(
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('Color consistency', () => {
beforeEach(() => {
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);
openProperties();
selectColorScheme('blueToGreen');
@ -272,11 +264,14 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
openExploreWithDashboardContext('Top 10 California Names Timeseries');
// hover over canvas elements
cy.get('canvas').trigger('mouseover', { force: true });
// label Anthony
cy.get('[data-test="chart-container"] .line .nv-legend-symbol')
.first()
@ -287,21 +282,21 @@ describe('Dashboard edit', () => {
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);
// open nested tab
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// open second top tab to catch shared labels
openTab(0, 1);
waitForChartLoad({
name: 'Trends',
viz: 'line',
viz: 'echarts_timeseries_line',
});
openTab(0, 0);
@ -317,7 +312,7 @@ describe('Dashboard edit', () => {
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);
openProperties();
openAdvancedProperties();
@ -330,7 +325,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -371,14 +366,14 @@ describe('Dashboard edit', () => {
.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);
// open nested tab
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -414,7 +409,7 @@ describe('Dashboard edit', () => {
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);
openProperties();
openAdvancedProperties();
@ -427,7 +422,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -473,14 +468,14 @@ describe('Dashboard edit', () => {
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);
// open nested tab
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -492,7 +487,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab
openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' });
waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Andrew
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -505,7 +500,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -517,7 +512,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab
openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' });
waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Andrew
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)');
});
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);
openProperties();
selectColorScheme('blueToGreen');
@ -536,7 +531,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -548,7 +543,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab
openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' });
waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Andrew
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -561,7 +556,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -573,7 +568,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab
openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' });
waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Andrew
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)');
});
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);
// open nested tab
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -599,7 +594,7 @@ describe('Dashboard edit', () => {
.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);
openProperties();
selectColorScheme('blueToGreen');
@ -610,7 +605,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -622,7 +617,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab
openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' });
waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Anthony
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -634,7 +629,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -646,7 +641,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab
openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' });
waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Anthony
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)');
});
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);
// open nested tab
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -673,7 +668,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab
openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' });
waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Anthony
cy.get('[data-test-chart-name="Trends"] .line .nv-legend-symbol')
@ -686,7 +681,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -698,7 +693,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab
openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' });
waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Anthony
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)');
});
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);
openProperties();
openAdvancedProperties();
@ -721,7 +716,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
cy.get(
'[data-test-chart-name="Top 10 California Names Timeseries"] .line .nv-legend-symbol',
@ -731,19 +726,19 @@ describe('Dashboard edit', () => {
// open another nested tab
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')
.first()
.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);
// open the tab first time and let chart load
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// go to previous tab
@ -766,7 +761,7 @@ describe('Dashboard edit', () => {
.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);
openProperties();
openAdvancedProperties();
@ -809,13 +804,13 @@ describe('Dashboard edit', () => {
.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);
// open nested tab
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
cy.get(
'[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)');
});
it('should show the same colors in Explore', () => {
it.skip('should show the same colors in Explore', () => {
visitEdit(TABBED_DASHBOARD);
openProperties();
openAdvancedProperties();
@ -888,7 +883,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -906,7 +901,7 @@ describe('Dashboard edit', () => {
.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);
openProperties();
selectColorScheme('blueToGreen');
@ -917,7 +912,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// label Anthony
@ -929,7 +924,7 @@ describe('Dashboard edit', () => {
// open 2nd main tab
openTab(0, 1);
waitForChartLoad({ name: 'Trends', viz: 'line' });
waitForChartLoad({ name: 'Trends', viz: 'echarts_timeseries_line' });
// label Anthony
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)');
});
it('should apply the color scheme across main tabs', () => {
it.skip('should apply the color scheme across main tabs', () => {
visitEdit(TABBED_DASHBOARD);
openProperties();
selectColorScheme('blueToGreen');
@ -968,14 +963,14 @@ describe('Dashboard edit', () => {
// go to second tab
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')
.first()
.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);
waitForChartLoad({ name: 'Treemap', viz: 'treemap_v2' });
openProperties();
@ -985,7 +980,7 @@ describe('Dashboard edit', () => {
// go to second tab
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')
.first()
@ -1003,7 +998,7 @@ describe('Dashboard edit', () => {
.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);
openProperties();
selectColorScheme('blueToGreen');
@ -1014,7 +1009,7 @@ describe('Dashboard edit', () => {
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
cy.get(
'[data-test-chart-name="Top 10 California Names Timeseries"] .line .nv-legend-symbol',
@ -1024,19 +1019,19 @@ describe('Dashboard edit', () => {
// open another nested tab
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')
.first()
.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);
// open the tab first time and let chart load
openTab(1, 1);
waitForChartLoad({
name: 'Top 10 California Names Timeseries',
viz: 'line',
viz: 'echarts_timeseries_line',
});
// go to previous tab

View File

@ -57,16 +57,16 @@ function setFilterBarOrientation(orientation: 'vertical' | 'horizontal') {
.trigger('mouseover');
if (orientation === 'vertical') {
cy.get('.ant-dropdown-menu-item-selected')
cy.get('.antd5-dropdown-menu-item-selected')
.contains('Horizontal (Top)')
.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');
} else {
cy.get('.ant-dropdown-menu-item-selected')
cy.get('.antd5-dropdown-menu-item-selected')
.contains('Vertical (Left)')
.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('filter-bar').should('exist');
cy.getBySel('dashboard-filters-panel').should('not.exist');
@ -176,7 +176,7 @@ describe('Horizontal FilterBar', () => {
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');
prepareDashboardFilters([
{ 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([
{ name: 'test_1', column: 'country_name', 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');
});
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([
{ 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';
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 BIG_NUMBER = { name: 'Number of Girls', viz: 'big_number_total' };
const TABLE = { name: 'Names Sorted by Num in California', viz: 'table' };
@ -64,9 +64,8 @@ describe('Dashboard tabs', () => {
cy.get('@top-level-tabs')
.last()
.should('not.have.class', 'ant-tabs-tab-active');
cy.getBySel('grid-container').find('.box_plot').should('not.exist');
cy.getBySel('grid-container').find('.line').should('not.exist');
cy.get('[data-test-chart-name="Box plot"]').should('not.exist');
cy.get('[data-test-chart-name="Trends"]').should('not.exist');
cy.get('@top-level-tabs').last().click();
cy.get('@top-level-tabs')
@ -76,7 +75,8 @@ describe('Dashboard tabs', () => {
.first()
.should('not.have.class', 'ant-tabs-tab-active');
waitForChartLoad(BOX_PLOT);
cy.getBySel('grid-container').find('.box_plot').should('be.visible');
cy.get('[data-test-chart-name="Box plot"]').should('exist');
resetTabs();
@ -88,7 +88,7 @@ describe('Dashboard tabs', () => {
cy.get('@row-level-tabs').last().click();
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();
});

View File

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

View File

@ -40,9 +40,25 @@ function openMenu() {
cy.get('[aria-label="more-vert"]').first().click();
}
function confirmDelete() {
cy.getBySel('delete-modal-input').type('DELETE');
cy.getBySel('modal-confirm-button').click();
function confirmDelete(bulk = false) {
interceptDelete();
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', () => {
@ -157,7 +173,6 @@ describe('Dashboards list', () => {
});
it('should bulk delete correctly', () => {
interceptBulkDelete();
toggleBulkSelect();
// 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(1).contains('2 - Sample dashboard').click();
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
confirmDelete();
cy.wait('@bulkDelete');
confirmDelete(true);
cy.getBySel('styled-card')
.eq(0)
.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(1).click();
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
confirmDelete();
cy.wait('@bulkDelete');
confirmDelete(true);
cy.getBySel('table-row')
.eq(0)
.should('not.contain', '3 - Sample dashboard');
@ -193,31 +206,36 @@ describe('Dashboards list', () => {
.should('not.contain', '4 - Sample dashboard');
});
it('should delete correctly', () => {
interceptDelete();
it.skip('should delete correctly in list mode', () => {
// 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
setGridMode('card');
orderAlphabetical();
cy.getBySel('styled-card').eq(0).contains('1 - Sample dashboard');
cy.getBySel('styled-card')
.eq(0)
.contains('1 - Sample dashboard')
.should('exist');
openMenu();
cy.getBySel('dashboard-card-option-delete-button').click();
confirmDelete();
cy.wait('@delete');
cy.getBySel('styled-card')
.eq(0)
.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', () => {

View File

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

View File

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

View File

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

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

View File

@ -20,13 +20,12 @@
// Tests for setting controls in the UI
// ***********************************************
import { interceptChart } from 'cypress/utils';
import { FORM_DATA_DEFAULTS, NUM_METRIC } from './visualizations/shared.helper';
describe('Datasource control', () => {
const newMetricName = `abc${Date.now()}`;
it('should allow edit dataset', () => {
interceptChart({ legacy: true }).as('chartData');
interceptChart({ legacy: false }).as('chartData');
cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@chartData' });
@ -52,8 +51,8 @@ describe('Datasource control', () => {
)
.first()
.focus();
cy.focused().clear();
cy.focused().type(`${newMetricName}{enter}`);
cy.focused().clear({ force: true });
cy.focused().type(`${newMetricName}{enter}`, { force: true });
cy.get('[data-test="datasource-modal-save"]').click();
cy.get('.antd5-modal-confirm-btns button').contains('OK').click();
@ -85,7 +84,7 @@ describe('Datasource control', () => {
describe('Color scheme control', () => {
beforeEach(() => {
interceptChart({ legacy: true }).as('chartData');
interceptChart({ legacy: false }).as('chartData');
cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@chartData' });
@ -137,7 +136,7 @@ describe('VizType control', () => {
describe('Test datatable', () => {
beforeEach(() => {
interceptChart({ legacy: false }).as('tableChartData');
interceptChart({ legacy: true }).as('lineChartData');
interceptChart({ legacy: false }).as('lineChartData');
cy.visitChartByName('Daily Totals');
});
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', () => {
it('Set groupby', () => {
interceptChart({ legacy: true }).as('chartData');
interceptChart({ legacy: false }).as('chartData');
cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@chartData' });
@ -271,6 +172,6 @@ describe('Groupby control', () => {
cy.get('[data-test="ColumnEdit#save"]').contains('Save').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', () => {
beforeEach(() => {
interceptChart({ legacy: true }).as('chartData');
interceptChart({ legacy: false }).as('chartData');
});
it('Open and close view query modal', () => {
@ -52,11 +52,10 @@ describe('Test explore links', () => {
cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[aria-label="Menu actions trigger"]').click();
cy.get('div[title="Share"]').trigger('mouseover');
// need to use [id= syntax, otherwise error gets triggered because of special character in id
cy.get('[id="share_submenu$Menu"]').within(() => {
cy.contains('Embed code').parent().click();
cy.get('div[role="menuitem"]').within(() => {
cy.contains('Share').parent().click();
});
cy.getBySel('embed-code-button').click();
cy.get('#embed-code-popover').within(() => {
cy.get('textarea[name=embedCode]').contains('iframe');
});

View File

@ -31,6 +31,10 @@ export function interceptDelete() {
cy.intercept('DELETE', `/api/v1/chart/*`).as('delete');
}
export function interceptFavoriteStatus() {
cy.intercept('GET', '/api/v1/chart/favorite_status/*').as('favoriteStatus');
}
export function interceptUpdate() {
cy.intercept('PUT', `/api/v1/chart/*`).as('update');
}
@ -68,7 +72,10 @@ export function saveChartToDashboard(dashboardName: string) {
interceptUpdate();
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.get(
'[data-test="save-chart-modal-select-dashboard-form"] [aria-label="Select a dashboard"]',
@ -77,7 +84,7 @@ export function saveChartToDashboard(dashboardName: string) {
.click();
cy.get(
'.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.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';
describe('Download Chart > Distribution bar chart', () => {
const VIZ_DEFAULTS = { ...FORM_DATA_DEFAULTS, viz_type: 'dist_bar' };
describe('Download Chart > Bar chart', () => {
const VIZ_DEFAULTS = {
...FORM_DATA_DEFAULTS,
viz_type: 'echarts_timeseries_bar',
};
beforeEach(() => {
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
@ -33,10 +36,10 @@ describe('Download Chart > Distribution bar chart', () => {
};
cy.visitChartByParams(formData);
cy.get('.header-with-actions .ant-dropdown-trigger').click();
cy.get(':nth-child(3) > .ant-dropdown-menu-submenu-title').click();
cy.get('.header-with-actions .antd5-dropdown-trigger').click();
cy.get(':nth-child(3) > .antd5-dropdown-menu-submenu-title').click();
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();
cy.verifyDownload('.jpg', {
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 = {
datasource: '3__table',
granularity_sqla: 'ds',
time_grain_sqla: null,
time_range: '100 years ago : now',
adhoc_filters: [],
x_axis: 'ds',
adhoc_filters: [
{
clause: 'WHERE',
subject: 'ds',
operator: 'TEMPORAL_RANGE',
comparator: '100 years ago : now',
expressionType: 'SIMPLE',
},
],
groupby: [],
limit: null,
timeseries_limit_metric: null,
order_desc: false,
contribution: false,
contributionMode: null,
};
export const HEALTH_POP_FORM_DATA_DEFAULTS = {

View File

@ -80,7 +80,6 @@ describe('SqlLab query panel', () => {
it.skip('successfully saves a query', () => {
cy.intercept('api/v1/database/**/tables/**').as('getTables');
cy.intercept('savedqueryviewapi/**').as('getSavedQuery');
const query =
'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/explore/**').as('explore');
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
cy.window().then(win => {
@ -154,6 +154,7 @@ describe('SqlLab query panel', () => {
win.location.href = url;
});
});
cy.wait('@tabstateview');
const query = 'SELECT gender, name FROM birth_names';

View File

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

View File

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

View File

@ -19,6 +19,8 @@
import '@cypress/code-coverage/support';
import '@applitools/eyes-cypress/commands';
import failOnConsoleError from 'cypress-fail-on-console-error';
import { expect } from 'chai';
import rison from 'rison';
/* 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
// these exceptions are a little pile of tech debt
//
// DISABLING FOR NOW
/*
const { getConfig, setConfig } = failOnConsoleError({
consoleMessages: [
/\[webpack-dev-server\]/,
@ -34,7 +40,9 @@ const { getConfig, setConfig } = failOnConsoleError({
'Error: Unknown Error',
/Unable to infer path to ace from script src/,
],
includeConsoleTypes: ['error'],
});
*/
// Set individual tests to allow certain console errors to NOT fail, e.g
// cy.allowConsoleErrors(['foo', /^some bar-regex.*/]);
@ -160,13 +168,28 @@ Cypress.Commands.add('login', () => {
url: '/login/',
body: { username: 'admin', password: 'general' },
}).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 => {
cy.request(`/chart/api/read?_flt_3_slice_name=${name}`).then(response => {
cy.visit(`${BASE_EXPLORE_URL}{"slice_id": ${response.body.pks[0]}}`);
const query = rison.encode({
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",
"@superset-ui/core": "^2.1.0",
"brace": "^0.11.1",
"chai": "^4.5.0",
"cy-verify-downloads": "^0.2.5",
"cypress-fail-on-console-error": "^4.0.3",
"nanoid": "^5.0.9",
@ -4317,6 +4318,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
"license": "MIT",
"engines": {
"node": "*"
}
@ -4790,22 +4792,32 @@
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
},
"node_modules/chai": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
"integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz",
"integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==",
"license": "MIT",
"dependencies": {
"assertion-error": "^1.1.0",
"check-error": "^1.0.2",
"deep-eql": "^4.1.2",
"get-func-name": "^2.0.0",
"loupe": "^2.3.1",
"check-error": "^1.0.3",
"deep-eql": "^4.1.3",
"get-func-name": "^2.0.2",
"loupe": "^2.3.6",
"pathval": "^1.1.1",
"type-detect": "^4.0.5"
"type-detect": "^4.1.0"
},
"engines": {
"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": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
@ -4851,9 +4863,13 @@
}
},
"node_modules/check-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
"integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
"integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
"license": "MIT",
"dependencies": {
"get-func-name": "^2.0.2"
},
"engines": {
"node": "*"
}
@ -5401,9 +5417,10 @@
}
},
"node_modules/deep-eql": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
"integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz",
"integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==",
"license": "MIT",
"dependencies": {
"type-detect": "^4.0.0"
},
@ -6505,6 +6522,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
"integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
"license": "MIT",
"engines": {
"node": "*"
}
@ -7739,11 +7757,12 @@
}
},
"node_modules/loupe": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz",
"integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==",
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
"integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
"license": "MIT",
"dependencies": {
"get-func-name": "^2.0.0"
"get-func-name": "^2.0.1"
}
},
"node_modules/lowercase-keys": {
@ -8803,6 +8822,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
"integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
"license": "MIT",
"engines": {
"node": "*"
}
@ -9715,6 +9735,7 @@
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz",
"integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==",
"license": "(BSD-2-Clause OR WTFPL)",
"peerDependencies": {
"chai": "^4.0.0",
"sinon": ">=4.0.0"
@ -14560,17 +14581,24 @@
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
},
"chai": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
"integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz",
"integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==",
"requires": {
"assertion-error": "^1.1.0",
"check-error": "^1.0.2",
"deep-eql": "^4.1.2",
"get-func-name": "^2.0.0",
"loupe": "^2.3.1",
"check-error": "^1.0.3",
"deep-eql": "^4.1.3",
"get-func-name": "^2.0.2",
"loupe": "^2.3.6",
"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": {
@ -14607,9 +14635,12 @@
"integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="
},
"check-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
"integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA=="
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
"integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
"requires": {
"get-func-name": "^2.0.2"
}
},
"check-more-types": {
"version": "2.24.0",
@ -15026,9 +15057,9 @@
}
},
"deep-eql": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
"integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz",
"integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==",
"requires": {
"type-detect": "^4.0.0"
}
@ -16786,11 +16817,11 @@
}
},
"loupe": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz",
"integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==",
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
"integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
"requires": {
"get-func-name": "^2.0.0"
"get-func-name": "^2.0.1"
}
},
"lowercase-keys": {

View File

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

View File

@ -35,6 +35,7 @@ module.exports = {
testEnvironment: 'jsdom',
modulePathIgnorePatterns: ['<rootDir>/packages/generator-superset'],
setupFilesAfterEnv: ['<rootDir>/spec/helpers/setup.ts'],
snapshotSerializers: ['@emotion/jest/serializer'],
testEnvironmentOptions: {
url: 'http://localhost',
},
@ -53,11 +54,14 @@ module.exports = {
'dist/',
],
coverageReporters: ['lcov', 'json-summary', 'html', 'text'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
snapshotSerializers: ['@emotion/jest/enzyme-serializer'],
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: {
__DEV__: 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-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-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-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-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-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-preset-chart-deckgl": "file:./plugins/legacy-preset-chart-deckgl",
"@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-handlebars": "file:./plugins/plugin-chart-handlebars",
"@superset-ui/plugin-chart-pivot-table": "file:./plugins/plugin-chart-pivot-table",
@ -125,27 +121,34 @@
"@visx/scale": "^3.5.0",
"@visx/tooltip": "^3.0.0",
"@visx/xychart": "^3.5.1",
"abortcontroller-polyfill": "^1.7.5",
"abortcontroller-polyfill": "^1.7.8",
"ace-builds": "^1.36.3",
"ag-grid-community": "32.2.1",
"ag-grid-react": "32.2.1",
"antd": "4.10.3",
"antd-v5": "npm:antd@^5.18.0",
"babel-plugin-typescript-to-proptypes": "^2.0.0",
"bootstrap": "^3.4.1",
"brace": "^0.11.1",
"chrono-node": "^2.7.6",
"classnames": "^2.2.5",
"core-js": "^3.38.1",
"d3-color": "^3.1.0",
"d3-scale": "^2.1.2",
"dayjs": "^1.11.13",
"dom-to-image-more": "^3.2.0",
"dom-to-pdf": "^0.3.2",
"echarts": "^5.6.0",
"emotion-rgba": "0.0.12",
"fast-glob": "^3.3.2",
"fs-extra": "^11.2.0",
"fuse.js": "^7.0.0",
"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",
"html-webpack-plugin": "^5.3.2",
"immer": "^10.1.1",
"interweave": "^13.1.0",
"jquery": "^3.7.1",
@ -156,27 +159,26 @@
"lodash": "^4.17.21",
"luxon": "^3.5.0",
"mapbox-gl": "^2.10.0",
"markdown-to-jsx": "^7.4.7",
"markdown-to-jsx": "^7.7.2",
"match-sorter": "^6.3.4",
"memoize-one": "^5.2.1",
"moment": "^2.30.1",
"moment-timezone": "^0.5.44",
"mousetrap": "^1.6.5",
"mustache": "^4.2.0",
"nanoid": "^5.0.7",
"nanoid": "^5.0.9",
"ol": "^7.5.2",
"polished": "^4.3.1",
"prop-types": "^15.8.1",
"query-string": "^6.13.7",
"rc-trigger": "^5.3.4",
"re-resizable": "^6.10.1",
"react": "^16.13.1",
"react": "^17.0.2",
"react-ace": "^10.1.0",
"react-checkbox-tree": "^1.8.0",
"react-color": "^2.13.8",
"react-diff-viewer-continued": "^3.4.0",
"react-dnd": "^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-hot-loader": "^4.13.1",
"react-intersection-observer": "^9.10.2",
@ -193,9 +195,9 @@
"react-split": "^2.0.9",
"react-syntax-highlighter": "^15.4.5",
"react-table": "^7.8.0",
"react-transition-group": "^2.5.3",
"react-transition-group": "^4.4.5",
"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",
"redux": "^4.2.1",
"redux-localstorage": "^0.4.1",
@ -217,7 +219,7 @@
"@applitools/eyes-storybook": "^3.50.9",
"@babel/cli": "^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/node": "^7.22.6",
"@babel/plugin-proposal-class-properties": "^7.18.6",
@ -225,15 +227,19 @@
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.22.7",
"@babel/preset-env": "^7.22.7",
"@babel/preset-react": "^7.22.5",
"@babel/plugin-transform-modules-commonjs": "^7.26.3",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@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",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/jest": "^11.11.0",
"@hot-loader/react-dom": "^16.14.0",
"@emotion/babel-plugin": "^11.13.5",
"@emotion/jest": "^11.13.0",
"@hot-loader/react-dom": "^17.0.2",
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@mihkeleidast/storybook-addon-source": "^1.0.1",
"@storybook/addon-actions": "8.1.11",
@ -254,7 +260,6 @@
"@types/classnames": "^2.2.10",
"@types/dom-to-image": "^2.6.7",
"@types/enzyme": "^3.10.18",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/fetch-mock": "^7.3.2",
"@types/jest": "^29.5.12",
"@types/jquery": "^3.5.8",
@ -262,8 +267,8 @@
"@types/json-bigint": "^1.0.4",
"@types/math-expression-evaluator": "^1.3.3",
"@types/mousetrap": "^1.6.15",
"@types/react": "^16.9.53",
"@types/react-dom": "^16.9.8",
"@types/react": "^17.0.83",
"@types/react-dom": "^17.0.26",
"@types/react-gravatar": "^2.6.14",
"@types/react-json-tree": "^0.6.11",
"@types/react-loadable": "^5.5.11",
@ -271,7 +276,7 @@
"@types/react-router-dom": "^5.3.3",
"@types/react-syntax-highlighter": "^15.5.13",
"@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-virtualized-auto-sizer": "^1.0.4",
"@types/react-window": "^1.8.8",
@ -283,24 +288,26 @@
"@types/yargs": "12 - 18",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
"babel-jest": "^29.7.0",
"babel-loader": "^9.1.3",
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
"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",
"cross-env": "^7.0.3",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.1",
"css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.7",
"esbuild": "^0.20.0",
"esbuild-loader": "^4.2.2",
"eslint": "^8.56.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^7.2.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-import": "^2.24.2",
"eslint-plugin-jest": "^27.8.0",
@ -310,7 +317,7 @@
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-prettier": "^5.1.3",
"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-storybook": "^0.8.0",
"eslint-plugin-testing-library": "^6.4.0",
@ -320,6 +327,7 @@
"fetch-mock": "^7.7.3",
"fork-ts-checker-webpack-plugin": "^9.0.2",
"history": "^5.3.0",
"html-webpack-plugin": "^5.6.3",
"ignore-styles": "^5.0.1",
"imports-loader": "^5.0.0",
"jest": "^29.7.0",
@ -328,21 +336,19 @@
"jest-enzyme": "^7.1.2",
"jest-html-reporter": "^3.10.2",
"jest-websocket-mock": "^2.5.0",
"jsdom": "^24.1.1",
"jsdom": "^26.0.0",
"lerna": "^8.1.7",
"less": "^4.2.0",
"less-loader": "^12.2.0",
"mini-css-extract-plugin": "^2.9.0",
"mock-socket": "^9.3.1",
"moment-locales-webpack-plugin": "^1.2.0",
"node-fetch": "^2.6.7",
"open-cli": "^8.0.0",
"po2json": "^0.4.5",
"prettier": "3.3.3",
"prettier-plugin-packagejson": "^2.5.3",
"process": "^0.11.10",
"react-resizable": "^3.0.5",
"react-test-renderer": "^16.14.0",
"react-test-renderer": "^17.0.2",
"redux-mock-store": "^1.5.4",
"sinon": "^18.0.0",
"source-map": "^0.7.4",
@ -351,17 +357,17 @@
"storybook": "8.1.11",
"style-loader": "^4.0.0",
"thread-loader": "^4.0.4",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.1",
"typescript": "^4.8.4",
"typescript": "5.1.6",
"vm-browserify": "^1.1.2",
"webpack": "^5.94.0",
"webpack": "^5.97.1",
"webpack-bundle-analyzer": "^4.10.1",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.15.1",
"webpack-manifest-plugin": "^5.0.0",
"webpack-sources": "^3.2.3",
"webpack-visualizer-plugin2": "^1.1.0",
"xdm": "^3.4.0"
"webpack-visualizer-plugin2": "^1.1.0"
},
"engines": {
"node": "^20.16.0",
@ -374,7 +380,6 @@
"ansi-regex": "^4.1.1"
},
"puppeteer": "^22.4.1",
"@types/react": "^16.9.53",
"underscore": "^1.13.7",
"fast-glob": {
"micromatch": "^4.0.6"

View File

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

View File

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

View File

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

View File

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

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