mirror of https://github.com/grafana/grafana.git
E2E: migrate to playwright (#107241)
* separate playwright into its own folder * better separation * add login scenario, add tags * remove ui option * update CODEOWNERS * add a panels suite test * migrate queries test to playwright * rename + add dashlist test * add panelEdit_base * add geomap map controls test * add geomap-layer-types * add geomap-spatial-operations tests * add frontend-sandbox-panel tests * add smoke-tests-suite * add comment about adding datasource * add dashboard-browse-nested * add dashboard-browse * add dashboard-export-json * add dashboard-keybindings test * remove @wip tag * turn on screenshots and add comment for why this test fails * add dashboard-links-without-slug test * try adding permissions in the test as well * add dashboard-live-streaming * context in the test doesn't work - sad * create dashboard-public-templating * add dashboard-public-create and make live streaming more resilient * add share externally test * add dashboard-share-internally * add share-snapshot-create test * add dashboard-templating * add timepicker tests * add embedded-dashboard test * add general_dashboards test * add import-dashboard test * add load-options-from-url test * add new-constant-variable test * add custom-variable test * add new-datasource-variable test * add new-interval-variable test * add text-box-variable test * add new-query-variable test * add horizontal repeat test * add panel-vertical-repeat test * add empty-row-repeat test * add set-options-from-ui test * add snapshot-create test * add templating test * add textbox-variables test * add cloud-plugins-suite * add storybook verification tests * add playwright storybook verification workflow * add playwright browsers * update CODEOWNERS * test change to trigger storybook verification workflows * try container instead * get the version right... * go back to installing - less chance of forgetting to update * Basic Github Actions Squashed commit of the following: commitf84c650a71
Author: joshhunt <josh.hunt@grafana.com> Date: Tue Jul 1 13:23:46 2025 +0100 add arg for sharding, but not using it yet commit7bcf0512c6
Author: joshhunt <josh.hunt@grafana.com> Date: Tue Jul 1 12:30:30 2025 +0100 less newline commitb643911882
Author: joshhunt <josh.hunt@grafana.com> Date: Tue Jul 1 12:24:31 2025 +0100 less logs commit38f871e9c2
Author: joshhunt <josh.hunt@grafana.com> Date: Tue Jul 1 10:00:26 2025 +0100 fix yaml commitdb9a773136
Author: joshhunt <josh.hunt@grafana.com> Date: Tue Jul 1 09:57:47 2025 +0100 clean up files commitc0525f41fa
Author: joshhunt <josh.hunt@grafana.com> Date: Tue Jul 1 09:44:56 2025 +0100 gha workflow commit895bea7c52
Author: joshhunt <josh.hunt@grafana.com> Date: Mon Jun 30 19:33:08 2025 +0100 working dagger commitcea1f84437
Author: joshhunt <josh.hunt@grafana.com> Date: Mon Jun 30 16:17:46 2025 +0100 wip * shard gha * some tidy up * add flags for exporting results, and a gha step to merge runs * fix shard gha * add dashboard-duplicate-panel test * add dashboard-outline test * add dashboards-add-panel * remove some commented out code * add dashboards-title-description test * add dashboards-remove-panel * don't install cypress * gha: check playwright results * add dashboards-edit-adhoc-variables test * fix check-jobs * add dagger cloud token * add dagger cloud token * add edit-datasource-variable test * update CODEOWNERS * add dashboards-edit-group-by-variables (skipped for now) * add dashboards-edit-panel-title-description test * add dashboards-edit-transparent-bg test * add dashboards-edit-query-variables test * run with 8 shards * add dashboards-edit-variables * tidy up gha * add dashboard-group-panels * fix action * try to cache the grafana build * fix missing action becuase no checkout, use builtin continue-on-error instead * fix missing id * cat out.txt * debug build cache * fix debug build cache * add dashboards-panel-layouts test * tidy up * no more debug * fix grafana dir * add dashboards-move-panel test * skip some failing tests * mark up plugins tests with @plugins tag, only run @plugins tests in drone * Hackathon/Playwright Conversion - Various Suite (#107516) * Playwright Migration: Various Suite tests * skipping bad tests * fix some tests that can fail * fix uid * separate user for the verify-i18n test * build test plugins for grafana server * properly blur input fields * login manually * get dashboardPage from goto * ignore a couple of type assertions * remove a couple of timeouts * remove timeouts on dashboard-share-internally * use toBeHidden * make dashboard-share-internally more stable * remove TEMP_DAGGER_TOKEN * clean up visaulization-suggestions * unskip gauge test * unskip trace-view-scrolling * attempt to make create variable utils stable * unskip loki tests * make go linter happy * unskip edit-group-by-variables test * unskip move panel tests * isolate dashboard-timepicker tests with separate user * create data source as part of smoke test * make sure we're awaiting in dashboard-edit-adhoc-variables * make dashboards-edit-variables test more robust * Hackathon Playwright: Dashboards Search (#107580) * Hackathon Playwright: Dashboards Search * Feedback changes * make trace-view-scrolling more stable * add json report and bench step * fix bench version * move fail step to after the playwright report so we can report test failures * fix output file name * fix typo * try wrap in expect.poll * stability * bit more tidy up * fix dashboard-new-layouts tests * move test-plugins to e2e-playwright * fix go code for drone e2e run * move loki plugin-e2e test * make v2 dashboards work again --------- Co-authored-by: joshhunt <josh.hunt@grafana.com> Co-authored-by: Josh Hunt <joshhunt@users.noreply.github.com> Co-authored-by: Collin Fingar <collin.fingar@grafana.com> Co-authored-by: Jeff Levin <jeff@levinology.com>
This commit is contained in:
parent
ea0ddb3fc9
commit
b6580ccb10
|
@ -35,7 +35,7 @@ module.exports = [
|
|||
'data/',
|
||||
'deployment_tools_config.json',
|
||||
'devenv',
|
||||
'e2e/test-plugins',
|
||||
'e2e-playwright/test-plugins',
|
||||
'e2e/tmp',
|
||||
'packages/grafana-ui/src/components/Icon/iconBundle.ts',
|
||||
'pkg',
|
||||
|
|
|
@ -289,7 +289,7 @@ steps:
|
|||
- commands:
|
||||
- npx wait-on@7.0.1 http://$HOST:$PORT
|
||||
- yarn playwright install --with-deps chromium
|
||||
- yarn e2e:playwright
|
||||
- yarn e2e:playwright --grep @plugins
|
||||
depends_on:
|
||||
- grafana-server
|
||||
- build-test-plugins
|
||||
|
@ -761,7 +761,7 @@ steps:
|
|||
- commands:
|
||||
- npx wait-on@7.0.1 http://$HOST:$PORT
|
||||
- yarn playwright install --with-deps chromium
|
||||
- yarn e2e:playwright
|
||||
- yarn e2e:playwright --grep @plugins
|
||||
depends_on:
|
||||
- grafana-server
|
||||
- build-test-plugins
|
||||
|
@ -2986,6 +2986,6 @@ kind: secret
|
|||
name: gcr_credentials
|
||||
---
|
||||
kind: signature
|
||||
hmac: 1198b1489e48a9ced211633a0325d112814553246847fc7320fb5ac2bcb32b7d
|
||||
hmac: d20f1d6e2e8347701f82114ad352f53db57dc95b5b3831941fa93d063a92b9d8
|
||||
|
||||
...
|
||||
|
|
|
@ -400,8 +400,10 @@
|
|||
/public/app/core/internationalization/ @grafana/grafana-frontend-platform
|
||||
/e2e/ @grafana/grafana-frontend-platform
|
||||
/e2e/cloud-plugins-suite/ @grafana/partner-datasources
|
||||
/e2e/plugin-e2e/plugin-e2e-api-tests/ @grafana/plugins-platform-frontend
|
||||
/e2e/test-plugins/grafana-extensionstest-app/ @grafana/plugins-platform-frontend
|
||||
/e2e-playwright/ @grafana/grafana-frontend-platform
|
||||
/e2e-playwright/plugin-e2e/ @grafana/oss-big-tent @grafana/partner-datasources
|
||||
/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/ @grafana/plugins-platform-frontend
|
||||
/e2e-playwright/test-plugins/grafana-extensionstest-app/ @grafana/plugins-platform-frontend
|
||||
|
||||
# Packages
|
||||
/packages/ @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
|
||||
|
@ -477,6 +479,7 @@
|
|||
/cypress.config.js @grafana/grafana-frontend-platform
|
||||
/.levignore.js @grafana/plugins-platform-frontend
|
||||
playwright.config.ts @grafana/plugins-platform-frontend
|
||||
playwright.storybook.config.ts @grafana/grafana-frontend-platform
|
||||
|
||||
# public folder
|
||||
/public/app/api/ @grafana/grafana-frontend-platform
|
||||
|
@ -770,6 +773,7 @@ embed.go @grafana/grafana-as-code
|
|||
/.github/pr-checks.json @tolzhabayev
|
||||
/.github/pr-commands.json @tolzhabayev
|
||||
/.github/renovate.json5 @grafana/frontend-ops
|
||||
/.github/actions/check-jobs/action.yml @grafana/grafana-frontend-platform
|
||||
/.github/actions/setup-enterprise/action.yml @grafana/grafana-backend-group
|
||||
/.github/actions/setup-grafana-bench/ @Proximyst
|
||||
/.github/actions/build-package @grafana/grafana-developer-enablement-squad
|
||||
|
@ -817,6 +821,7 @@ embed.go @grafana/grafana-as-code
|
|||
/.github/workflows/scripts/json-file-to-job-output.js @grafana/plugins-platform-frontend
|
||||
/.github/workflows/stale.yml @grafana/grafana-developer-enablement-squad
|
||||
/.github/workflows/storybook-verification.yml @grafana/grafana-frontend-platform
|
||||
/.github/workflows/storybook-verification-playwright.yml @grafana/grafana-frontend-platform
|
||||
/.github/workflows/update-make-docs.yml @grafana/docs-tooling
|
||||
/.github/workflows/scripts/kinds/verify-kinds.go @grafana/platform-monitoring
|
||||
/.github/workflows/scripts/create-security-branch/create-security-branch.sh @grafana/grafana-developer-enablement-squad
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
name: Check jobs results
|
||||
description: Checks if any jobs have failed and exits with error if failures are found. Use to check the results of matrix test runs.
|
||||
inputs:
|
||||
needs:
|
||||
description: JSON string containing the needs context from the workflow
|
||||
required: true
|
||||
failure-message:
|
||||
description: Custom message to display when failures are found
|
||||
required: false
|
||||
default: "One or more jobs have failed"
|
||||
success-message:
|
||||
description: Custom message to display when all jobs pass
|
||||
required: false
|
||||
default: "All jobs passed successfully"
|
||||
outputs:
|
||||
any-failed:
|
||||
description: Whether any jobs failed
|
||||
value: ${{ steps.check-jobs.outputs.any-failed }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Check test suites
|
||||
id: check-jobs
|
||||
shell: bash
|
||||
env:
|
||||
NEEDS: ${{ inputs.needs }}
|
||||
FAILURE_MSG: ${{ inputs.failure-message }}
|
||||
SUCCESS_MSG: ${{ inputs.success-message }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Print the needs context, debugging
|
||||
echo "$NEEDS" | jq
|
||||
|
||||
# Extract failures
|
||||
FAILURES="$(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | map_values(.result)')"
|
||||
|
||||
# Check if there are any failures
|
||||
if [ "$(echo "$FAILURES" | jq '. | length')" != "0" ]; then
|
||||
echo "❌ $FAILURE_MSG"
|
||||
echo "Failed suites:"
|
||||
echo "$FAILURES" | jq -r 'to_entries[] | "- \(.key): \(.value)"'
|
||||
echo "any-failed=true" >> "$GITHUB_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ $SUCCESS_MSG"
|
|
@ -7,12 +7,17 @@ on:
|
|||
- main
|
||||
- release-*.*.*
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
|
||||
# TODO: re-enable this before merging
|
||||
# concurrency:
|
||||
# group: ${{ github.workflow }}-${{ github.ref }}
|
||||
# cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
ACTIONS_STEP_DEBUG: true
|
||||
RUNNER_DEBUG: 1
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
name: Detect whether code changed
|
||||
|
@ -44,22 +49,56 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: ./grafana
|
||||
persist-credentials: false
|
||||
- uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
|
||||
|
||||
# TODO: add a cleanup workflow to remove the cache when the PR is closed
|
||||
# https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy
|
||||
# TODO: maybe we could just use the cache to store the build, instead of uploading as an artifact?
|
||||
- uses: actions/cache@v4
|
||||
id: cache
|
||||
with:
|
||||
key: "build-grafana-${{ runner.os }}-${{ hashFiles('yarn.lock', 'public/*', 'packages/*', 'pkg/**/*.go', '**/go.mod', '**/go.sum', '!**_test.go', '!**.test.ts', '!**.test.tsx') }}"
|
||||
path: |
|
||||
build-dir
|
||||
|
||||
# If no cache hit, build Grafana
|
||||
- name: Build Grafana
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
|
||||
with:
|
||||
verb: run
|
||||
args: go -C grafana run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="${PWD}/grafana" > out.txt
|
||||
- run: mv "$(cat out.txt)" grafana.tar.gz
|
||||
- run: echo "artifact=grafana-e2e-${{github.run_number}}" >> "$GITHUB_OUTPUT"
|
||||
args: go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="${PWD}" > out.txt
|
||||
- name: Cat built artifact
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: cat out.txt
|
||||
- name: Move built artifact
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p build-dir
|
||||
mv "$(cat out.txt)" build-dir/grafana.tar.gz
|
||||
|
||||
# If cache hit, validate the artifact is present
|
||||
- name: Validate artifact
|
||||
if: steps.cache.outputs.cache-hit == 'true'
|
||||
run: |
|
||||
if [ ! -f build-dir/grafana.tar.gz ]; then
|
||||
echo "Error: build-dir/grafana.tar.gz not found in cache"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Set artifact name
|
||||
run: echo "artifact=grafana-server-${{github.run_number}}" >> "$GITHUB_OUTPUT"
|
||||
id: artifact
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
id: upload
|
||||
with:
|
||||
retention-days: 1
|
||||
name: ${{ steps.artifact.outputs.artifact }}
|
||||
path: grafana.tar.gz
|
||||
path: build-dir/grafana.tar.gz
|
||||
|
||||
# TODO: we won't need this when we only have playwright
|
||||
build-e2e-runner:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.changed == 'true'
|
||||
|
@ -159,6 +198,119 @@ jobs:
|
|||
path: videos
|
||||
retention-days: 1
|
||||
|
||||
run-playwright-tests:
|
||||
needs:
|
||||
- build-grafana
|
||||
name: Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }})
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
shardTotal: [8]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ needs.build-grafana.outputs.artifact }}
|
||||
- name: Run E2E tests
|
||||
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
|
||||
with:
|
||||
verb: run
|
||||
args: go run ./pkg/build/e2e-playwright --package=grafana.tar.gz --shard=${{ matrix.shard }}/${{ matrix.shardTotal }} --blob-dir=./blob-report
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: success() || failure()
|
||||
with:
|
||||
name: playwright-blob-${{ github.run_number }}-${{ matrix.shard }}
|
||||
path: ./blob-report
|
||||
retention-days: 1
|
||||
|
||||
required-playwright-tests:
|
||||
needs:
|
||||
- run-playwright-tests
|
||||
- build-grafana
|
||||
if: ${{ !cancelled() }}
|
||||
name: All Playwright tests complete
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: blobs
|
||||
pattern: playwright-blob-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Check blob reports
|
||||
run: |
|
||||
if [ ! "$(ls -A ./blobs)" ]; then
|
||||
echo "Error: No blob reports found in ./blobs directory"
|
||||
echo "Did the Playwright tests run at all?"
|
||||
exit 1
|
||||
fi
|
||||
echo "Found blob reports in ./blobs:"
|
||||
ls -lah ./blobs
|
||||
|
||||
- name: Merge into HTML Report
|
||||
run: npx playwright merge-reports --reporter html ./blobs
|
||||
|
||||
- name: Merge into JSON Report
|
||||
env:
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME: /tmp/playwright-results.json
|
||||
run: npx playwright merge-reports --reporter=json ./blobs
|
||||
|
||||
- name: Bench report
|
||||
run: |
|
||||
docker run --rm \
|
||||
--volume="/tmp/playwright-results.json:/home/bench/tests/playwright-results.json" \
|
||||
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.5.1 report \
|
||||
--grafana-url "http://localhost:3000" \
|
||||
--grafana-version "CI- ${{ github.sha }}" \
|
||||
--test-suite-name "FrontendCore" \
|
||||
--report-input playwright \
|
||||
--report-output log \
|
||||
--log-level DEBUG \
|
||||
/home/bench/tests/playwright-results.json
|
||||
|
||||
- name: Upload HTML report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: playwright-html-${{ github.run_number }}
|
||||
path: playwright-report
|
||||
retention-days: 7
|
||||
|
||||
- name: Check test suites
|
||||
id: check-jobs
|
||||
uses: ./.github/actions/check-jobs
|
||||
continue-on-error: true # Failure will be reported on Show test results step
|
||||
with:
|
||||
needs: ${{ toJson(needs) }}
|
||||
failure-message: "One or more E2E test suites have failed"
|
||||
success-message: "All E2E test suites completed successfully"
|
||||
|
||||
- name: Show test results
|
||||
env:
|
||||
FAILED: ${{ steps.check-jobs.outputs.any-failed }}
|
||||
# sed removes the leading `../../src/` from the paths
|
||||
run: |
|
||||
npx playwright merge-reports --reporter list ./all-blob-reports | sed 's|\(\.\./\)\{1,\}src/|/|g'
|
||||
if [ "$FAILED" = "true" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run-a11y-test:
|
||||
needs:
|
||||
- build-grafana
|
||||
|
@ -193,6 +345,7 @@ jobs:
|
|||
required-e2e-tests:
|
||||
needs:
|
||||
- run-e2e-tests
|
||||
- build-grafana
|
||||
# a11y test is not listed on purpose: it is not an important E2E test.
|
||||
# It is also totally fine to fail right now.
|
||||
# always() is the best function here.
|
||||
|
@ -203,13 +356,13 @@ jobs:
|
|||
name: All E2E tests complete
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Check test suites
|
||||
env:
|
||||
NEEDS: ${{ toJson(needs) }}
|
||||
run: |
|
||||
FAILURES="$(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | map_values(.result)')"
|
||||
echo "$FAILURES"
|
||||
if [ "$(echo "$FAILURES" | jq '. | length')" != "0" ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "All OK!"
|
||||
uses: ./.github/actions/check-jobs
|
||||
with:
|
||||
needs: ${{ toJson(needs) }}
|
||||
failure-message: "One or more E2E test suites have failed"
|
||||
success-message: "All E2E test suites completed successfully"
|
||||
|
|
|
@ -208,7 +208,7 @@ jobs:
|
|||
if: ${{ inputs.bump == true || inputs.bump == 'true' }}
|
||||
run: |
|
||||
git add package.json lerna.json yarn.lock packages public
|
||||
test -e e2e/test-plugins && git add e2e/test-plugins
|
||||
test -e e2e-playwright/test-plugins && git add e2e-playwright/test-plugins
|
||||
git commit -m "Update version to $VERSION"
|
||||
|
||||
- name: Git push
|
||||
|
|
|
@ -64,7 +64,7 @@ jobs:
|
|||
tools/
|
||||
public/
|
||||
conf/
|
||||
e2e/test-plugins/
|
||||
e2e-playwright/test-plugins/
|
||||
devenv/
|
||||
key: ${{ runner.os }}-grafana-${{ hashFiles('go.mod', 'package-lock.json', 'Makefile', 'pkg/storage/**/*.go', 'public/app/features/search/**/*.ts', 'public/app/features/search/**/*.tsx') }}
|
||||
# only rebuild grafana if search files have changed ( or dependencies )
|
||||
|
@ -116,7 +116,7 @@ jobs:
|
|||
tools/
|
||||
public/
|
||||
conf/
|
||||
e2e/test-plugins/
|
||||
e2e-playwright/test-plugins/
|
||||
devenv/
|
||||
key: ${{ runner.os }}-grafana-${{ hashFiles('go.mod', 'package-lock.json', 'Makefile', 'pkg/storage/**/*.go', 'public/app/features/search/**/*.ts', 'public/app/features/search/**/*.tsx') }}
|
||||
- name: Set the step name
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
name: Verify Storybook (Playwright)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/grafana-ui/**'
|
||||
- '!docs/**'
|
||||
- '!*.md'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'packages/grafana-ui/**'
|
||||
- '!docs/**'
|
||||
- '!*.md'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
verify-storybook:
|
||||
name: Verify Storybook (Playwright)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Install Playwright browsers
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
- name: Run Storybook and E2E tests
|
||||
run: yarn e2e:playwright:storybook
|
|
@ -237,4 +237,4 @@ public/app/plugins/**/dist/
|
|||
# Mock service worker used for fake API responses in frontend development
|
||||
public/mockServiceWorker.js
|
||||
|
||||
/e2e/test-plugins/*/dist
|
||||
/e2e-playwright/test-plugins/*/dist
|
||||
|
|
|
@ -6,9 +6,9 @@ When end-to-end testing Grafana plugins, a best practice is to use the [`@grafan
|
|||
|
||||
## Add end-to-end tests for a core plugin
|
||||
|
||||
You can add Playwright end-to-end tests for plugins to the [`e2e/plugin-e2e`](https://github.com/grafana/grafana/tree/main/e2e/plugin-e2e) directory.
|
||||
You can add Playwright end-to-end tests for plugins to the [`e2e-playwright/plugin-e2e`](https://github.com/grafana/grafana/tree/main/e2e-playwright/plugin-e2e) directory.
|
||||
|
||||
1. Add a new directory that has the name as your plugin [`here`](https://github.com/grafana/grafana/tree/main/e2e/plugin-e2e). This is the directory where your plugin tests will be kept.
|
||||
1. Add a new directory that has the name as your plugin [`here`](https://github.com/grafana/grafana/tree/main/e2e-playwright/plugin-e2e). This is the directory where your plugin tests will be kept.
|
||||
|
||||
1. Playwright uses [projects](https://playwright.dev/docs/test-projects) to logically group tests together. All tests in a project share the same configuration.
|
||||
In the [Playwright config file](https://github.com/grafana/grafana/blob/main/playwright.config.ts), add a new project item. Make sure the `name` and the `testDir` subdirectory match the name of the directory that contains your plugin tests.
|
||||
|
|
|
@ -0,0 +1,544 @@
|
|||
import { readFileSync } from 'fs';
|
||||
import { load } from 'js-yaml';
|
||||
import { Page } from 'playwright-core';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import {
|
||||
test,
|
||||
expect,
|
||||
CreateDataSourcePageArgs,
|
||||
DataSourceConfigPage,
|
||||
E2ESelectorGroups,
|
||||
DashboardPage,
|
||||
} from '@grafana/plugin-e2e';
|
||||
|
||||
import { AzureQueryType } from '../../public/app/plugins/datasource/azuremonitor/dataquery.gen';
|
||||
import { selectors as azMonSelectors } from '../../public/app/plugins/datasource/azuremonitor/e2e/selectors';
|
||||
import {
|
||||
AzureMonitorDataSourceJsonData,
|
||||
AzureMonitorDataSourceSecureJsonData,
|
||||
} from '../../public/app/plugins/datasource/azuremonitor/types/types';
|
||||
|
||||
const provisioningPath = 'provisioning/datasources/azmonitor-ds.yaml';
|
||||
|
||||
type AzureMonitorConfig = {
|
||||
secureJsonData: AzureMonitorDataSourceSecureJsonData;
|
||||
jsonData: AzureMonitorDataSourceJsonData;
|
||||
};
|
||||
|
||||
type AzureMonitorProvision = { datasources: AzureMonitorConfig[] };
|
||||
|
||||
const dataSourceName = `Azure Monitor E2E Tests - ${uuidv4()}`;
|
||||
const storageAcctName = 'azmonteststorage';
|
||||
const logAnalyticsName = 'az-mon-test-logs';
|
||||
const applicationInsightsName = 'az-mon-test-ai-a';
|
||||
const rootSubscription = 'grafanalabs-datasources-dev';
|
||||
|
||||
async function provisionAzureMonitorDatasources(
|
||||
createDataSourceConfigPage: (args: CreateDataSourcePageArgs) => Promise<DataSourceConfigPage>,
|
||||
datasourceConfig: AzureMonitorConfig,
|
||||
page: Page,
|
||||
selectors: E2ESelectorGroups
|
||||
) {
|
||||
const configPage = await createDataSourceConfigPage({
|
||||
type: 'grafana-azure-monitor-datasource',
|
||||
name: dataSourceName,
|
||||
});
|
||||
|
||||
const azureCloudInput = page.getByTestId(azMonSelectors.components.configEditor.azureCloud.input).locator('input');
|
||||
await azureCloudInput.fill('Azure');
|
||||
await azureCloudInput.press('Enter');
|
||||
const tenantIdInput = page.getByTestId(azMonSelectors.components.configEditor.tenantID.input).locator('input');
|
||||
await tenantIdInput.fill(datasourceConfig.jsonData.tenantId!);
|
||||
const clientIdInput = page.getByTestId(azMonSelectors.components.configEditor.clientID.input).locator('input');
|
||||
await clientIdInput.fill(datasourceConfig.jsonData.clientId!);
|
||||
const clientSecretInput = page
|
||||
.getByTestId(azMonSelectors.components.configEditor.clientSecret.input)
|
||||
.locator('input');
|
||||
await clientSecretInput.fill(datasourceConfig.secureJsonData.clientSecret!);
|
||||
const loadSubscriptionsButton = page.getByTestId(azMonSelectors.components.configEditor.loadSubscriptions.button);
|
||||
await loadSubscriptionsButton.click();
|
||||
const defaultSubscriptionInput = page
|
||||
.getByTestId(azMonSelectors.components.configEditor.defaultSubscription.input)
|
||||
.locator('input');
|
||||
await defaultSubscriptionInput.fill('datasources');
|
||||
await configPage
|
||||
.getByGrafanaSelector(selectors.components.Select.option)
|
||||
.filter({
|
||||
hasText: 'datasources',
|
||||
})
|
||||
.click();
|
||||
await configPage.saveAndTest();
|
||||
}
|
||||
|
||||
// TODO unskip when we've figured out how to populate the credentials in CI
|
||||
test.describe.skip(
|
||||
'Azure Monitor datasource',
|
||||
{
|
||||
tag: ['@cloud-plugins'],
|
||||
},
|
||||
() => {
|
||||
let datasourceConfig: AzureMonitorConfig;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
// Check if we're running in CI
|
||||
const CI = process.env.CI;
|
||||
if (CI) {
|
||||
const outputs = JSON.parse(readFileSync('outputs.json', 'utf8'));
|
||||
datasourceConfig = {
|
||||
jsonData: {
|
||||
cloudName: 'Azure',
|
||||
tenantId: outputs.tenantId,
|
||||
clientId: outputs.clientId,
|
||||
},
|
||||
secureJsonData: { clientSecret: outputs.clientSecret },
|
||||
};
|
||||
} else {
|
||||
const yamlContent = readFileSync(provisioningPath, 'utf8');
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const yaml = load(yamlContent) as AzureMonitorProvision;
|
||||
datasourceConfig = yaml.datasources[0];
|
||||
}
|
||||
});
|
||||
|
||||
test('create dashboard, add panel for metrics, log analytics, ARG, and traces queries', async ({
|
||||
createDataSourceConfigPage,
|
||||
gotoDashboardPage,
|
||||
selectors,
|
||||
page,
|
||||
}) => {
|
||||
// this test can absolutely take longer than the default 30s timeout
|
||||
test.setTimeout(120000);
|
||||
await provisionAzureMonitorDatasources(createDataSourceConfigPage, datasourceConfig, page, selectors);
|
||||
// Create new dashboard
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
timeRange: {
|
||||
from: 'now-6h',
|
||||
to: 'now',
|
||||
zone: 'Coordinated Universal Time',
|
||||
},
|
||||
});
|
||||
|
||||
// Add metrics panel
|
||||
const metricsPanel = await dashboardPage.addPanel();
|
||||
|
||||
// Select Azure Monitor datasource
|
||||
const datasourcePicker = metricsPanel.getByGrafanaSelector(selectors.components.DataSourcePicker.inputV2);
|
||||
await datasourcePicker.fill(dataSourceName);
|
||||
const datasourceList = metricsPanel.getByGrafanaSelector(selectors.components.DataSourcePicker.dataSourceList);
|
||||
await datasourceList.getByText(dataSourceName).click();
|
||||
|
||||
// Configure metrics query
|
||||
const resourcePickerButton = page.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.select.button);
|
||||
await resourcePickerButton.click();
|
||||
await expect(page.getByText(rootSubscription)).toBeVisible({ timeout: 30000 });
|
||||
const resourceSearchInput = page.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.search.input);
|
||||
await resourceSearchInput.fill(storageAcctName);
|
||||
await expect(page.getByText(storageAcctName)).toBeVisible({ timeout: 30000 });
|
||||
await page.getByText(storageAcctName).click();
|
||||
const applyButton = page.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.apply.button);
|
||||
await applyButton.click();
|
||||
await expect(page.getByText('microsoft.storage/storageaccounts')).toBeVisible();
|
||||
const metricNameInput = page
|
||||
.getByTestId(azMonSelectors.components.queryEditor.metricsQueryEditor.metricName.input)
|
||||
.locator('input');
|
||||
await metricNameInput.fill('Used capacity');
|
||||
await metricNameInput.press('Enter');
|
||||
|
||||
// Save and go back to dashboard
|
||||
metricsPanel.backToDashboard();
|
||||
|
||||
// Add logs panel
|
||||
const logsPanel = await dashboardPage.addPanel();
|
||||
|
||||
// Select Azure Monitor datasource
|
||||
await datasourcePicker.fill(dataSourceName);
|
||||
await datasourceList.getByText(dataSourceName).click();
|
||||
|
||||
// Switch to Logs query type
|
||||
const queryTypeSelect = page.getByTestId(azMonSelectors.components.queryEditor.header.select).locator('input');
|
||||
await queryTypeSelect.fill('Logs');
|
||||
await queryTypeSelect.press('Enter');
|
||||
|
||||
// Configure logs query
|
||||
await resourcePickerButton.click();
|
||||
await expect(page.getByText(rootSubscription)).toBeVisible({ timeout: 30000 });
|
||||
await resourceSearchInput.fill(logAnalyticsName);
|
||||
await expect(page.getByText(logAnalyticsName)).toBeVisible({ timeout: 30000 });
|
||||
await page.getByText(logAnalyticsName).click();
|
||||
await applyButton.click();
|
||||
let codeEditor = logsPanel.getByGrafanaSelector(selectors.components.CodeEditor.container).locator('textarea');
|
||||
await codeEditor.fill('AzureDiagnostics', { force: true });
|
||||
const formatSelection = page
|
||||
.getByTestId(azMonSelectors.components.queryEditor.logsQueryEditor.formatSelection.input)
|
||||
.locator('input');
|
||||
await formatSelection.fill('Time series');
|
||||
await formatSelection.press('Enter');
|
||||
|
||||
// Save and go back to dashboard
|
||||
logsPanel.backToDashboard();
|
||||
|
||||
// Add Azure Resource Graph panel
|
||||
const resourceGraphPanel = await dashboardPage.addPanel();
|
||||
|
||||
// Select Azure Monitor datasource
|
||||
await datasourcePicker.fill(dataSourceName);
|
||||
await datasourceList.getByText(dataSourceName).click();
|
||||
|
||||
// Switch to Azure Resource Graph query type
|
||||
await queryTypeSelect.fill('Azure Resource Graph');
|
||||
await queryTypeSelect.press('Enter');
|
||||
|
||||
// Configure Azure Resource Graph query
|
||||
const subscriptionsInput = page
|
||||
.getByTestId(azMonSelectors.components.queryEditor.argsQueryEditor.subscriptions.input)
|
||||
.locator('input');
|
||||
await subscriptionsInput.fill('datasources');
|
||||
await subscriptionsInput.press('Enter');
|
||||
codeEditor = resourceGraphPanel
|
||||
.getByGrafanaSelector(selectors.components.CodeEditor.container)
|
||||
.locator('textarea');
|
||||
await codeEditor.fill(
|
||||
"Resources | where resourceGroup == 'cloud-plugins-e2e-test-azmon' | project name, resourceGroup"
|
||||
);
|
||||
|
||||
// Save and go back to dashboard
|
||||
resourceGraphPanel.backToDashboard();
|
||||
|
||||
// Add traces panel
|
||||
const tracesPanel = await dashboardPage.addPanel();
|
||||
|
||||
// Select Azure Monitor datasource
|
||||
await datasourcePicker.fill(dataSourceName);
|
||||
await datasourceList.getByText(dataSourceName).click();
|
||||
|
||||
// Switch to Traces query type
|
||||
await queryTypeSelect.fill('Traces');
|
||||
await queryTypeSelect.press('Enter');
|
||||
|
||||
// Configure traces query
|
||||
await resourcePickerButton.click();
|
||||
await expect(page.getByText(rootSubscription)).toBeVisible({ timeout: 30000 });
|
||||
await resourceSearchInput.fill(applicationInsightsName);
|
||||
await expect(page.getByText(applicationInsightsName)).toBeVisible({ timeout: 30000 });
|
||||
await page.getByText(applicationInsightsName).click();
|
||||
await applyButton.click();
|
||||
await formatSelection.fill('Trace');
|
||||
await formatSelection.press('Enter');
|
||||
});
|
||||
|
||||
test('create dashboard with template variables', async ({
|
||||
createDataSourceConfigPage,
|
||||
gotoDashboardPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
await provisionAzureMonitorDatasources(createDataSourceConfigPage, datasourceConfig, page, selectors);
|
||||
// Create new dashboard
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
timeRange: {
|
||||
from: 'now-6h',
|
||||
to: 'now',
|
||||
},
|
||||
});
|
||||
|
||||
// Add subscription variable
|
||||
await addAzureMonitorVariable(
|
||||
page,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
'subscription',
|
||||
AzureQueryType.SubscriptionsQuery,
|
||||
true
|
||||
);
|
||||
|
||||
// Add resource groups variable
|
||||
await addAzureMonitorVariable(
|
||||
page,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
'resourceGroups',
|
||||
AzureQueryType.ResourceGroupsQuery,
|
||||
false,
|
||||
{
|
||||
subscription: '$subscription',
|
||||
}
|
||||
);
|
||||
|
||||
// Add namespaces variable
|
||||
await addAzureMonitorVariable(
|
||||
page,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
'namespaces',
|
||||
AzureQueryType.NamespacesQuery,
|
||||
false,
|
||||
{
|
||||
subscription: '$subscription',
|
||||
resourceGroup: '$resourceGroups',
|
||||
}
|
||||
);
|
||||
|
||||
// Add region variable
|
||||
await addAzureMonitorVariable(page, dashboardPage, selectors, 'region', AzureQueryType.LocationsQuery, false, {
|
||||
subscription: '$subscription',
|
||||
});
|
||||
|
||||
// Add resource variable
|
||||
await addAzureMonitorVariable(
|
||||
page,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
'resource',
|
||||
AzureQueryType.ResourceNamesQuery,
|
||||
false,
|
||||
{
|
||||
subscription: '$subscription',
|
||||
resourceGroup: '$resourceGroups',
|
||||
namespace: '$namespaces',
|
||||
region: '$region',
|
||||
}
|
||||
);
|
||||
|
||||
// Set variable values
|
||||
const subscriptionVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('subscription'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await subscriptionVariable.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Select.option)
|
||||
.filter({ hasText: 'grafanalabs-datasources-dev' })
|
||||
.click();
|
||||
|
||||
const resourceGroupsVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('resourceGroups'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await resourceGroupsVariable.fill('cloud-plugins-e2e-test-azmon');
|
||||
await resourceGroupsVariable.press('ArrowDown');
|
||||
await resourceGroupsVariable.press('Enter');
|
||||
|
||||
const namespacesVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('namespaces'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await namespacesVariable.fill('microsoft.storage/storageaccounts');
|
||||
await namespacesVariable.press('ArrowDown');
|
||||
await namespacesVariable.press('Enter');
|
||||
|
||||
const regionVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('region'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await regionVariable.fill('uk south');
|
||||
await regionVariable.press('ArrowDown');
|
||||
await regionVariable.press('Enter');
|
||||
|
||||
const resourceVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('resource'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await resourceVariable.fill(storageAcctName);
|
||||
await resourceVariable.press('ArrowDown');
|
||||
await resourceVariable.press('Enter');
|
||||
|
||||
// Add panel with template variables
|
||||
const newPanel = await dashboardPage.addPanel();
|
||||
|
||||
const datasourcePicker = newPanel.getByGrafanaSelector(selectors.components.DataSourcePicker.inputV2);
|
||||
await datasourcePicker.fill(dataSourceName);
|
||||
const datasourceList = newPanel.getByGrafanaSelector(selectors.components.DataSourcePicker.dataSourceList);
|
||||
await datasourceList.getByText(dataSourceName).click();
|
||||
|
||||
const resourcePickerButton = page.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.select.button);
|
||||
await resourcePickerButton.click();
|
||||
const advancedCollapse = page.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.advanced.collapse);
|
||||
await advancedCollapse.click();
|
||||
const advancedSubscription = page
|
||||
.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.advanced.subscription.input)
|
||||
.locator('input');
|
||||
await advancedSubscription.fill('$subscription');
|
||||
const advancedResourceGroup = page
|
||||
.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.advanced.resourceGroup.input)
|
||||
.locator('input');
|
||||
await advancedResourceGroup.fill('$resourceGroups');
|
||||
const advancedNamespace = page
|
||||
.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.advanced.namespace.input)
|
||||
.locator('input');
|
||||
await advancedNamespace.fill('$namespaces');
|
||||
const advancedRegion = page
|
||||
.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.advanced.region.input)
|
||||
.locator('input');
|
||||
await advancedRegion.fill('$region');
|
||||
const advancedResource = page
|
||||
.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.advanced.resource.input)
|
||||
.locator('input');
|
||||
await advancedResource.fill('$resource');
|
||||
|
||||
const applyButton = page.getByTestId(azMonSelectors.components.queryEditor.resourcePicker.apply.button);
|
||||
await applyButton.click();
|
||||
|
||||
const metricNameInput = page
|
||||
.getByTestId(azMonSelectors.components.queryEditor.metricsQueryEditor.metricName.input)
|
||||
.locator('input');
|
||||
await metricNameInput.fill('Transactions');
|
||||
await metricNameInput.press('Enter');
|
||||
});
|
||||
|
||||
// Helper function to add template variables
|
||||
async function addAzureMonitorVariable(
|
||||
page: Page,
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
name: string,
|
||||
type: AzureQueryType,
|
||||
isFirst: boolean,
|
||||
options?: {
|
||||
subscription?: string;
|
||||
resourceGroup?: string;
|
||||
namespace?: string;
|
||||
resource?: string;
|
||||
region?: string;
|
||||
}
|
||||
) {
|
||||
// Navigate to variables settings
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.settingsButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Variables')).click();
|
||||
|
||||
if (isFirst) {
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.List.addVariableCTAV2)
|
||||
.click();
|
||||
} else {
|
||||
await page.getByTestId(selectors.pages.Dashboard.Settings.Variables.List.newButton).click();
|
||||
}
|
||||
|
||||
// Configure variable
|
||||
const nameInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2
|
||||
);
|
||||
await nameInput.clear();
|
||||
await nameInput.fill(name);
|
||||
|
||||
const datasourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.inputV2);
|
||||
await datasourcePicker.fill(dataSourceName);
|
||||
const datasourceList = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.dataSourceList);
|
||||
await datasourceList.getByText(dataSourceName).click();
|
||||
|
||||
const queryTypeInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.queryType.input)
|
||||
.locator('input');
|
||||
await queryTypeInput.fill(type.replace('Azure', '').trim());
|
||||
await queryTypeInput.press('Enter');
|
||||
|
||||
// Configure type-specific options
|
||||
switch (type) {
|
||||
case AzureQueryType.ResourceGroupsQuery:
|
||||
if (options?.subscription) {
|
||||
const subscriptionInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.subscription.input)
|
||||
.locator('input');
|
||||
await subscriptionInput.fill(options.subscription);
|
||||
await subscriptionInput.press('Enter');
|
||||
}
|
||||
break;
|
||||
case AzureQueryType.LocationsQuery:
|
||||
if (options?.subscription) {
|
||||
const subscriptionInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.subscription.input)
|
||||
.locator('input');
|
||||
await subscriptionInput.fill(options.subscription);
|
||||
await subscriptionInput.press('Enter');
|
||||
}
|
||||
break;
|
||||
case AzureQueryType.NamespacesQuery:
|
||||
if (options?.subscription) {
|
||||
const subscriptionInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.subscription.input)
|
||||
.locator('input');
|
||||
await subscriptionInput.fill(options.subscription);
|
||||
await subscriptionInput.press('Enter');
|
||||
}
|
||||
if (options?.resourceGroup) {
|
||||
const resourceGroupInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.resourceGroup.input)
|
||||
.locator('input');
|
||||
await resourceGroupInput.fill(options.resourceGroup);
|
||||
await resourceGroupInput.press('Enter');
|
||||
}
|
||||
break;
|
||||
case AzureQueryType.ResourceNamesQuery:
|
||||
if (options?.subscription) {
|
||||
const subscriptionInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.subscription.input)
|
||||
.locator('input');
|
||||
await subscriptionInput.fill(options.subscription);
|
||||
await subscriptionInput.press('Enter');
|
||||
}
|
||||
if (options?.resourceGroup) {
|
||||
const resourceGroupInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.resourceGroup.input)
|
||||
.locator('input');
|
||||
await resourceGroupInput.fill(options.resourceGroup);
|
||||
await resourceGroupInput.press('Enter');
|
||||
}
|
||||
if (options?.namespace) {
|
||||
const namespaceInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.namespace.input)
|
||||
.locator('input');
|
||||
await namespaceInput.fill(options.namespace);
|
||||
await namespaceInput.press('Enter');
|
||||
}
|
||||
if (options?.region) {
|
||||
const regionInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.region.input)
|
||||
.locator('input');
|
||||
await regionInput.fill(options.region);
|
||||
await regionInput.press('Enter');
|
||||
}
|
||||
break;
|
||||
case AzureQueryType.MetricNamesQuery:
|
||||
if (options?.subscription) {
|
||||
const subscriptionInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.subscription.input)
|
||||
.locator('input');
|
||||
await subscriptionInput.fill(options.subscription);
|
||||
await subscriptionInput.press('Enter');
|
||||
}
|
||||
if (options?.resourceGroup) {
|
||||
const resourceGroupInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.resourceGroup.input)
|
||||
.locator('input');
|
||||
await resourceGroupInput.fill(options.resourceGroup);
|
||||
await resourceGroupInput.press('Enter');
|
||||
}
|
||||
if (options?.namespace) {
|
||||
const namespaceInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.namespace.input)
|
||||
.locator('input');
|
||||
await namespaceInput.fill(options.namespace);
|
||||
await namespaceInput.press('Enter');
|
||||
}
|
||||
if (options?.resource) {
|
||||
const resourceInput = page
|
||||
.getByTestId(azMonSelectors.components.variableEditor.resource.input)
|
||||
.locator('input');
|
||||
await resourceInput.fill(options.resource);
|
||||
await resourceInput.press('Enter');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Save variable
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton)
|
||||
.click();
|
||||
|
||||
// Go back to dashboard
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
}
|
||||
}
|
||||
);
|
|
@ -0,0 +1,56 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import testV2Dashboard from '../dashboards/TestV2Dashboard.json';
|
||||
|
||||
import { flows } from './utils';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Dashboard panels',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can duplicate a panel', async ({ dashboardPage, selectors, page }) => {
|
||||
await page.goto(selectors.pages.ImportDashboard.url);
|
||||
await page.getByTestId(selectors.components.DashboardImportPage.textarea).fill(JSON.stringify(testV2Dashboard));
|
||||
await page.getByTestId(selectors.components.DashboardImportPage.submit).click();
|
||||
await page.getByTestId(selectors.components.ImportDashboardForm.name).fill('Paste tab');
|
||||
await page.getByTestId(selectors.components.ImportDashboardForm.submit).click();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
const oldPanelTitle = 'New panel';
|
||||
const panelTitle = 'Unique';
|
||||
await flows.changePanelTitle(dashboardPage, selectors, oldPanelTitle, panelTitle);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(panelTitle))
|
||||
).toBeVisible();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.menu(panelTitle))
|
||||
.click({ force: true });
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.menuItems('More...')).hover();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.menuItems('Duplicate')).click();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(panelTitle))).toHaveCount(
|
||||
2
|
||||
);
|
||||
|
||||
// Save, reload, and ensure duplicate has persisted
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.saveButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Drawer.DashboardSaveDrawer.saveButton).click();
|
||||
await page.reload();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(panelTitle))).toHaveCount(
|
||||
2
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,804 @@
|
|||
import { Page } from 'playwright-core';
|
||||
|
||||
import { test, expect, E2ESelectorGroups, DashboardPage } from '@grafana/plugin-e2e';
|
||||
|
||||
import testV2Dashboard from '../dashboards/TestV2Dashboard.json';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
// these tests require a larger viewport
|
||||
test.use({
|
||||
viewport: { width: 1280, height: 1080 },
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Grouping panels',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
// Helper functions
|
||||
async function importTestDashboard(page: Page, selectors: E2ESelectorGroups, title: string) {
|
||||
await page.goto(selectors.pages.ImportDashboard.url);
|
||||
await page.getByTestId(selectors.components.DashboardImportPage.textarea).fill(JSON.stringify(testV2Dashboard));
|
||||
await page.getByTestId(selectors.components.DashboardImportPage.submit).click();
|
||||
await page.getByTestId(selectors.components.ImportDashboardForm.name).fill(title);
|
||||
await page.getByTestId(selectors.components.ImportDashboardForm.submit).click();
|
||||
}
|
||||
|
||||
async function saveDashboard(dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.saveButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Drawer.DashboardSaveDrawer.saveButton).click();
|
||||
}
|
||||
|
||||
async function groupIntoRow(page: Page, dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.groupPanels).click();
|
||||
await page.getByText('Group into row').click();
|
||||
}
|
||||
|
||||
async function groupIntoTab(page: Page, dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.groupPanels).click();
|
||||
await page.getByText('Group into tab').click();
|
||||
}
|
||||
|
||||
async function ungroupPanels(dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.ungroup).click();
|
||||
}
|
||||
|
||||
async function addPanel(dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addPanel).last().click();
|
||||
}
|
||||
|
||||
/*
|
||||
* Rows
|
||||
*/
|
||||
|
||||
test('can group and ungroup new panels into row', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Group new panels into row');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Group into row
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
|
||||
// Verify row and panel titles
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
// Save dashboard and reload
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
// Verify row and panel titles after reload
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Ungroup
|
||||
await ungroupPanels(dashboardPage, selectors);
|
||||
|
||||
// Verify Row title is gone
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
// Save dashboard and reload
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
// Verify Row title is gone
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('can add and remove several rows', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Add and remove rows');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow).click();
|
||||
await addPanel(dashboardPage, selectors);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addPanel).last().click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 2'))
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(5);
|
||||
|
||||
// Save dashboard and reload
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 2'))
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(5);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
.locator('..')
|
||||
.click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.ConfirmModal.delete).click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 2'))
|
||||
.locator('..')
|
||||
.click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.ConfirmModal.delete).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 2'))
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 2'))
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('can paste a copied row', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Paste row');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
|
||||
// Copy by selecting row and using copy button
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.copyDropdown).click();
|
||||
await page.getByRole('menuitem', { name: 'Copy' }).click();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.pasteRow).click();
|
||||
|
||||
// scroll `New row` into view - this is at the bottom of the dashboard body
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow)
|
||||
.scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(6);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
// scroll last `New panel` into view - this is at the bottom of the dashboard body
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
.last()
|
||||
.scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(6);
|
||||
});
|
||||
|
||||
test('can duplicate a row', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Duplicate row');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
|
||||
// Duplicate by selecting row and using duplicate button
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.copyDropdown).click();
|
||||
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
|
||||
|
||||
// scroll `New row` into view - this is at the bottom of the dashboard body
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow)
|
||||
.scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(6);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
// scroll last `New panel` into view - this is at the bottom of the dashboard body
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
.last()
|
||||
.scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(6);
|
||||
});
|
||||
|
||||
test('can collapse rows', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Collapse rows');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
|
||||
// Duplicate row
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.copyDropdown).click();
|
||||
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
|
||||
|
||||
// scroll `New row` into view - this is at the bottom of the dashboard body
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow)
|
||||
.scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(6);
|
||||
|
||||
// Collapse rows by clicking on their titles
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(0);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('can convert rows into tabs when changing layout', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Rows to tabs');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
|
||||
// Duplicate row
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.copyDropdown).click();
|
||||
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1'))
|
||||
).toBeVisible();
|
||||
|
||||
// Go back to dashboard options
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.backButton).click({ force: true });
|
||||
|
||||
// Expand layouts section
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.OptionsGroup.toggle('group-layout-category'))
|
||||
.click();
|
||||
|
||||
// Select tabs layout
|
||||
await page.getByLabel('Tabs').click();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New row'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New row 1'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New row 1')).click();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New row'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New row 1'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New row')).click();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('can group and ungroup new panels into row with tab', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Group new panels into tab with row');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Group into row with tab
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
await groupIntoTab(page, dashboardPage, selectors);
|
||||
|
||||
// Verify tab and panel titles
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
// Save dashboard and reload
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
// Verify tab, row and panel titles after reload
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Ungroup
|
||||
await ungroupPanels(dashboardPage, selectors); // ungroup tabs
|
||||
await ungroupPanels(dashboardPage, selectors); // ungroup rows
|
||||
|
||||
// Verify tab and row titles is gone
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))).toBeHidden();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
// Save dashboard and reload
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
// Verify Row title is gone
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))).toBeHidden();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
|
||||
/*
|
||||
* Tabs
|
||||
*/
|
||||
|
||||
test('can group and ungroup new panels into tab', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Group new panels into tab');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Group into tab
|
||||
await groupIntoTab(page, dashboardPage, selectors);
|
||||
|
||||
// Verify tab and panel titles
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
// Save dashboard and reload
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
// Verify row and panel titles after reload
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Ungroup
|
||||
await ungroupPanels(dashboardPage, selectors);
|
||||
|
||||
// Verify Row title is gone
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
// Save dashboard and reload
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
// Verify Row title is gone
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('can add and remove several tabs', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Add and remove tabs');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoTab(page, dashboardPage, selectors);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addTab).click();
|
||||
await addPanel(dashboardPage, selectors);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addTab).click();
|
||||
await addPanel(dashboardPage, selectors);
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 2'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 2'))).toHaveAttribute(
|
||||
'aria-selected',
|
||||
'true'
|
||||
);
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(1);
|
||||
|
||||
// Save dashboard and reload
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 2'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 2'))).toHaveAttribute(
|
||||
'aria-selected',
|
||||
'true'
|
||||
);
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(1);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 2')).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.ConfirmModal.delete).click();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1')).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.ConfirmModal.delete).click();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeHidden();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 2'))).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeHidden();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 2'))).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('can paste a copied tab', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Paste tab');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoTab(page, dashboardPage, selectors);
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
|
||||
// Copy by selecting tab and using copy button
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.copyDropdown).click();
|
||||
await page.getByRole('menuitem', { name: 'Copy' }).click();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.pasteTab).click();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('can duplicate a tab', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Duplicate tab');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoTab(page, dashboardPage, selectors);
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
|
||||
// Duplicate by selecting tab and using duplicate button
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.copyDropdown).click();
|
||||
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('can convert tabs into rows when changing layout', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Tabs to rows');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoTab(page, dashboardPage, selectors);
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
|
||||
// Duplicate tab twice
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.copyDropdown).click();
|
||||
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.copyDropdown).click();
|
||||
await page.getByRole('menuitem', { name: 'Duplicate' }).click();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 1'))).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab 2'))).toBeVisible();
|
||||
|
||||
// Go back to dashboard options
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.backButton).click({ force: true });
|
||||
|
||||
// Expand layouts section
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.OptionsGroup.toggle('group-layout-category'))
|
||||
.click();
|
||||
|
||||
// Select rows layout
|
||||
await page.getByLabel('Rows').click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab'))
|
||||
).toBeVisible();
|
||||
// Wait for panels to load
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first()
|
||||
).toBeVisible();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 1'))
|
||||
.scrollIntoViewIfNeeded();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 1'))
|
||||
).toBeVisible();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 2'))
|
||||
.scrollIntoViewIfNeeded();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 2'))
|
||||
).toBeVisible();
|
||||
|
||||
// scroll `New row` into view - this is at the bottom of the dashboard body
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow)
|
||||
.scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(9);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab'))
|
||||
).toBeVisible();
|
||||
// Wait for panels to load
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first()
|
||||
).toBeVisible();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 1'))
|
||||
.scrollIntoViewIfNeeded();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 1'))
|
||||
).toBeVisible();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 2'))
|
||||
.scrollIntoViewIfNeeded();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 2'))
|
||||
).toBeVisible();
|
||||
|
||||
// scroll last `New panel` into view - this is at the bottom of the dashboard body
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
.last()
|
||||
.scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(9);
|
||||
});
|
||||
|
||||
test('can group and ungroup new panels into tab with row', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Group new panels into tab with row');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Group into tab
|
||||
await groupIntoTab(page, dashboardPage, selectors);
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
|
||||
// Verify tab and panel titles
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
// Save dashboard and reload
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
// Verify tab, row and panel titles after reload
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Ungroup
|
||||
await ungroupPanels(dashboardPage, selectors); // ungroup rows
|
||||
await ungroupPanels(dashboardPage, selectors); // ungroup tabs
|
||||
|
||||
// Verify tab and row titles is gone
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeHidden();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
// Save dashboard and reload
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
// Verify Row title is gone
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeHidden();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,38 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = 'edediimbjhdz4b/a-tall-dashboard';
|
||||
|
||||
test.describe(
|
||||
'Dashboard Outline',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can use dashboard outline', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.section).click();
|
||||
|
||||
// Should be able to click Variables item in outline to see add variable button
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.item('Variables')).click();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.addVariableButton)
|
||||
).toBeVisible();
|
||||
|
||||
// Clicking a panel should scroll that panel in view
|
||||
await expect(page.getByText('Dashboard panel 48')).toBeHidden();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.item('Panel #48')).click();
|
||||
await expect(page.getByText('Dashboard panel 48')).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,44 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
test.describe(
|
||||
'Dashboard panels',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a new panel', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await page.evaluate(() => {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
});
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addPanel).last().click();
|
||||
|
||||
// Check that new panel has been added
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toBeVisible();
|
||||
|
||||
// Check that pressing the configure button shows the panel editor
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.content)
|
||||
.filter({ hasText: 'Configure' })
|
||||
.click();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.General.content)).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,91 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import { flows, type Variable } from './utils';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
test.describe(
|
||||
'Dashboard edit - Ad hoc variables',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a new adhoc variable', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
const variable: Variable = {
|
||||
type: 'adhoc',
|
||||
name: 'VariableUnderTest',
|
||||
value: 'label1',
|
||||
label: 'VariableUnderTest',
|
||||
};
|
||||
|
||||
// common steps to add a new variable
|
||||
await flows.newEditPaneVariableClick(dashboardPage, selectors);
|
||||
await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable);
|
||||
|
||||
// Select datasource for the ad hoc variable
|
||||
const dataSource = 'gdev-loki';
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.AdHocFiltersVariable.datasourceSelect)
|
||||
.click();
|
||||
await page.getByText(dataSource).click();
|
||||
|
||||
// mock the API call to get the labels
|
||||
const labels = ['label1', 'label2'];
|
||||
await page.route('**/resources/labels*', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
status: 'success',
|
||||
data: labels,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
// Select the variable in the dashboard and confirm the variable value is set
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItem).click();
|
||||
const variableLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!)
|
||||
);
|
||||
await expect(variableLabel).toBeVisible();
|
||||
await expect(variableLabel).toContainText(variable.label!);
|
||||
|
||||
// mock the API call to get the label values
|
||||
const labelValues = ['label2Value1'];
|
||||
await page.route(`**/resources/label/${labels[1]}/values*`, async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
status: 'success',
|
||||
data: labelValues,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
// choose the label and value
|
||||
await page.getByText(labels[1]).click();
|
||||
await page.getByText('=', { exact: true }).click();
|
||||
await page.getByText(labelValues[0]).click();
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
// assert the panel is visible and has the correct value
|
||||
const panelContent = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.content).first();
|
||||
await expect(panelContent).toBeVisible();
|
||||
const markdownContent = panelContent.locator('.markdown-html');
|
||||
await expect(markdownContent).toContainText(`VariableUnderTest: ${labels[1]}="${labelValues[0]}"`);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,62 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import { flows, type Variable } from './utils';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
test.describe(
|
||||
'Dashboard edit - datasource variables',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a new datasource variable', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
const dsType = 'cloudwatch';
|
||||
const variable: Variable = {
|
||||
type: 'datasource',
|
||||
name: 'VariableUnderTest',
|
||||
label: 'VariableUnderTest',
|
||||
value: `gdev-${dsType}`,
|
||||
};
|
||||
|
||||
// Common steps to add a new variable
|
||||
await flows.newEditPaneVariableClick(dashboardPage, selectors);
|
||||
await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.DatasourceVariable.datasourceSelect)
|
||||
.click();
|
||||
await page.getByText(dsType).click();
|
||||
|
||||
const regexFilter = 'cloud';
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.DatasourceVariable.nameFilter)
|
||||
.fill(regexFilter);
|
||||
|
||||
// Assert the variable dropdown is visible with correct label
|
||||
const variableLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!)
|
||||
);
|
||||
await expect(variableLabel).toBeVisible();
|
||||
await expect(variableLabel).toContainText(variable.label!);
|
||||
|
||||
// Assert the variable values are correctly displayed in the panel
|
||||
const panelContent = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.content).first();
|
||||
await expect(panelContent).toBeVisible();
|
||||
const markdownContent = panelContent.locator('.markdown-html');
|
||||
await expect(markdownContent).toContainText(`${variable.name}: ${variable.value}`);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,89 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import { flows, type Variable } from './utils';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
test.describe(
|
||||
'Dashboard edit - Group By variables',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a new group by variable', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
const variable: Variable = {
|
||||
type: 'groupby',
|
||||
name: 'VariableUnderTest',
|
||||
value: 'label1',
|
||||
label: 'VariableUnderTest',
|
||||
};
|
||||
|
||||
// common steps to add a new variable
|
||||
await flows.newEditPaneVariableClick(dashboardPage, selectors);
|
||||
await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable);
|
||||
|
||||
// select datasource for the group by variable
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.GroupByVariable.dataSourceSelect)
|
||||
.click();
|
||||
const dataSource = 'gdev-loki';
|
||||
await page.getByText(dataSource).click();
|
||||
|
||||
// mock the API call to get the labels
|
||||
const labels = ['label1', 'label2'];
|
||||
await page.route('**/resources/labels*', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
status: 'success',
|
||||
data: labels,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
// open the variable dropdown in the dashboard and confirm the variable value is set
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItem).locator('> div').click();
|
||||
const variableLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!)
|
||||
);
|
||||
await expect(variableLabel).toBeVisible();
|
||||
await expect(variableLabel).toContainText(variable.label!);
|
||||
|
||||
// mock the API call to get the label values
|
||||
const labelValues = ['label2Value1'];
|
||||
await page.route(`**/resources/label/${labels[1]}/values*`, async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
status: 'success',
|
||||
data: labelValues,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
// choose the label and value
|
||||
await page.getByText(labels[1]).click();
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
// assert the panel is visible and has the correct value
|
||||
const panelContent = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.content).first();
|
||||
await expect(panelContent).toBeVisible();
|
||||
const markdownContent = panelContent.locator('.markdown-html');
|
||||
await expect(markdownContent).toContainText(`VariableUnderTest: ${labels[1]}`);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,58 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import { flows } from './utils';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = '5SdHCadmz/panel-tests-graph';
|
||||
|
||||
test.describe(
|
||||
'Dashboard',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can edit panel title and description', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
const oldTitle = 'No Data Points Warning';
|
||||
const firstPanelTitle = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.first()
|
||||
.locator('h2')
|
||||
.first();
|
||||
await expect(firstPanelTitle).toHaveText(oldTitle);
|
||||
|
||||
const newDescription = 'A description of this panel';
|
||||
await flows.changePanelDescription(dashboardPage, selectors, oldTitle, newDescription);
|
||||
|
||||
const newTitle = 'New Panel Title';
|
||||
await flows.changePanelTitle(dashboardPage, selectors, oldTitle, newTitle);
|
||||
|
||||
// Check that new title is reflected in panel header
|
||||
const updatedPanelTitle = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.first()
|
||||
.locator('h2')
|
||||
.first();
|
||||
await expect(updatedPanelTitle).toHaveText(newTitle);
|
||||
|
||||
// Reveal description tooltip and check that its value is as expected
|
||||
const descriptionIcon = page.locator('[data-testid="title-items-container"] > span').first();
|
||||
await descriptionIcon.click({ force: true });
|
||||
|
||||
// Get the tooltip ID from the aria-describedby attribute
|
||||
const tooltipId = await descriptionIcon.getAttribute('aria-describedby');
|
||||
const tooltip = page.locator(`[id="${tooltipId}"]`);
|
||||
await expect(tooltip).toHaveText(`${newDescription}\n`);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,43 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = '5SdHCadmz/panel-tests-graph';
|
||||
|
||||
test.describe(
|
||||
'Dashboard',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can toggle transparent background switch', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: /^No Data Points Warning$/ })
|
||||
.first()
|
||||
.click();
|
||||
|
||||
const panelTitle = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Panels.Panel.title('No Data Points Warning')
|
||||
);
|
||||
|
||||
const initialBackground = await panelTitle.evaluate((el) => getComputedStyle(el).background);
|
||||
expect(initialBackground).not.toMatch(/rgba\(0, 0, 0, 0\)/);
|
||||
|
||||
await page.locator('#transparent-background').click({ force: true });
|
||||
|
||||
const transparentBackground = await panelTitle.evaluate((el) => getComputedStyle(el).background);
|
||||
expect(transparentBackground).toMatch(/rgba\(0, 0, 0, 0\)/);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,81 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import { flows, type Variable } from './utils';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
test.describe(
|
||||
'Dashboard edit - Query variable',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a new query variable', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
const queryVariableOptions = ['default'];
|
||||
|
||||
const variable: Variable = {
|
||||
type: 'query',
|
||||
name: 'VariableUnderTest',
|
||||
value: queryVariableOptions[0],
|
||||
label: 'VariableUnderTest', // constant doesn't really need a label
|
||||
};
|
||||
|
||||
// common steps to add a new variable
|
||||
await flows.newEditPaneVariableClick(dashboardPage, selectors);
|
||||
await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable);
|
||||
|
||||
// open the modal query variable editor
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsOpenButton)
|
||||
.click();
|
||||
// select a core data source that just runs a query during preview
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container).click();
|
||||
|
||||
const dataSource = 'gdev-cloudwatch';
|
||||
// this will trigger an API call to get the query options
|
||||
await page.getByText(dataSource).click();
|
||||
|
||||
// show the preview of the query results
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.previewButton)
|
||||
.click();
|
||||
// assert the query results are shown
|
||||
const firstPreviewOption = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption)
|
||||
.first();
|
||||
await expect(firstPreviewOption).toBeVisible();
|
||||
const previewOptionText = await firstPreviewOption.textContent();
|
||||
const previewOption = previewOptionText?.trim() || '';
|
||||
|
||||
// close the modal
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.closeButton)
|
||||
.click();
|
||||
|
||||
// assert the query variable values are in the variable value select
|
||||
const variableLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.name!)
|
||||
);
|
||||
const nextElement = variableLabel.locator('+ *');
|
||||
await expect(nextElement).toHaveText(previewOption);
|
||||
|
||||
// Assert the panel is visible and has the correct value
|
||||
const panelContent = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.content).first();
|
||||
await expect(panelContent).toBeVisible();
|
||||
const markdownContent = panelContent.locator('.markdown-html');
|
||||
await expect(markdownContent).toContainText(`VariableUnderTest: ${previewOption}`);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,183 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import { flows, type Variable } from './utils';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
test.describe(
|
||||
'Dashboard edit - variables',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a new custom variable', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
const variable: Variable = {
|
||||
type: 'custom',
|
||||
name: 'foo',
|
||||
label: 'Foo',
|
||||
value: 'one,two,three',
|
||||
};
|
||||
|
||||
// common steps to add a new variable
|
||||
await flows.newEditPaneVariableClick(dashboardPage, selectors);
|
||||
await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable);
|
||||
|
||||
// set the custom variable value
|
||||
const customValueInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput
|
||||
);
|
||||
await customValueInput.fill(variable.value);
|
||||
await customValueInput.blur();
|
||||
|
||||
// assert the dropdown for the variable is visible and has the correct values
|
||||
const variableLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!)
|
||||
);
|
||||
await expect(variableLabel).toBeVisible();
|
||||
await expect(variableLabel).toContainText(variable.label!);
|
||||
|
||||
const values = variable.value.split(',');
|
||||
const firstValueLink = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(values[0])
|
||||
);
|
||||
await expect(firstValueLink).toBeVisible();
|
||||
|
||||
// check that variable deletion works
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click();
|
||||
await expect(variableLabel).toBeHidden();
|
||||
});
|
||||
|
||||
test('can add a new constant variable', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
const variable: Variable = {
|
||||
type: 'constant',
|
||||
name: 'VariableUnderTest',
|
||||
value: 'foo',
|
||||
label: 'VariableUnderTest', // constant doesn't really need a label
|
||||
};
|
||||
|
||||
// common steps to add a new variable
|
||||
await flows.newEditPaneVariableClick(dashboardPage, selectors);
|
||||
await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable);
|
||||
|
||||
// set the constant variable value
|
||||
const type = 'variable-type Value';
|
||||
const fieldLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel(type)
|
||||
);
|
||||
await expect(fieldLabel).toBeVisible();
|
||||
const inputField = fieldLabel.locator('input');
|
||||
await expect(inputField).toBeVisible();
|
||||
await inputField.fill(variable.value);
|
||||
await inputField.blur();
|
||||
|
||||
// assert the panel is visible and has the correct value
|
||||
const panelContent = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.content).first();
|
||||
await expect(panelContent).toBeVisible();
|
||||
const markdownContent = panelContent.locator('.markdown-html');
|
||||
await expect(markdownContent).toContainText(`VariableUnderTest: ${variable.value}`);
|
||||
});
|
||||
|
||||
test('can add a new textbox variable', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
const variable: Variable = {
|
||||
type: 'textbox',
|
||||
name: 'VariableUnderTest',
|
||||
value: 'foo',
|
||||
label: 'VariableUnderTest',
|
||||
};
|
||||
|
||||
// common steps to add a new variable
|
||||
await flows.newEditPaneVariableClick(dashboardPage, selectors);
|
||||
await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable);
|
||||
|
||||
// set the textbox variable value
|
||||
const type = 'variable-type Value';
|
||||
const fieldLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel(type)
|
||||
);
|
||||
await expect(fieldLabel).toBeVisible();
|
||||
const inputField = fieldLabel.locator('input');
|
||||
await expect(inputField).toBeVisible();
|
||||
await inputField.fill(variable.value);
|
||||
await inputField.blur();
|
||||
|
||||
// select the variable in the dashboard and confirm the variable value is set
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItem).click();
|
||||
const variableLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!)
|
||||
);
|
||||
await expect(variableLabel).toBeVisible();
|
||||
await expect(variableLabel).toContainText(variable.label!);
|
||||
|
||||
// assert the panel is visible and has the correct value
|
||||
const panelContent = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.content).first();
|
||||
await expect(panelContent).toBeVisible();
|
||||
const markdownContent = panelContent.locator('.markdown-html');
|
||||
await expect(markdownContent).toContainText(`VariableUnderTest: ${variable.value}`);
|
||||
});
|
||||
|
||||
test('can add a new interval variable', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
const variable: Variable = {
|
||||
type: 'interval',
|
||||
name: 'VariableUnderTest',
|
||||
value: '1m',
|
||||
label: 'VariableUnderTest',
|
||||
};
|
||||
|
||||
// common steps to add a new variable
|
||||
await flows.newEditPaneVariableClick(dashboardPage, selectors);
|
||||
await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable);
|
||||
|
||||
// enable the auto option
|
||||
await page.getByText('Auto option').click();
|
||||
|
||||
// select the variable in the dashboard and confirm the variable value is set
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItem).click();
|
||||
const variableLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!)
|
||||
);
|
||||
await expect(variableLabel).toBeVisible();
|
||||
await expect(variableLabel).toContainText(variable.label!);
|
||||
|
||||
// assert the panel is visible and has the correct value
|
||||
const panelContent = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.content).first();
|
||||
await expect(panelContent).toBeVisible();
|
||||
const markdownContent = panelContent.locator('.markdown-html');
|
||||
await expect(markdownContent).toContainText(`VariableUnderTest: ${variable.value}`);
|
||||
|
||||
// select the variable in the dashboard and set the Auto option
|
||||
const variableDropdown = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.name!)
|
||||
);
|
||||
const nextElement = variableDropdown.locator('+ *');
|
||||
await expect(nextElement).toHaveText('1m');
|
||||
await nextElement.click();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Select.option).filter({ hasText: 'Auto' }).click();
|
||||
|
||||
// assert the panel is visible and has the correct "Auto" value
|
||||
await expect(panelContent).toBeVisible();
|
||||
await expect(markdownContent).toContainText('VariableUnderTest: 10m');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,120 @@
|
|||
import { Page } from 'playwright-core';
|
||||
|
||||
import { test, expect, E2ESelectorGroups, DashboardPage } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'ed155665/annotation-filtering';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
// these tests require a larger viewport
|
||||
viewport: { width: 1920, height: 1080 },
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Dashboard',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can drag and drop panels', async ({ gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: `${PAGE_UNDER_TEST}?orgId=1` });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Move panel three to panel one position
|
||||
await movePanel(dashboardPage, selectors, /^Panel three$/, /^Panel one$/);
|
||||
|
||||
// Verify panel three is now above panel one
|
||||
const panel3Position = await getPanelPosition(dashboardPage, selectors, /^Panel three$/);
|
||||
const panel1Position = await getPanelPosition(dashboardPage, selectors, /^Panel one$/);
|
||||
|
||||
expect(panel3Position?.y).toBeLessThan(panel1Position?.y || 0);
|
||||
|
||||
// Move panel two to panel three position
|
||||
await movePanel(dashboardPage, selectors, /^Panel two$/, /^Panel three$/);
|
||||
|
||||
// Verify panel two is now above panel three
|
||||
const panel2Position = await getPanelPosition(dashboardPage, selectors, /^Panel two$/);
|
||||
const panel3PositionAfter = await getPanelPosition(dashboardPage, selectors, /^Panel three$/);
|
||||
|
||||
expect(panel2Position?.y).toBeLessThan(panel3PositionAfter?.y || 0);
|
||||
});
|
||||
|
||||
// Note, moving a panel from a nested row to a parent row currently just deletes the panel
|
||||
// This test will need to be updated once the correct behavior is implemented.
|
||||
test('can move panel from nested row to parent row', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: `${PAGE_UNDER_TEST}?orgId=1` });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
await groupIntoRow(page, dashboardPage, selectors);
|
||||
|
||||
const firstRowElement = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DashboardRow.title('New row'))
|
||||
.first();
|
||||
const rowBoundingBox = await firstRowElement.boundingBox();
|
||||
|
||||
if (!rowBoundingBox) {
|
||||
throw new Error('Row element not found');
|
||||
}
|
||||
|
||||
const panel1Element = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: /^Panel one$/ });
|
||||
|
||||
await panel1Element.hover();
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(rowBoundingBox.x, rowBoundingBox.y);
|
||||
await page.mouse.up();
|
||||
|
||||
await expect(
|
||||
dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: /^Panel one$/ })
|
||||
).toBeHidden();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Helper functions
|
||||
async function groupIntoRow(page: Page, dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.groupPanels).click();
|
||||
await page.getByText('Group into row').click();
|
||||
}
|
||||
|
||||
async function getPanelPosition(
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
panelTitle: string | RegExp
|
||||
) {
|
||||
const panel = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: panelTitle });
|
||||
const boundingBox = await panel.boundingBox();
|
||||
return boundingBox;
|
||||
}
|
||||
|
||||
async function movePanel(
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
sourcePanel: string | RegExp,
|
||||
targetPanel: string | RegExp
|
||||
) {
|
||||
// Get target panel position
|
||||
const targetPanelElement = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: targetPanel });
|
||||
|
||||
// Get source panel element
|
||||
const sourcePanelElement = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: sourcePanel });
|
||||
|
||||
// Perform drag and drop
|
||||
await sourcePanelElement.dragTo(targetPanelElement);
|
||||
}
|
|
@ -0,0 +1,409 @@
|
|||
import { Page } from 'playwright-core';
|
||||
|
||||
import { test, expect, E2ESelectorGroups, DashboardPage } from '@grafana/plugin-e2e';
|
||||
|
||||
import testV2Dashboard from '../dashboards/TestV2Dashboard.json';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Dashboard Panel Layouts',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can switch to auto grid layout', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Switch to auto grid');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.OptionsGroup.toggle('grid-layout-category'))
|
||||
.click();
|
||||
|
||||
await page.getByLabel('Auto grid').click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await checkAutoGridLayoutInputs(dashboardPage, selectors);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await checkAutoGridLayoutInputs(dashboardPage, selectors);
|
||||
});
|
||||
|
||||
test('can change min column width in auto grid layout', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Set min column width');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.OptionsGroup.toggle('grid-layout-category'))
|
||||
.click();
|
||||
|
||||
await page.getByLabel('Auto grid').click();
|
||||
|
||||
// Get initial positions - standard width should have panels on different rows
|
||||
const firstPanelTop = await getPanelTop(dashboardPage, selectors);
|
||||
const lastPanel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).last();
|
||||
const lastPanelBox = await lastPanel.boundingBox();
|
||||
const lastPanelTop = lastPanelBox?.y || 0;
|
||||
|
||||
// Verify standard layout has panels on different rows
|
||||
expect(lastPanelTop).toBeGreaterThan(firstPanelTop);
|
||||
|
||||
// Change to narrow min column width
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth)
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'Narrow' }).click();
|
||||
|
||||
// Verify narrow layout has all panels on same row
|
||||
const firstPanelTopNarrow = await getPanelTop(dashboardPage, selectors);
|
||||
const lastPanelBoxNarrow = await lastPanel.boundingBox();
|
||||
const lastPanelTopNarrow = lastPanelBoxNarrow?.y || 0;
|
||||
|
||||
expect(lastPanelTopNarrow).toBe(firstPanelTopNarrow);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth
|
||||
)
|
||||
).toHaveValue('Narrow');
|
||||
|
||||
const firstPanelTopReload = await getPanelTop(dashboardPage, selectors);
|
||||
const lastPanelBoxReload = await lastPanel.boundingBox();
|
||||
const lastPanelTopReload = lastPanelBoxReload?.y || 0;
|
||||
|
||||
expect(lastPanelTopReload).toBe(firstPanelTopReload);
|
||||
});
|
||||
|
||||
test('can change to custom min column width in auto grid layout', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Set custom min column width');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.OptionsGroup.toggle('grid-layout-category'))
|
||||
.click();
|
||||
|
||||
await page.getByLabel('Auto grid').click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth)
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'Custom' }).click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.customMinColumnWidth)
|
||||
.fill('900');
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.customMinColumnWidth)
|
||||
.blur();
|
||||
|
||||
// Changing to 900 custom width should have each panel span the whole row (stacked vertically)
|
||||
await verifyPanelsStackedVertically(dashboardPage, selectors);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await verifyPanelsStackedVertically(dashboardPage, selectors);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.customMinColumnWidth
|
||||
)
|
||||
).toHaveValue('900');
|
||||
|
||||
await verifyPanelsStackedVertically(dashboardPage, selectors);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.clearCustomMinColumnWidth)
|
||||
.click();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth
|
||||
)
|
||||
).toHaveValue('Standard');
|
||||
});
|
||||
|
||||
test('can change max columns in auto grid layout', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Set max columns');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.OptionsGroup.toggle('grid-layout-category'))
|
||||
.click();
|
||||
|
||||
await page.getByLabel('Auto grid').click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.maxColumns)
|
||||
.click();
|
||||
await page.getByRole('option', { name: '1', exact: true }).click();
|
||||
|
||||
// Changing to 1 max column should have each panel span the whole row (stacked vertically)
|
||||
await verifyPanelsStackedVertically(dashboardPage, selectors);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
await verifyPanelsStackedVertically(dashboardPage, selectors);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.maxColumns)
|
||||
).toHaveValue('1');
|
||||
|
||||
await verifyPanelsStackedVertically(dashboardPage, selectors);
|
||||
});
|
||||
|
||||
test('can change row height in auto grid layout', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Set row height');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.OptionsGroup.toggle('grid-layout-category'))
|
||||
.click();
|
||||
|
||||
await page.getByLabel('Auto grid').click();
|
||||
|
||||
const regularRowHeight = await getPanelHeight(dashboardPage, selectors);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight)
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'Short' }).click();
|
||||
|
||||
const shortHeight = await getPanelHeight(dashboardPage, selectors);
|
||||
expect(shortHeight).toBeLessThan(regularRowHeight);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight)
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'Tall' }).click();
|
||||
|
||||
const tallHeight = await getPanelHeight(dashboardPage, selectors);
|
||||
expect(tallHeight).toBeGreaterThan(regularRowHeight);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
const tallHeightAfterReload = await getPanelHeight(dashboardPage, selectors);
|
||||
expect(tallHeightAfterReload).toBeGreaterThan(regularRowHeight);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight)
|
||||
).toHaveValue('Tall');
|
||||
|
||||
const tallHeightAfterEdit = await getPanelHeight(dashboardPage, selectors);
|
||||
expect(tallHeightAfterEdit).toBeGreaterThan(regularRowHeight);
|
||||
});
|
||||
|
||||
test('can change to custom row height in auto grid layout', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Set custom row height');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.OptionsGroup.toggle('grid-layout-category'))
|
||||
.click();
|
||||
|
||||
await page.getByLabel('Auto grid').click();
|
||||
|
||||
const regularRowHeight = await getPanelHeight(dashboardPage, selectors);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight)
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'Custom' }).click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.customRowHeight)
|
||||
.fill('800');
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.customRowHeight)
|
||||
.blur();
|
||||
|
||||
const customHeight = await getPanelHeight(dashboardPage, selectors);
|
||||
expect(customHeight).toBeCloseTo(800, 5); // Allow some tolerance for rendering differences
|
||||
expect(customHeight).toBeGreaterThan(regularRowHeight);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
const customHeightAfterReload = await getPanelHeight(dashboardPage, selectors);
|
||||
expect(customHeightAfterReload).toBeCloseTo(800, 5);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.customRowHeight
|
||||
)
|
||||
).toHaveValue('800');
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.clearCustomRowHeight)
|
||||
.click();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight)
|
||||
).toHaveValue('Standard');
|
||||
});
|
||||
|
||||
test('can change fill screen in auto grid layout', async ({ dashboardPage, selectors, page }) => {
|
||||
await importTestDashboard(page, selectors, 'Set fill screen');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'))
|
||||
).toHaveCount(3);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.OptionsGroup.toggle('grid-layout-category'))
|
||||
.click();
|
||||
|
||||
await page.getByLabel('Auto grid').click();
|
||||
|
||||
// Set narrow column width first to ensure panels fit horizontally
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth)
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'Narrow' }).click();
|
||||
|
||||
const initialHeight = await getPanelHeight(dashboardPage, selectors);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.fillScreen)
|
||||
.click({ force: true });
|
||||
|
||||
const fillScreenHeight = await getPanelHeight(dashboardPage, selectors);
|
||||
expect(fillScreenHeight).toBeGreaterThan(initialHeight);
|
||||
|
||||
await saveDashboard(dashboardPage, selectors);
|
||||
await page.reload();
|
||||
|
||||
const fillScreenHeightAfterReload = await getPanelHeight(dashboardPage, selectors);
|
||||
expect(fillScreenHeightAfterReload).toBeGreaterThan(initialHeight);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.fillScreen)
|
||||
).toBeChecked();
|
||||
|
||||
const fillScreenHeightAfterEdit = await getPanelHeight(dashboardPage, selectors);
|
||||
expect(fillScreenHeightAfterEdit).toBeGreaterThan(initialHeight);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Helper functions
|
||||
async function importTestDashboard(page: Page, selectors: E2ESelectorGroups, title: string) {
|
||||
await page.goto(selectors.pages.ImportDashboard.url);
|
||||
await page.getByTestId(selectors.components.DashboardImportPage.textarea).fill(JSON.stringify(testV2Dashboard));
|
||||
await page.getByTestId(selectors.components.DashboardImportPage.submit).click();
|
||||
await page.getByTestId(selectors.components.ImportDashboardForm.name).fill(title);
|
||||
await page.getByTestId(selectors.components.ImportDashboardForm.submit).click();
|
||||
}
|
||||
|
||||
async function saveDashboard(dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.saveButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Drawer.DashboardSaveDrawer.saveButton).click();
|
||||
}
|
||||
|
||||
async function checkAutoGridLayoutInputs(dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.maxColumns)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.fillScreen)
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
async function verifyPanelsStackedVertically(dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
const panels = await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).all();
|
||||
let previousTop = 0;
|
||||
|
||||
for (const panel of panels) {
|
||||
const boundingBox = await panel.boundingBox();
|
||||
if (boundingBox) {
|
||||
if (previousTop === 0) {
|
||||
previousTop = boundingBox.y;
|
||||
} else {
|
||||
expect(boundingBox.y).toBeGreaterThan(previousTop);
|
||||
previousTop = boundingBox.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getPanelHeight(dashboardPage: DashboardPage, selectors: E2ESelectorGroups): Promise<number> {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first();
|
||||
const boundingBox = await panel.boundingBox();
|
||||
return boundingBox?.height || 0;
|
||||
}
|
||||
|
||||
async function getPanelTop(dashboardPage: DashboardPage, selectors: E2ESelectorGroups): Promise<number> {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first();
|
||||
const boundingBox = await panel.boundingBox();
|
||||
return boundingBox?.y || 0;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import { test, expect, DashboardPage, E2ESelectorGroups } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = 'edediimbjhdz4b/a-tall-dashboard';
|
||||
|
||||
test.describe(
|
||||
'Dashboard panels',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can remove a panel', async ({ gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Remove Panel #1
|
||||
await removePanelsByTitle(dashboardPage, selectors, ['Panel #1']);
|
||||
|
||||
// Check that panel has been deleted
|
||||
await expect(
|
||||
dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: /^Panel #1$/ })
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
test('can remove several panels at once', async ({ gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Remove multiple panels
|
||||
await removePanelsByTitle(dashboardPage, selectors, ['Panel #1', 'Panel #2', 'Panel #3']);
|
||||
|
||||
// Check that panels have been deleted
|
||||
await expect(
|
||||
dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: /^Panel #[123]$/ })
|
||||
).toBeHidden();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Helper function to remove a panel by its title
|
||||
async function removePanelsByTitle(dashboardPage: DashboardPage, selectors: E2ESelectorGroups, panelTitles: string[]) {
|
||||
for (const panelTitle of panelTitles) {
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: panelTitle })
|
||||
.click({
|
||||
modifiers: ['Shift'],
|
||||
});
|
||||
}
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.ConfirmModal.delete).click();
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
kubernetesDashboards: true,
|
||||
dashboardNewLayouts: true,
|
||||
groupByVariable: true,
|
||||
},
|
||||
});
|
||||
|
||||
const PAGE_UNDER_TEST = 'ed155665/annotation-filtering';
|
||||
|
||||
test.describe(
|
||||
'Dashboard',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can change dashboard description and title', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
|
||||
// Check that current dashboard title is visible in breadcrumb
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Breadcrumbs.breadcrumb('Annotation filtering'))
|
||||
).toBeVisible();
|
||||
|
||||
const titleInput = page.locator('[aria-label="dashboard-options Title field property editor"] input');
|
||||
await expect(titleInput).toHaveValue('Annotation filtering');
|
||||
await titleInput.fill('New dashboard title');
|
||||
await expect(titleInput).toHaveValue('New dashboard title');
|
||||
|
||||
// Check that new dashboard title is reflected in breadcrumb
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Breadcrumbs.breadcrumb('New dashboard title'))
|
||||
).toBeVisible();
|
||||
|
||||
// Check that we can successfully change the dashboard description
|
||||
const descriptionTextArea = page.locator(
|
||||
'[aria-label="dashboard-options Description field property editor"] textarea'
|
||||
);
|
||||
await descriptionTextArea.fill('Dashboard description');
|
||||
await expect(descriptionTextArea).toHaveValue('Dashboard description');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,84 @@
|
|||
import { DashboardPage, E2ESelectorGroups } from '@grafana/plugin-e2e';
|
||||
|
||||
const deselectPanels = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.Controls).click({
|
||||
position: { x: 0, y: 0 },
|
||||
});
|
||||
};
|
||||
|
||||
export const flows = {
|
||||
deselectPanels,
|
||||
async changePanelTitle(
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
oldPanelTitle: string,
|
||||
newPanelTitle: string
|
||||
) {
|
||||
await deselectPanels(dashboardPage, selectors);
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: oldPanelTitle })
|
||||
.first()
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldInput('Title'))
|
||||
.fill(newPanelTitle);
|
||||
},
|
||||
async changePanelDescription(
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
panelTitle: string,
|
||||
newDescription: string
|
||||
) {
|
||||
await deselectPanels(dashboardPage, selectors);
|
||||
const panelTitleRegex = new RegExp(`^${panelTitle}$`);
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer)
|
||||
.filter({ hasText: panelTitleRegex })
|
||||
.first()
|
||||
.click();
|
||||
const descriptionTextArea = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('panel-options Description'))
|
||||
.locator('textarea');
|
||||
await descriptionTextArea.fill(newDescription);
|
||||
},
|
||||
async newEditPaneVariableClick(dashboardPage: DashboardPage, selectors: E2ESelectorGroups) {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.section).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.item('Variables')).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.addVariableButton)
|
||||
.click();
|
||||
},
|
||||
async newEditPanelCommonVariableInputs(
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
variable: Variable
|
||||
) {
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.variableType(variable.type))
|
||||
.click();
|
||||
const variableNameInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.ElementEditPane.variableNameInput
|
||||
);
|
||||
await variableNameInput.click();
|
||||
await variableNameInput.fill(variable.name);
|
||||
await variableNameInput.blur();
|
||||
if (variable.label) {
|
||||
const variableLabelInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.ElementEditPane.variableLabelInput
|
||||
);
|
||||
await variableLabelInput.click();
|
||||
await variableLabelInput.fill(variable.label);
|
||||
await variableLabelInput.blur();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export type Variable = {
|
||||
type: string;
|
||||
name: string;
|
||||
label?: string;
|
||||
description?: string;
|
||||
value: string;
|
||||
};
|
|
@ -0,0 +1,63 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.describe(
|
||||
'Dashboard search',
|
||||
{
|
||||
tag: ['@dashboards-search'],
|
||||
},
|
||||
() => {
|
||||
test.use({ viewport: { width: 1280, height: 1080 } });
|
||||
|
||||
test.beforeEach(async ({ page, selectors }) => {
|
||||
await page.goto('/dashboards');
|
||||
await expect(page.getByTestId(selectors.pages.BrowseDashboards.table.row('gdev dashboards'))).toBeVisible();
|
||||
});
|
||||
|
||||
test('Search - Dashboards list', async ({ page, selectors }) => {
|
||||
await toggleSearchView(page, selectors);
|
||||
await assertResultsCount(page, 24);
|
||||
});
|
||||
|
||||
test('Search - Filter by search input', async ({ page, selectors }) => {
|
||||
await toggleSearchView(page, selectors);
|
||||
await assertResultsCount(page, 24);
|
||||
|
||||
const searchInput = await page.getByTestId('input-wrapper');
|
||||
await searchInput.click();
|
||||
await page.keyboard.type('Datasource tests - MySQL');
|
||||
|
||||
await assertResultsCount(page, 2);
|
||||
|
||||
await page.keyboard.press('ControlOrMeta+A');
|
||||
await page.keyboard.press('Backspace');
|
||||
await page.keyboard.type('Datasource tests - MySQL (unittest)');
|
||||
|
||||
await assertResultsCount(page, 1);
|
||||
|
||||
await page.keyboard.press('ControlOrMeta+A');
|
||||
await page.keyboard.press('Backspace');
|
||||
await page.keyboard.type('- MySQL');
|
||||
|
||||
await assertResultsCount(page, 2);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
async function assertResultsCount(page, length) {
|
||||
const rowGroup = await page.getByRole('rowgroup');
|
||||
await expect(rowGroup).toHaveCount(1);
|
||||
|
||||
const rows = await rowGroup.first().getByRole('row');
|
||||
await expect(rows).toHaveCount(length);
|
||||
}
|
||||
|
||||
async function toggleSearchView(page, selectors) {
|
||||
const toggleButtons = await page.getByTestId(selectors.pages.Dashboards.toggleView);
|
||||
await expect(toggleButtons).toHaveCount(2);
|
||||
|
||||
const listRadioButton = await toggleButtons.nth(1).locator('input');
|
||||
await expect(listRadioButton).toBeChecked({ checked: false });
|
||||
|
||||
await listRadioButton.check();
|
||||
await expect(listRadioButton).toBeChecked({ checked: true });
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import { makeNewDashboardRequestBody } from './utils/makeDashboard';
|
||||
|
||||
const NUM_ROOT_FOLDERS = 60;
|
||||
const NUM_ROOT_DASHBOARDS = 60;
|
||||
const NUM_NESTED_FOLDERS = 60;
|
||||
const NUM_NESTED_DASHBOARDS = 60;
|
||||
|
||||
// TODO change this test so it doesn't conflict with the existing dashboard browse test
|
||||
// probably needs a separate user
|
||||
test.describe.skip(
|
||||
'Dashboard browse (nested)',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
const dashboardUIDsToCleanUp: string[] = [];
|
||||
const folderUIDsToCleanUp: string[] = [];
|
||||
|
||||
test.beforeAll(async ({ request }) => {
|
||||
// Add root folders
|
||||
for (let i = 0; i < NUM_ROOT_FOLDERS; i++) {
|
||||
const response = await request.post('/api/folders', {
|
||||
data: {
|
||||
title: `Root folder ${i.toString().padStart(2, '0')}`,
|
||||
},
|
||||
});
|
||||
const responseBody = await response.json();
|
||||
folderUIDsToCleanUp.push(responseBody.uid);
|
||||
}
|
||||
|
||||
// Add root dashboards
|
||||
for (let i = 0; i < NUM_ROOT_DASHBOARDS; i++) {
|
||||
const response = await request.post('/api/dashboards/db', {
|
||||
data: makeNewDashboardRequestBody(`Root dashboard ${i.toString().padStart(2, '0')}`),
|
||||
});
|
||||
const responseBody = await response.json();
|
||||
dashboardUIDsToCleanUp.push(responseBody.uid);
|
||||
}
|
||||
|
||||
// Add folder with children
|
||||
const folderResponse = await request.post('/api/folders', {
|
||||
data: {
|
||||
title: 'A root folder with children',
|
||||
},
|
||||
});
|
||||
const folderResponseBody = await folderResponse.json();
|
||||
const folderUid = folderResponseBody.uid;
|
||||
folderUIDsToCleanUp.push(folderUid);
|
||||
|
||||
// Add nested folders
|
||||
for (let i = 0; i < NUM_NESTED_FOLDERS; i++) {
|
||||
await request.post('/api/folders', {
|
||||
data: {
|
||||
title: `Nested folder ${i.toString().padStart(2, '0')}`,
|
||||
parentUid: folderUid,
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Add nested dashboards
|
||||
for (let i = 0; i < NUM_NESTED_DASHBOARDS; i++) {
|
||||
await request.post('/api/dashboards/db', {
|
||||
data: makeNewDashboardRequestBody(`Nested dashboard ${i.toString().padStart(2, '0')}`, folderUid),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test.afterAll(async ({ request }) => {
|
||||
// Clean up root dashboards
|
||||
for (const dashboardUID of dashboardUIDsToCleanUp) {
|
||||
await request.delete(`/api/dashboards/uid/${dashboardUID}`);
|
||||
}
|
||||
|
||||
// Clean up root folders (cascading delete will remove any nested folders and dashboards)
|
||||
for (const folderUID of folderUIDsToCleanUp) {
|
||||
await request.delete(`/api/folders/${folderUID}`, {
|
||||
params: {
|
||||
forceDeleteRules: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('pagination works correctly for folders and root', async ({ page, selectors }) => {
|
||||
// Navigate to dashboards page
|
||||
await page.goto('/dashboards');
|
||||
|
||||
// Wait for and verify the root folder with children is visible
|
||||
await expect(page.getByText('A root folder with children')).toBeVisible();
|
||||
|
||||
// Expand A root folder with children
|
||||
await page.getByLabel('Expand folder A root folder with children').click();
|
||||
await expect(page.getByText('Nested folder 00')).toBeVisible();
|
||||
|
||||
// Get the table body container for scrolling
|
||||
const tableBody = page.getByTestId(selectors.pages.BrowseDashboards.table.body).locator('> div');
|
||||
|
||||
// Scroll the page and check visibility of next set of items
|
||||
await tableBody.evaluate((el) => el.scrollTo(0, 2100));
|
||||
await expect(page.getByText('Nested folder 59')).toBeVisible();
|
||||
await expect(page.getByText('Nested dashboard 00')).toBeVisible();
|
||||
|
||||
// Scroll the page and check visibility of next set of items
|
||||
await tableBody.evaluate((el) => el.scrollTo(0, 4200));
|
||||
await expect(page.getByText('Nested dashboard 59')).toBeVisible();
|
||||
await expect(page.getByText('Root folder 00')).toBeVisible();
|
||||
|
||||
// Scroll the page and check visibility of next set of items
|
||||
await tableBody.evaluate((el) => el.scrollTo(0, 6300));
|
||||
await expect(page.getByText('Root folder 59')).toBeVisible();
|
||||
await expect(page.getByText('Root dashboard 00')).toBeVisible();
|
||||
|
||||
// Scroll the page and check visibility of next set of items
|
||||
await tableBody.evaluate((el) => el.scrollTo(0, 8400));
|
||||
await expect(page.getByText('Root dashboard 59')).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,102 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import testDashboard from '../dashboards/TestDashboard.json';
|
||||
|
||||
test.describe(
|
||||
'Dashboard browse',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
let dashboardUID: string;
|
||||
|
||||
test.beforeAll(async ({ request }) => {
|
||||
// Import the test dashboard
|
||||
const response = await request.post('/api/dashboards/import', {
|
||||
data: {
|
||||
dashboard: testDashboard,
|
||||
folderUid: '',
|
||||
overwrite: true,
|
||||
inputs: [],
|
||||
},
|
||||
});
|
||||
const responseBody = await response.json();
|
||||
dashboardUID = responseBody.uid;
|
||||
});
|
||||
|
||||
test.afterAll(async ({ request }) => {
|
||||
// Clean up the imported dashboard
|
||||
if (dashboardUID) {
|
||||
await request.delete(`/api/dashboards/uid/${dashboardUID}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('Manage Dashboards tests', async ({ page, selectors }) => {
|
||||
// Navigate to dashboards page
|
||||
await page.goto('/dashboards');
|
||||
|
||||
// Folders and dashboards should be visible
|
||||
await expect(page.getByTestId(selectors.pages.BrowseDashboards.table.row('gdev dashboards'))).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId(selectors.pages.BrowseDashboards.table.row('E2E Test - Import Dashboard'))
|
||||
).toBeVisible();
|
||||
|
||||
// gdev dashboards folder is collapsed - its content should not be visible
|
||||
await expect(page.getByTestId(selectors.pages.BrowseDashboards.table.row('Bar Gauge Demo'))).toBeHidden();
|
||||
|
||||
// should click a folder and see its children
|
||||
await page
|
||||
.getByTestId(selectors.pages.BrowseDashboards.table.row('gdev dashboards'))
|
||||
.getByLabel(/Expand folder/)
|
||||
.click();
|
||||
await expect(page.getByTestId(selectors.pages.BrowseDashboards.table.row('Bar Gauge Demo'))).toBeVisible();
|
||||
|
||||
// Open the new folder drawer
|
||||
await page.getByText('New').click();
|
||||
await page.getByRole('menuitem', { name: 'New folder' }).click();
|
||||
|
||||
// And create a new folder
|
||||
await page.getByTestId(selectors.pages.BrowseDashboards.NewFolderForm.nameInput).fill('My new folder');
|
||||
await page
|
||||
.getByTestId(selectors.pages.BrowseDashboards.NewFolderForm.form)
|
||||
.getByRole('button', { name: 'Create' })
|
||||
.click();
|
||||
// await page.getByTestId(selectors.pages.BrowseDashboards.NewFolderForm.form).getByRole('button', { name: 'Create' }).click({ force: true });
|
||||
|
||||
// Verify success alert and close it
|
||||
const alert = page.getByTestId(selectors.components.Alert.alertV2('success'));
|
||||
await expect(alert).toBeVisible();
|
||||
await alert.getByLabel('Close alert').click();
|
||||
await expect(page.getByRole('heading', { name: 'My new folder' })).toBeVisible();
|
||||
|
||||
// Delete the folder and expect to go back to the root
|
||||
await page.getByRole('button', { name: 'Folder actions' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
await page.getByPlaceholder('Type "Delete" to confirm').fill('Delete');
|
||||
await page.getByTestId(selectors.pages.ConfirmModal.delete).click();
|
||||
await expect(page.getByRole('heading', { name: 'Dashboards' })).toBeVisible();
|
||||
|
||||
// Can collapse the gdev folder and delete the dashboard we imported
|
||||
await page
|
||||
.getByTestId(selectors.pages.BrowseDashboards.table.row('gdev dashboards'))
|
||||
.getByLabel(/Collapse folder/)
|
||||
.click();
|
||||
|
||||
// Select the imported dashboard using checkbox
|
||||
await page
|
||||
.getByTestId(selectors.pages.BrowseDashboards.table.row('E2E Test - Import Dashboard'))
|
||||
.getByRole('checkbox')
|
||||
.click({ force: true });
|
||||
|
||||
// Delete the selected dashboard
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
|
||||
// Confirm deletion in modal
|
||||
await page.getByPlaceholder('Type "Delete" to confirm').fill('Delete');
|
||||
await page.getByTestId(selectors.pages.ConfirmModal.delete).click();
|
||||
await expect(
|
||||
page.getByTestId(selectors.pages.BrowseDashboards.table.row('E2E Test - Import Dashboard'))
|
||||
).toBeHidden();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,66 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.describe(
|
||||
'Export as JSON',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('Export for internal and external use', async ({ gotoDashboardPage, page, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: 'ZqZnVvFZz',
|
||||
});
|
||||
|
||||
// Open the export drawer
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.NewExportButton.arrowMenu).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.NewExportButton.Menu.exportAsJson)
|
||||
.click();
|
||||
|
||||
await expect(page).toHaveURL(/.*shareView=export.*/);
|
||||
|
||||
// Export as JSON
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ExportDashboardDrawer.ExportAsJson.container)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ExportDashboardDrawer.ExportAsJson.exportExternallyToggle)
|
||||
).toBeChecked({
|
||||
checked: false,
|
||||
});
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.CodeEditor.container)).toBeVisible();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ExportDashboardDrawer.ExportAsJson.saveToFileButton)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ExportDashboardDrawer.ExportAsJson.copyToClipboardButton)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ExportDashboardDrawer.ExportAsJson.cancelButton)
|
||||
).toBeVisible();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ExportDashboardDrawer.ExportAsJson.copyToClipboardButton)
|
||||
.click();
|
||||
// TODO failing in CI - fix it
|
||||
// let clipboardContent = await page.evaluate(() => navigator.clipboard.readText());
|
||||
// expect(clipboardContent).not.toContain('__inputs');
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ExportDashboardDrawer.ExportAsJson.exportExternallyToggle)
|
||||
.click({ force: true });
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ExportDashboardDrawer.ExportAsJson.copyToClipboardButton)
|
||||
.click();
|
||||
// TODO failing in CI - fix it
|
||||
// clipboardContent = await page.evaluate(() => navigator.clipboard.readText());
|
||||
// expect(clipboardContent).toContain('__inputs');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.ExportDashboardDrawer.ExportAsJson.cancelButton).click();
|
||||
|
||||
await expect(page).not.toHaveURL(/.*shareView=export.*/);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,60 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.describe(
|
||||
'Dashboard keybindings',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
// the "should collapse and expand all rows" test requires a larger viewport
|
||||
// otherwise the final panel is not visible when everything is expanded
|
||||
test.use({
|
||||
viewport: { width: 1280, height: 1080 },
|
||||
});
|
||||
|
||||
test('should collapse and expand all rows', async ({ gotoDashboardPage, page, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: 'Repeating-rows-uid/repeating-rows' });
|
||||
|
||||
const panelContents = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.content);
|
||||
await expect(panelContents).toHaveCount(5);
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('server = A, pod = Bob'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('server = B, pod = Bob'))
|
||||
).toBeVisible();
|
||||
|
||||
// Collapse all rows using keyboard shortcut: d + Shift+C
|
||||
await page.keyboard.press('d');
|
||||
await page.keyboard.press('Shift+C');
|
||||
|
||||
await expect(panelContents).toHaveCount(0);
|
||||
await expect(page.getByText('server = A, pod = Bob')).toBeHidden();
|
||||
await expect(page.getByText('server = B, pod = Bob')).toBeHidden();
|
||||
|
||||
// Expand all rows using keyboard shortcut: d + Shift+E
|
||||
await page.keyboard.press('d');
|
||||
await page.keyboard.press('Shift+E');
|
||||
|
||||
await expect(panelContents).toHaveCount(6);
|
||||
await expect(page.getByText('server = A, pod = Bob')).toBeVisible();
|
||||
await expect(page.getByText('server = B, pod = Bob')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should open panel inspect', async ({ gotoDashboardPage, page, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: 'edediimbjhdz4b/a-tall-dashboard' });
|
||||
|
||||
// Find Panel #1 and press 'i' to open inspector
|
||||
const panel1 = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel #1'));
|
||||
await expect(panel1).toBeVisible();
|
||||
await panel1.press('i');
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelInspector.Json.content)).toBeVisible();
|
||||
|
||||
// Press Escape to close inspector
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
await expect(page.getByTestId(selectors.components.PanelInspector.Json.content)).toBeHidden();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,65 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import testDashboard from '../dashboards/DataLinkWithoutSlugTest.json';
|
||||
|
||||
test.describe(
|
||||
'Dashboard with data links that have no slug',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
let dashboardUID: string;
|
||||
|
||||
test.beforeAll(async ({ request }) => {
|
||||
// Import the test dashboard
|
||||
const response = await request.post('/api/dashboards/import', {
|
||||
data: {
|
||||
dashboard: testDashboard,
|
||||
folderUid: '',
|
||||
overwrite: true,
|
||||
inputs: [],
|
||||
},
|
||||
});
|
||||
const responseBody = await response.json();
|
||||
dashboardUID = responseBody.uid;
|
||||
});
|
||||
|
||||
test.afterAll(async ({ request }) => {
|
||||
// Clean up the imported dashboard
|
||||
if (dashboardUID) {
|
||||
await request.delete(`/api/dashboards/uid/${dashboardUID}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('Should not reload if linking to same dashboard', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: dashboardUID });
|
||||
|
||||
const panel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Panels.Panel.title('Data links without slug')
|
||||
);
|
||||
await expect(panel).toBeVisible();
|
||||
|
||||
const urlShouldContain = '/d/data-link-no-slug/data-link-without-slug-test';
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DataLinksContextMenu.singleLink)
|
||||
.getByText(/9yy21uzzxypg/)
|
||||
.click();
|
||||
await expect(page.getByText(/Loading/)).toBeHidden();
|
||||
await expect(page).toHaveURL(new RegExp(urlShouldContain));
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DataLinksContextMenu.singleLink)
|
||||
.getByText(/dr199bpvpcru/)
|
||||
.click();
|
||||
await expect(page.getByText(/Loading/)).toBeHidden();
|
||||
await expect(page).toHaveURL(new RegExp(urlShouldContain));
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DataLinksContextMenu.singleLink)
|
||||
.getByText(/dre33fzyxcrz/)
|
||||
.click();
|
||||
await expect(page.getByText(/Loading/)).toBeHidden();
|
||||
await expect(page).toHaveURL(new RegExp(urlShouldContain));
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,48 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import testDashboard from '../dashboards/DashboardLiveTest.json';
|
||||
|
||||
test.describe(
|
||||
'Dashboard Live streaming support',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
let dashboardUID: string;
|
||||
|
||||
test.beforeAll(async ({ request }) => {
|
||||
// Import the test dashboard
|
||||
const response = await request.post('/api/dashboards/import', {
|
||||
data: {
|
||||
dashboard: testDashboard,
|
||||
folderUid: '',
|
||||
overwrite: true,
|
||||
inputs: [],
|
||||
},
|
||||
});
|
||||
const responseBody = await response.json();
|
||||
dashboardUID = responseBody.uid;
|
||||
});
|
||||
|
||||
test.afterAll(async ({ request }) => {
|
||||
// Clean up the imported dashboard
|
||||
if (dashboardUID) {
|
||||
await request.delete(`/api/dashboards/uid/${dashboardUID}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('Should receive streaming data', async ({ gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: dashboardUID });
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Live'))).toBeVisible();
|
||||
await expect
|
||||
.poll(
|
||||
async () =>
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.body)
|
||||
.getByRole('row')
|
||||
.count()
|
||||
)
|
||||
.toBeGreaterThan(5);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,211 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
newDashboardSharingComponent: false,
|
||||
},
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Public dashboards',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('Create, open and disable a public dashboard', async ({ page, gotoDashboardPage, selectors, request }) => {
|
||||
// Navigate to dashboard without template variables
|
||||
let dashboardPage = await gotoDashboardPage({ uid: 'ZqZnVvFZz' });
|
||||
|
||||
// Open sharing modal
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.shareDashboard).click();
|
||||
|
||||
// Select public dashboards tab
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public Dashboard')).click();
|
||||
|
||||
// Create button should be disabled
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CreateButton)
|
||||
).toBeDisabled();
|
||||
|
||||
// Create flow shouldn't show these elements
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlButton)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.EnableAnnotationsSwitch)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.EnableTimeRangeSwitch)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.PauseSwitch)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.DeleteButton)
|
||||
).toBeHidden();
|
||||
|
||||
// Acknowledge checkboxes
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.WillBePublicCheckbox)
|
||||
.click({ force: true });
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.LimitedDSCheckbox)
|
||||
.click({ force: true });
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CostIncreaseCheckbox)
|
||||
.click({ force: true });
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CreateButton)
|
||||
).toBeEnabled();
|
||||
|
||||
// Create public dashboard
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CreateButton)
|
||||
.click();
|
||||
|
||||
// These elements shouldn't be rendered after creating public dashboard
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.WillBePublicCheckbox)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.LimitedDSCheckbox)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CostIncreaseCheckbox)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CreateButton)
|
||||
).toBeHidden();
|
||||
|
||||
// These elements should be rendered
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlButton)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.PauseSwitch)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.DeleteButton)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.SettingsDropdown)
|
||||
).toBeVisible();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.SettingsDropdown)
|
||||
.click();
|
||||
|
||||
// These elements should be rendered once the Settings dropdown is opened
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.EnableAnnotationsSwitch)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.EnableTimeRangeSwitch)
|
||||
).toBeVisible();
|
||||
|
||||
// Close the sharing modal
|
||||
await page.getByRole('button', { name: 'Close', exact: true }).click();
|
||||
|
||||
// Tag indicating a dashboard is public
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.publicDashboardTag)
|
||||
).toBeVisible();
|
||||
|
||||
// Open sharing modal
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.shareDashboard).click();
|
||||
|
||||
// Select public dashboards tab
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public Dashboard')).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlButton)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.PauseSwitch)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.DeleteButton)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.SettingsDropdown)
|
||||
).toBeVisible();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.SettingsDropdown)
|
||||
.click();
|
||||
|
||||
// These elements should be rendered once the Settings dropdown is opened
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.EnableTimeRangeSwitch)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.EnableAnnotationsSwitch)
|
||||
).toBeVisible();
|
||||
|
||||
// Make a request to public dashboards api endpoint without authentication
|
||||
let copyUrlInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput
|
||||
);
|
||||
let url = await copyUrlInput.inputValue();
|
||||
let publicDashboardApiUrl = getPublicDashboardAPIUrl(url);
|
||||
|
||||
// Create a new context without authentication
|
||||
let response = await request.get(publicDashboardApiUrl);
|
||||
expect(response.status()).toBe(200);
|
||||
|
||||
// Close the sharing modal
|
||||
await page.getByRole('button', { name: 'Close', exact: true }).click();
|
||||
|
||||
// Navigate to dashboard without template variables
|
||||
dashboardPage = await gotoDashboardPage({ uid: 'ZqZnVvFZz' });
|
||||
|
||||
// Open sharing modal
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.shareDashboard).click();
|
||||
|
||||
// Select public dashboards tab
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public Dashboard')).click();
|
||||
|
||||
// Save url before disabling public dashboard
|
||||
copyUrlInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput
|
||||
);
|
||||
url = await copyUrlInput.inputValue();
|
||||
|
||||
// Switch off enabling toggle
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.PauseSwitch)
|
||||
).toBeEnabled();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.PauseSwitch)
|
||||
.click({ force: true });
|
||||
|
||||
// Url should be disabled
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput)
|
||||
).toBeDisabled();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlButton)
|
||||
).toBeDisabled();
|
||||
|
||||
// Make a request to public dashboards api endpoint without authentication
|
||||
publicDashboardApiUrl = getPublicDashboardAPIUrl(url);
|
||||
response = await request.get(publicDashboardApiUrl);
|
||||
expect(response.status()).toBe(403);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const getPublicDashboardAPIUrl = (url: string): string => {
|
||||
const accessToken = url.split('/').pop();
|
||||
return `/api/public/dashboards/${accessToken}`;
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
newDashboardSharingComponent: false,
|
||||
},
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Public dashboard with template variables',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('Create a public dashboard with template variables shows a template variable warning', async ({
|
||||
gotoDashboardPage,
|
||||
selectors,
|
||||
}) => {
|
||||
// Navigate to dashboard with template variables
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: 'HYaGDGIMk',
|
||||
});
|
||||
|
||||
// Open sharing modal
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.shareDashboard).click();
|
||||
|
||||
// Select public dashboards tab
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public Dashboard')).click();
|
||||
|
||||
// Warning Alert dashboard cannot be made public because it has template variables
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardModal.PublicDashboard.TemplateVariablesWarningAlert
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
// Configuration elements for public dashboards should exist
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.WillBePublicCheckbox)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.LimitedDSCheckbox)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CostIncreaseCheckbox)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CreateButton)
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.PauseSwitch)
|
||||
).toBeHidden();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,191 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
scenes: true,
|
||||
newDashboardSharingComponent: true,
|
||||
},
|
||||
});
|
||||
|
||||
const DASHBOARD_UID = 'd41dbaa2-a39e-4536-ab2b-caca52f1a9c8';
|
||||
const DASHBOARD_UID_2 = 'edediimbjhdz4b';
|
||||
|
||||
test.describe(
|
||||
'Shared dashboards',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('Close share externally drawer', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: DASHBOARD_UID });
|
||||
|
||||
// Open share externally drawer
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.arrowMenu).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.menu.shareExternally)
|
||||
.click();
|
||||
|
||||
await expect(page).toHaveURL(/.*shareView=public_dashboard.*/);
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardDrawer.ShareExternally.container)
|
||||
).toBeVisible();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardDrawer.ShareExternally.Creation.PublicShare.cancelButton)
|
||||
.click();
|
||||
|
||||
await expect(page).not.toHaveURL(/.*shareView=public_dashboard.*/);
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardDrawer.ShareExternally.container)
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
test('Create and disable a shared dashboard and check API', async ({
|
||||
page,
|
||||
gotoDashboardPage,
|
||||
selectors,
|
||||
request,
|
||||
}) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: DASHBOARD_UID_2 });
|
||||
|
||||
// Open share externally drawer
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.arrowMenu).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.menu.shareExternally)
|
||||
.click();
|
||||
|
||||
// Create button should be disabled
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Creation.PublicShare.createButton
|
||||
)
|
||||
).toBeDisabled();
|
||||
|
||||
// Create flow shouldn't show these elements
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.enableTimeRangeSwitch
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.enableAnnotationsSwitch
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.copyUrlButton
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.revokeAccessButton
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.toggleAccessButton
|
||||
)
|
||||
).toBeHidden();
|
||||
|
||||
// Acknowledge checkbox
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Creation.willBePublicCheckbox
|
||||
)
|
||||
).toBeEnabled();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardDrawer.ShareExternally.Creation.willBePublicCheckbox)
|
||||
.click({ force: true });
|
||||
|
||||
// Create shared dashboard
|
||||
const createResponse = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes(`/api/dashboards/uid/${DASHBOARD_UID_2}/public-dashboards`) &&
|
||||
response.request().method() === 'POST'
|
||||
);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Creation.PublicShare.createButton
|
||||
)
|
||||
).toBeEnabled();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardDrawer.ShareExternally.Creation.PublicShare.createButton)
|
||||
.click();
|
||||
|
||||
let response = await createResponse;
|
||||
let publicDashboard = await response.json();
|
||||
|
||||
// Test API access with the created dashboard
|
||||
let apiResponse = await request.get(`/api/public/dashboards/${publicDashboard.accessToken}`);
|
||||
expect(apiResponse.status()).toBe(200);
|
||||
|
||||
// These elements shouldn't be rendered after creating public dashboard
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Creation.willBePublicCheckbox
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Creation.PublicShare.createButton
|
||||
)
|
||||
).toBeHidden();
|
||||
|
||||
// These elements should be rendered
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.enableTimeRangeSwitch
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.enableAnnotationsSwitch
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.copyUrlButton
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.revokeAccessButton
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.toggleAccessButton
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
// Switch off enabling toggle
|
||||
const updateResponse = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes(`/api/dashboards/uid/${DASHBOARD_UID_2}/public-dashboards/`) &&
|
||||
response.request().method() === 'PATCH'
|
||||
);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.toggleAccessButton
|
||||
)
|
||||
).toBeEnabled();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardDrawer.ShareExternally.Configuration.toggleAccessButton)
|
||||
.click({ force: true });
|
||||
|
||||
response = await updateResponse;
|
||||
expect(response.status()).toBe(200);
|
||||
|
||||
publicDashboard = await response.json();
|
||||
|
||||
// Test that API access is now forbidden
|
||||
apiResponse = await request.get(`/api/public/dashboards/${publicDashboard.accessToken}`);
|
||||
expect(apiResponse.status()).toBe(403);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,175 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
scenes: true,
|
||||
newDashboardSharingComponent: true,
|
||||
},
|
||||
});
|
||||
|
||||
const DASHBOARD_UID = 'ZqZnVvFZz';
|
||||
|
||||
test.describe(
|
||||
'Share internally',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Clear localStorage before each test
|
||||
await page.evaluate(() => {
|
||||
window.localStorage.removeItem('grafana.dashboard.link.shareConfiguration');
|
||||
});
|
||||
});
|
||||
|
||||
test('Create a locked time range short link', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
// Navigate to dashboard with specific time range
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: DASHBOARD_UID,
|
||||
queryParams: new URLSearchParams({ from: 'now-6h', to: 'now' }),
|
||||
});
|
||||
|
||||
// Open share internally drawer
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.arrowMenu).click();
|
||||
|
||||
const createResponse = page.waitForResponse(
|
||||
(response) => response.url().includes('/api/short-urls') && response.request().method() === 'POST'
|
||||
);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.menu.shareInternally)
|
||||
.click();
|
||||
|
||||
await expect(page).toHaveURL(/.*shareView=link.*/);
|
||||
|
||||
// Check that the required elements exist
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardDrawer.ShareInternally.lockTimeRangeSwitch)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardDrawer.ShareInternally.shortenUrlSwitch)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardDrawer.ShareInternally.copyUrlButton)
|
||||
).toBeVisible();
|
||||
|
||||
// Check radio buttons
|
||||
const radioButtons = dashboardPage.getByGrafanaSelector(selectors.components.RadioButton.container);
|
||||
await expect(radioButtons).toHaveCount(3);
|
||||
|
||||
// Check localStorage is initially null
|
||||
const initialConfig = await page.evaluate(() => {
|
||||
return window.localStorage.getItem('grafana.dashboard.link.shareConfiguration');
|
||||
});
|
||||
expect(initialConfig).toBeNull();
|
||||
|
||||
// Wait for the short URL creation
|
||||
const response = await createResponse;
|
||||
expect(response.status()).toBe(200);
|
||||
|
||||
const responseBody = await response.json();
|
||||
expect(responseBody.url).toContain('goto');
|
||||
});
|
||||
|
||||
test('Create a relative time range short link', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
// Navigate to dashboard with specific time range
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: DASHBOARD_UID,
|
||||
queryParams: new URLSearchParams({ from: 'now-6h', to: 'now' }),
|
||||
});
|
||||
|
||||
// Open share internally drawer
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.arrowMenu).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.menu.shareInternally)
|
||||
.click();
|
||||
|
||||
const updateResponse = page.waitForResponse(
|
||||
(response) => response.url().includes('/api/short-urls') && response.request().method() === 'POST'
|
||||
);
|
||||
|
||||
// Toggle the lock time range switch
|
||||
const lockTimeRangeSwitch = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareInternally.lockTimeRangeSwitch
|
||||
);
|
||||
await expect(lockTimeRangeSwitch).toBeInViewport();
|
||||
await expect(async () => {
|
||||
await lockTimeRangeSwitch.uncheck({ force: true });
|
||||
}).toPass();
|
||||
|
||||
await expect(async () => {
|
||||
// Check localStorage configuration
|
||||
const shareConfig = await page.evaluate(() => {
|
||||
const config = window.localStorage.getItem('grafana.dashboard.link.shareConfiguration');
|
||||
return config ? JSON.parse(config) : null;
|
||||
});
|
||||
|
||||
expect(shareConfig).not.toBeNull();
|
||||
expect(shareConfig.useAbsoluteTimeRange).toBe(false);
|
||||
expect(shareConfig.useShortUrl).toBe(true);
|
||||
expect(shareConfig.theme).toBe('current');
|
||||
}).toPass();
|
||||
|
||||
// Wait for the API response
|
||||
const response = await updateResponse;
|
||||
expect(response.status()).toBe(200);
|
||||
|
||||
const responseBody = await response.json();
|
||||
expect(responseBody.url).toContain('goto');
|
||||
});
|
||||
|
||||
test('Share button gets configured link', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
// Navigate to dashboard with specific time range
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: DASHBOARD_UID,
|
||||
queryParams: new URLSearchParams({ from: 'now-6h', to: 'now' }),
|
||||
});
|
||||
|
||||
// Open share internally drawer
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.arrowMenu).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.menu.shareInternally)
|
||||
.click();
|
||||
|
||||
// Check localStorage is initially null
|
||||
const initialConfig = await page.evaluate(() => {
|
||||
return window.localStorage.getItem('grafana.dashboard.link.shareConfiguration');
|
||||
});
|
||||
expect(initialConfig).toBeNull();
|
||||
|
||||
// Configure the sharing options
|
||||
const lockTimeRangeSwitch = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareInternally.lockTimeRangeSwitch
|
||||
);
|
||||
await expect(lockTimeRangeSwitch).toBeInViewport();
|
||||
await expect(async () => {
|
||||
await lockTimeRangeSwitch.uncheck({ force: true });
|
||||
}).toPass();
|
||||
const shortenUrlSwitch = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardDrawer.ShareInternally.shortenUrlSwitch
|
||||
);
|
||||
await expect(shortenUrlSwitch).toBeInViewport();
|
||||
await expect(async () => {
|
||||
await shortenUrlSwitch.uncheck({ force: true });
|
||||
}).toPass();
|
||||
|
||||
// Close the drawer
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Drawer.General.close).click();
|
||||
|
||||
await expect(page).not.toHaveURL(/.*shareView=link.*/);
|
||||
|
||||
await expect(async () => {
|
||||
// Check that localStorage has been updated with the configuration
|
||||
const finalConfig = await page.evaluate(() => {
|
||||
const config = window.localStorage.getItem('grafana.dashboard.link.shareConfiguration');
|
||||
return config ? JSON.parse(config) : null;
|
||||
});
|
||||
|
||||
expect(finalConfig).not.toBeNull();
|
||||
expect(finalConfig.useAbsoluteTimeRange).toBe(false);
|
||||
expect(finalConfig.useShortUrl).toBe(false);
|
||||
expect(finalConfig.theme).toBe('current');
|
||||
}).toPass();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,69 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import { SnapshotCreateResponse } from '../../public/app/features/dashboard/services/SnapshotSrv';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
scenes: true,
|
||||
newDashboardSharingComponent: true,
|
||||
},
|
||||
});
|
||||
|
||||
const DASHBOARD_UID = 'ZqZnVvFZz';
|
||||
|
||||
test.describe(
|
||||
'Snapshots',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('Create a snapshot dashboard', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
// Opening a dashboard
|
||||
const dashboardPage = await gotoDashboardPage({ uid: DASHBOARD_UID });
|
||||
|
||||
const panelsToCheck = [
|
||||
'Raw Data Graph',
|
||||
'Last non-null',
|
||||
'min',
|
||||
'Max',
|
||||
'The data from graph above with seriesToColumns transform',
|
||||
];
|
||||
|
||||
// Open the sharing drawer
|
||||
await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.arrowMenu).click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.newShareButton.menu.shareSnapshot)
|
||||
.click();
|
||||
|
||||
// Publish snapshot
|
||||
const createSnapshotPromise = page.waitForResponse(
|
||||
(response) => response.url().includes('/api/snapshots') && response.request().method() === 'POST'
|
||||
);
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardDrawer.ShareSnapshot.publishSnapshot)
|
||||
.click();
|
||||
|
||||
const createResponse = await createSnapshotPromise;
|
||||
expect(createResponse.status()).toBe(200);
|
||||
|
||||
const responseBody: SnapshotCreateResponse = await createResponse.json();
|
||||
|
||||
// Navigate to the snapshot URL
|
||||
const snapshotUrl = getSnapshotUrl(responseBody.key);
|
||||
await page.goto(snapshotUrl);
|
||||
|
||||
// Validate the dashboard controls are rendered
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.Controls)).toBeVisible();
|
||||
|
||||
// Validate the panels are rendered
|
||||
for (const title of panelsToCheck) {
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title))).toBeVisible();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const getSnapshotUrl = (uid: string): string => {
|
||||
return `/dashboard/snapshot/${uid}`;
|
||||
};
|
|
@ -0,0 +1,74 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const DASHBOARD_UID = 'HYaGDGIMk';
|
||||
|
||||
test.use({
|
||||
timezoneId: 'Pacific/Easter',
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Dashboard templating',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('Verify variable interpolation works', async ({ page, gotoDashboardPage }) => {
|
||||
// Open dashboard global variables and interpolation
|
||||
await gotoDashboardPage({ uid: DASHBOARD_UID });
|
||||
|
||||
// Get the actual timezone from the browser context (should be Pacific/Easter due to test.use)
|
||||
const timeZone = await page.evaluate(() => Intl.DateTimeFormat().resolvedOptions().timeZone);
|
||||
const example = `Example: from=now-6h&to=now&timezone=${encodeURIComponent(timeZone)}`;
|
||||
|
||||
const expectedItems: string[] = [
|
||||
'__dashboard = Templating - Global variables and interpolation',
|
||||
'__dashboard.name = Templating - Global variables and interpolation',
|
||||
'__dashboard.uid = HYaGDGIMk',
|
||||
'__org.name = Main Org.',
|
||||
'__org.id = 1',
|
||||
'__user.id = 1',
|
||||
'__user.login = admin',
|
||||
'__user.email = admin@localhost',
|
||||
`Server:raw = A'A"A,BB\\B,CCC`,
|
||||
`Server:regex = (A'A"A|BB\\\\B|CCC)`,
|
||||
`Server:lucene = ("A'A\\"A" OR "BB\\\\B" OR "CCC")`,
|
||||
`Server:glob = {A'A"A,BB\\B,CCC}`,
|
||||
`Server:pipe = A'A"A|BB\\B|CCC`,
|
||||
`Server:distributed = A'A"A,Server=BB\\B,Server=CCC`,
|
||||
`Server:csv = A'A"A,BB\\B,CCC`,
|
||||
`Server:html = A'A"A, BB\\B, CCC`,
|
||||
`Server:json = ["A'A\\"A","BB\\\\B","CCC"]`,
|
||||
`Server:percentencode = %7BA%27A%22A%2CBB%5CB%2CCCC%7D`,
|
||||
`Server:singlequote = 'A\\'A"A','BB\\B','CCC'`,
|
||||
`Server:doublequote = "A'A\\"A","BB\\B","CCC"`,
|
||||
`Server:sqlstring = 'A''A\\"A','BB\\\B','CCC'`,
|
||||
`Server:date = NaN`,
|
||||
`Server:text = All`,
|
||||
`Server:queryparam = var-Server=$__all`,
|
||||
`1 < 2`,
|
||||
example,
|
||||
];
|
||||
|
||||
// Get all list items from the markdown content
|
||||
const listItems = page.locator('.markdown-html li');
|
||||
|
||||
// Verify we have the expected number of items
|
||||
await expect(listItems).toHaveCount(26);
|
||||
|
||||
// Get all the text content from list items
|
||||
const actualItems = await listItems.allTextContents();
|
||||
|
||||
// Compare each expected item with actual items
|
||||
for (let i = 0; i < expectedItems.length; i++) {
|
||||
expect(actualItems[i]).toBe(expectedItems[i]);
|
||||
}
|
||||
|
||||
// Check link interpolation is working correctly
|
||||
const exampleLink = page.locator(`a:has-text("${example}")`);
|
||||
await expect(exampleLink).toHaveAttribute(
|
||||
'href',
|
||||
`https://example.com/?from=now-6h&to=now&timezone=${encodeURIComponent(timeZone)}`
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,97 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const DASHBOARD_UID = '5SdHCasdf';
|
||||
|
||||
const USER = 'dashboard-timepicker-test';
|
||||
const PASSWORD = 'dashboard-timepicker-test';
|
||||
|
||||
// Separate user to isolate changes from other tests
|
||||
test.use({
|
||||
user: {
|
||||
user: USER,
|
||||
password: PASSWORD,
|
||||
},
|
||||
storageState: {
|
||||
cookies: [],
|
||||
origins: [],
|
||||
},
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Dashboard timepicker',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('Shows the correct calendar days with custom timezone set via preferences', async ({
|
||||
createUser,
|
||||
page,
|
||||
gotoDashboardPage,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
}) => {
|
||||
await createUser();
|
||||
// login manually for now
|
||||
await page.getByTestId(selectors.pages.Login.username).fill(USER);
|
||||
await page.getByTestId(selectors.pages.Login.password).fill(PASSWORD);
|
||||
await page.getByTestId(selectors.pages.Login.submit).click();
|
||||
await expect(page.getByTestId(selectors.components.NavToolbar.commandPaletteTrigger)).toBeVisible();
|
||||
|
||||
// Set user preferences for timezone
|
||||
await page.goto('/profile');
|
||||
await page.getByTestId(selectors.components.TimeZonePicker.containerV2).click();
|
||||
await page.getByRole('option', { name: 'Asia/Tokyo' }).click();
|
||||
await page.getByTestId(selectors.components.UserProfile.preferencesSaveButton).click();
|
||||
|
||||
// Open dashboard with time range from 8th to end of 10th.
|
||||
// Will be Tokyo time because of above preference
|
||||
await gotoDashboardPage({
|
||||
uid: DASHBOARD_UID,
|
||||
queryParams: new URLSearchParams({
|
||||
timezone: 'Default',
|
||||
}),
|
||||
});
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.openButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.fromField).fill('2022-06-08 00:00:00');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.toField).fill('2022-06-10 23:59:59');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.calendar.openButton).first().click();
|
||||
|
||||
// Assert that the calendar shows 08 and 09 and 10 as selected days
|
||||
const activeTiles = page.locator('.react-calendar__tile--active, .react-calendar__tile--hasActive');
|
||||
await expect(activeTiles).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('Shows the correct calendar days with custom timezone set via time picker', async ({
|
||||
createUser,
|
||||
page,
|
||||
gotoDashboardPage,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
}) => {
|
||||
await createUser();
|
||||
// login manually for now
|
||||
await page.getByTestId(selectors.pages.Login.username).fill(USER);
|
||||
await page.getByTestId(selectors.pages.Login.password).fill(PASSWORD);
|
||||
await page.getByTestId(selectors.pages.Login.submit).click();
|
||||
await expect(page.getByTestId(selectors.components.NavToolbar.commandPaletteTrigger)).toBeVisible();
|
||||
|
||||
// Open dashboard with time range from 2022-06-08 00:00:00 to 2022-06-10 23:59:59 in Tokyo time
|
||||
await gotoDashboardPage({
|
||||
uid: DASHBOARD_UID,
|
||||
queryParams: new URLSearchParams({
|
||||
timezone: 'Asia/Tokyo',
|
||||
}),
|
||||
});
|
||||
|
||||
// Open dashboard with time range from 8th to end of 10th.
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.openButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.fromField).fill('2022-06-08 00:00:00');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.toField).fill('2022-06-10 23:59:59');
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.calendar.openButton).first().click();
|
||||
|
||||
// Assert that the calendar shows 08 and 09 and 10 as selected days
|
||||
const activeTiles = page.locator('.react-calendar__tile--active, .react-calendar__tile--hasActive');
|
||||
await expect(activeTiles).toHaveCount(3);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,26 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.describe(
|
||||
'Embedded dashboard',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('open test page', async ({ page, dashboardPage, selectors }) => {
|
||||
await page.goto('/dashboards/embedding-test');
|
||||
|
||||
// Verify pie charts are rendered
|
||||
const pieChartSlices = page.locator(
|
||||
`[data-viz-panel-key="panel-11"] [data-testid^="${selectors.components.Panels.Visualization.PieChart.svgSlice}"]`
|
||||
);
|
||||
await expect(pieChartSlices).toHaveCount(5);
|
||||
|
||||
// Verify no url sync
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.openButton).click();
|
||||
await page.locator('label').filter({ hasText: 'Last 1 hour' }).click();
|
||||
|
||||
// Verify URL remains the same (no sync)
|
||||
expect(page.url()).toMatch(/\/dashboards\/embedding-test$/);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,47 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'edediimbjhdz4b/a-tall-dashboard';
|
||||
|
||||
test.describe(
|
||||
'Dashboards',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('should restore scroll position', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
// Verify first panel is visible
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel #1'))
|
||||
).toBeVisible();
|
||||
|
||||
// Scroll to the bottom
|
||||
await page.evaluate(() => {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
});
|
||||
|
||||
// The last panel should be visible...
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel #50'))
|
||||
).toBeVisible();
|
||||
|
||||
// Then we open and close the panel editor
|
||||
// Click on panel menu (it only shows on hover)
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Panels.Panel.menu('Panel #50'))
|
||||
.click({ force: true });
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.menuItems('Edit')).click();
|
||||
|
||||
// Go back to dashboard
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
|
||||
// The last panel should still be visible!
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel #50'))
|
||||
).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,63 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import testDashboard from '../dashboards/TestDashboard.json';
|
||||
|
||||
test.describe(
|
||||
'Import Dashboards Test',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
let dashboardUID: string;
|
||||
|
||||
test('Ensure you can import a number of json test dashboards from a specific test directory', async ({
|
||||
page,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
}) => {
|
||||
await page.goto('/dashboard/import');
|
||||
|
||||
// Fill in the dashboard JSON and name
|
||||
const textarea = dashboardPage.getByGrafanaSelector(selectors.components.DashboardImportPage.textarea);
|
||||
await textarea.fill(JSON.stringify(testDashboard));
|
||||
|
||||
// Submit the JSON
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.DashboardImportPage.submit).click();
|
||||
|
||||
// Fill in the dashboard name and submit
|
||||
const nameField = dashboardPage.getByGrafanaSelector(selectors.components.ImportDashboardForm.name);
|
||||
await expect(nameField).toBeVisible();
|
||||
await nameField.click();
|
||||
await nameField.clear();
|
||||
await nameField.fill(testDashboard.title);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.ImportDashboardForm.submit).click();
|
||||
|
||||
// Wait for dashboard to load and extract UID from URL
|
||||
await page.waitForURL('**/d/**');
|
||||
const url = page.url();
|
||||
const urlParts = url.split('/d/');
|
||||
if (urlParts.length > 1) {
|
||||
dashboardUID = urlParts[1].split('/')[0];
|
||||
}
|
||||
|
||||
// Verify the dashboard title is present in the breadcrumbs
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Breadcrumbs.breadcrumb(testDashboard.title))
|
||||
).toBeVisible();
|
||||
|
||||
// Verify that specific panels from the test dashboard are loaded
|
||||
await expect(page.getByText('Gauge Example')).toBeVisible();
|
||||
await expect(page.getByText('Stat')).toBeVisible();
|
||||
await expect(page.getByText('Time series example')).toBeVisible();
|
||||
});
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
// Clean up the imported dashboard using API
|
||||
if (dashboardUID) {
|
||||
await request.delete(`/api/dashboards/uid/${dashboardUID}`);
|
||||
dashboardUID = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,300 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = '-Y-tnEDWk/templating-nested-template-variables';
|
||||
|
||||
test.describe(
|
||||
'Variables - Load options from Url',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('default options should be correct', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
// Test first variable (A)
|
||||
const firstVariableInput = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A'))
|
||||
.locator('input');
|
||||
await expect(firstVariableInput).toBeVisible();
|
||||
await firstVariableInput.click();
|
||||
|
||||
// Check dropdown has 10 options
|
||||
const firstDropdownOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option).locator('..');
|
||||
await expect(firstDropdownOptions).toHaveCount(10);
|
||||
|
||||
// Check toggle shows 'Selected (1)'
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Select.toggleAllOptions)).toHaveText(
|
||||
'Selected (1)'
|
||||
);
|
||||
|
||||
// Check specific options are visible
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('C'))
|
||||
).toBeVisible();
|
||||
|
||||
// Close dropdown by clicking outside
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
// Test second variable (AA)
|
||||
const secondVariableInput = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('AA'))
|
||||
.locator('input');
|
||||
|
||||
await expect(secondVariableInput).toBeVisible();
|
||||
await secondVariableInput.click();
|
||||
|
||||
// Check dropdown has 10 options
|
||||
const secondDropdownOptions = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Select.option)
|
||||
.locator('..');
|
||||
await expect(secondDropdownOptions).toHaveCount(10);
|
||||
|
||||
// Check toggle shows 'Selected (1)'
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Select.toggleAllOptions)).toHaveText(
|
||||
'Selected (1)'
|
||||
);
|
||||
|
||||
// Check specific options are visible
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AA'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AB'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AC'))
|
||||
).toBeVisible();
|
||||
|
||||
// Close dropdown by clicking outside
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
// Test third variable ($__all)
|
||||
const thirdVariableInput = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('$__all'))
|
||||
.locator('input');
|
||||
|
||||
await expect(thirdVariableInput).toBeVisible();
|
||||
await thirdVariableInput.click();
|
||||
|
||||
// Check dropdown has 10 options
|
||||
const thirdDropdownOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option).locator('..');
|
||||
await expect(thirdDropdownOptions).toHaveCount(10);
|
||||
|
||||
// Check toggle shows 'Selected (1)'
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Select.toggleAllOptions)).toHaveText(
|
||||
'Selected (1)'
|
||||
);
|
||||
|
||||
// Check specific options are visible
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAA'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAB'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAC'))
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('options set in url should load correct options', async ({
|
||||
page,
|
||||
gotoDashboardPage,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
}) => {
|
||||
await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({
|
||||
orgId: '1',
|
||||
'var-datacenter': 'B',
|
||||
'var-server': 'BB',
|
||||
'var-pod': 'BBB',
|
||||
}),
|
||||
});
|
||||
|
||||
// Test first variable (B) - should be selected from URL
|
||||
const firstVariableInput = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('B'))
|
||||
.locator('input');
|
||||
|
||||
await expect(firstVariableInput).toBeVisible();
|
||||
await firstVariableInput.click();
|
||||
|
||||
// Check dropdown has 10 options
|
||||
const firstDropdownOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option).locator('..');
|
||||
await expect(firstDropdownOptions).toHaveCount(10);
|
||||
|
||||
// Check toggle shows 'Selected (1)'
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Select.toggleAllOptions)).toHaveText(
|
||||
'Selected (1)'
|
||||
);
|
||||
|
||||
// Check specific options are visible
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('C'))
|
||||
).toBeVisible();
|
||||
|
||||
// Close dropdown by clicking outside
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
// Test second variable (BB) - should be selected from URL
|
||||
const secondVariableInput = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('BB'))
|
||||
.locator('input');
|
||||
|
||||
await expect(secondVariableInput).toBeVisible();
|
||||
await secondVariableInput.click();
|
||||
|
||||
// Check dropdown has 10 options
|
||||
const secondDropdownOptions = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Select.option)
|
||||
.locator('..');
|
||||
await expect(secondDropdownOptions).toHaveCount(10);
|
||||
|
||||
// Check toggle shows 'Selected (1)'
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Select.toggleAllOptions)).toHaveText(
|
||||
'Selected (1)'
|
||||
);
|
||||
|
||||
// Check specific options are visible
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BA'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BB'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BC'))
|
||||
).toBeVisible();
|
||||
|
||||
// Close dropdown by clicking outside
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
// Test third variable (BBB) - should be selected from URL
|
||||
const thirdVariableInput = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('BBB'))
|
||||
.locator('input');
|
||||
|
||||
await expect(thirdVariableInput).toBeVisible();
|
||||
await thirdVariableInput.click();
|
||||
|
||||
// Check dropdown has 10 options
|
||||
const thirdDropdownOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option).locator('..');
|
||||
await expect(thirdDropdownOptions).toHaveCount(10);
|
||||
|
||||
// Check toggle shows 'Selected (1)'
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Select.toggleAllOptions)).toHaveText(
|
||||
'Selected (1)'
|
||||
);
|
||||
|
||||
// Check specific options are visible
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBA'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBB'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBC'))
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('options set in url that do not exist should load correct options', async ({
|
||||
page,
|
||||
gotoDashboardPage,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
}) => {
|
||||
// Handle uncaught exceptions that are expected
|
||||
page.on('pageerror', (error) => {
|
||||
if (error.message.includes("Couldn't find any field of type string in the results.")) {
|
||||
// This error is expected and should not fail the test
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
|
||||
await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({
|
||||
orgId: '1',
|
||||
'var-datacenter': 'X',
|
||||
}),
|
||||
});
|
||||
|
||||
// Test first variable (X) - invalid value from URL
|
||||
const firstVariableInput = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('X'))
|
||||
.locator('input');
|
||||
|
||||
await expect(firstVariableInput).toBeVisible();
|
||||
await firstVariableInput.click();
|
||||
|
||||
// Check dropdown has 10 options
|
||||
const firstDropdownOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option).locator('..');
|
||||
await expect(firstDropdownOptions).toHaveCount(10);
|
||||
|
||||
// Check toggle shows 'Selected (1)'
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Select.toggleAllOptions)).toHaveText(
|
||||
'Selected (1)'
|
||||
);
|
||||
|
||||
// Check specific options are visible (should fall back to default options)
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('C'))
|
||||
).toBeVisible();
|
||||
|
||||
// Close dropdown by clicking outside
|
||||
await page.locator('body').click();
|
||||
|
||||
// Check that second variable shows $__all (should be 2 instances)
|
||||
const allVariables = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('$__all')
|
||||
);
|
||||
|
||||
await expect(allVariables).toHaveCount(2);
|
||||
await expect(allVariables.first()).toBeVisible();
|
||||
await expect(allVariables.last()).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,65 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
test.describe(
|
||||
'Variables - Constant',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a new constant variable', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }),
|
||||
});
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
// Create a new "Constant" variable
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CallToActionCard.buttonV2('Add variable')).click();
|
||||
|
||||
// Select "Constant" type from dropdown
|
||||
const typeSelect = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2
|
||||
);
|
||||
await typeSelect.locator('input').fill('Constant');
|
||||
await typeSelect.locator('input').press('Enter');
|
||||
|
||||
// Set variable name
|
||||
const nameInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2
|
||||
);
|
||||
await nameInput.fill('VariableUnderTest');
|
||||
|
||||
// Set constant value
|
||||
const constantInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.ConstantVariable.constantOptionsQueryInputV2
|
||||
);
|
||||
await constantInput.fill('pesto');
|
||||
|
||||
// Set label
|
||||
const labelInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2
|
||||
);
|
||||
await labelInput.fill('Variable under test');
|
||||
|
||||
// Navigate back to the homepage and change the selected variable value
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton)
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.RefreshPicker.runButtonV2).click();
|
||||
|
||||
// Assert the variable was rendered in the markdown content
|
||||
await expect(page.locator('.markdown-html').first()).toContainText('VariableUnderTest: pesto');
|
||||
|
||||
// Assert the variable is not visible in the dashboard navigation (constant variables are hidden)
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('Variable under test'))
|
||||
).toBeHidden();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,121 @@
|
|||
import { test, expect, DashboardPage, E2ESelectorGroups } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
async function fillInCustomVariable(
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
name: string,
|
||||
label: string,
|
||||
value: string
|
||||
) {
|
||||
// Select "Custom" type from dropdown
|
||||
const typeSelect = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2
|
||||
);
|
||||
await typeSelect.locator('input').fill('Custom');
|
||||
await typeSelect.locator('input').press('Enter');
|
||||
|
||||
// Set variable name
|
||||
const nameInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2
|
||||
);
|
||||
await nameInput.fill(name);
|
||||
|
||||
// Set label
|
||||
const labelInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2
|
||||
);
|
||||
await labelInput.fill(label);
|
||||
|
||||
// Set custom values
|
||||
const customValueInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput
|
||||
);
|
||||
await customValueInput.fill(value);
|
||||
await customValueInput.blur();
|
||||
}
|
||||
|
||||
async function assertPreviewValues(
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
expectedValues: string[]
|
||||
) {
|
||||
for (let i = 0; i < expectedValues.length; i++) {
|
||||
const previewOption = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption)
|
||||
.nth(i);
|
||||
await expect(previewOption).toHaveText(expectedValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
test.describe(
|
||||
'Variables - Custom',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a custom template variable', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }),
|
||||
});
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
// Create a new "Custom" variable
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CallToActionCard.buttonV2('Add variable')).click();
|
||||
await fillInCustomVariable(dashboardPage, selectors, 'VariableUnderTest', 'Variable under test', 'one,two,three');
|
||||
await assertPreviewValues(dashboardPage, selectors, ['one', 'two', 'three']);
|
||||
|
||||
// Navigate back to the homepage and change the selected variable value
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton)
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('one'))
|
||||
.click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Select.option).filter({ hasText: 'two' }).click();
|
||||
|
||||
// Assert it was rendered
|
||||
await expect(page.locator('.markdown-html').first()).toContainText('VariableUnderTest: two');
|
||||
});
|
||||
|
||||
test('can add a custom template variable with labels', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }),
|
||||
});
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
// Create a new "Custom" variable
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CallToActionCard.buttonV2('Add variable')).click();
|
||||
await fillInCustomVariable(
|
||||
dashboardPage,
|
||||
selectors,
|
||||
'VariableUnderTest',
|
||||
'Variable under test',
|
||||
'One : 1,Two : 2, Three : 3'
|
||||
);
|
||||
await assertPreviewValues(dashboardPage, selectors, ['One', 'Two', 'Three']);
|
||||
|
||||
// Navigate back to the homepage and change the selected variable value
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton)
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('1'))
|
||||
.click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Select.option).filter({ hasText: 'Two' }).click();
|
||||
|
||||
// Assert it was rendered (the value "2" should be displayed, not the label "Two")
|
||||
await expect(page.locator('.markdown-html').first()).toContainText('VariableUnderTest: 2');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,74 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
test.describe(
|
||||
'Variables - Datasource',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a new datasource variable', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }),
|
||||
});
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
// Create a new "Datasource" variable
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CallToActionCard.buttonV2('Add variable')).click();
|
||||
const typeSelect = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2
|
||||
);
|
||||
await typeSelect.locator('input').fill('Data source');
|
||||
await typeSelect.locator('input').press('Enter');
|
||||
const nameInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2
|
||||
);
|
||||
await nameInput.fill('VariableUnderTest');
|
||||
const labelInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2
|
||||
);
|
||||
await labelInput.fill('Variable under test');
|
||||
|
||||
// If this is failing, make sure there are Prometheus datasources named "gdev-prometheus" and "gdev-slow-prometheus"
|
||||
// Or update to match available gdev datasources for testing
|
||||
const datasourceSelect = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.DatasourceVariable.datasourceSelect
|
||||
);
|
||||
await datasourceSelect.locator('input').fill('Prometheus');
|
||||
await datasourceSelect.locator('input').press('Enter');
|
||||
|
||||
const previewOptions = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption
|
||||
);
|
||||
await expect(previewOptions.first()).toContainText('gdev-prometheus');
|
||||
await expect(previewOptions.last()).toContainText('gdev-slow-prometheus');
|
||||
|
||||
// Navigate back to the homepage and change the selected variable value
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton)
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.RefreshPicker.runButtonV2).click();
|
||||
|
||||
// Change the selected variable value
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('gdev-prometheus')
|
||||
)
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.Select.option)
|
||||
.filter({ hasText: 'gdev-slow-prometheus' })
|
||||
.click();
|
||||
|
||||
// Assert it was rendered
|
||||
await expect(page.locator('.markdown-html').first()).toContainText('VariableUnderTest: gdev-slow-prometheus-uid');
|
||||
await expect(page.locator('.markdown-html').nth(1)).toContainText('VariableUnderTestText: gdev-slow-prometheus');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,84 @@
|
|||
import { test, expect, DashboardPage, E2ESelectorGroups } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
async function assertPreviewValues(
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
expectedValues: string[]
|
||||
) {
|
||||
for (let i = 0; i < expectedValues.length; i++) {
|
||||
const previewOption = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption)
|
||||
.nth(i);
|
||||
await expect(previewOption).toHaveText(expectedValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
test.describe(
|
||||
'Variables - Interval',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a new interval variable', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }),
|
||||
});
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
// Create a new "Interval" variable
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CallToActionCard.buttonV2('Add variable')).click();
|
||||
const typeSelect = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2
|
||||
);
|
||||
await typeSelect.locator('input').fill('Interval');
|
||||
await typeSelect.locator('input').press('Enter');
|
||||
const nameInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2
|
||||
);
|
||||
await nameInput.fill('VariableUnderTest');
|
||||
const labelInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2
|
||||
);
|
||||
await labelInput.fill('Variable under test');
|
||||
|
||||
// Set interval values
|
||||
const intervalInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.IntervalVariable.intervalsValueInput
|
||||
);
|
||||
await intervalInput.fill('10s,10m,60m,90m,1h30m');
|
||||
await intervalInput.blur();
|
||||
await assertPreviewValues(dashboardPage, selectors, ['10s', '10m', '60m', '90m', '1h30m']);
|
||||
|
||||
// Navigate back to the homepage and change the selected variable value
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton)
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.RefreshPicker.runButtonV2).click();
|
||||
|
||||
// Verify the variable label and initial value, then change the selected value
|
||||
const variableLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemLabels('Variable under test')
|
||||
);
|
||||
|
||||
// Find the value element (next sibling) and verify it shows the default value
|
||||
const variableValue = variableLabel.locator('+ div');
|
||||
await expect(variableValue).toHaveText('10s');
|
||||
|
||||
// Click to open the dropdown
|
||||
await variableValue.click();
|
||||
|
||||
// Select '1h30m' from the dropdown
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Select.option).filter({ hasText: '1h30m' }).click();
|
||||
|
||||
// Assert it was rendered
|
||||
await expect(page.locator('.markdown-html').first()).toContainText('VariableUnderTest: 1h30m');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,251 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = '-Y-tnEDWk/templating-nested-template-variables';
|
||||
const DASHBOARD_NAME = 'Templating - Nested Template Variables';
|
||||
|
||||
test.describe(
|
||||
'Variables - Query - Add variable',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('query variable should be default and default fields should be correct', async ({
|
||||
page,
|
||||
gotoDashboardPage,
|
||||
selectors,
|
||||
}) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }),
|
||||
});
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
await page.getByTestId(selectors.pages.Dashboard.Settings.Variables.List.newButton).click();
|
||||
const nameInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2
|
||||
);
|
||||
await expect(nameInput).toBeVisible();
|
||||
await expect(nameInput).toHaveAttribute('placeholder', 'Variable name');
|
||||
await expect(nameInput).toHaveValue('query0');
|
||||
|
||||
const typeSelect = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2
|
||||
);
|
||||
await expect(typeSelect).toBeVisible();
|
||||
const singleValue = typeSelect.locator('div[class*="-singleValue"]');
|
||||
await expect(singleValue).toHaveText('Query');
|
||||
|
||||
const labelInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2
|
||||
);
|
||||
await expect(labelInput).toBeVisible();
|
||||
await expect(labelInput).toHaveAttribute('placeholder', 'Label name');
|
||||
await expect(labelInput).toHaveValue('');
|
||||
|
||||
const descriptionInput = page.locator('[placeholder="Descriptive text"]');
|
||||
await expect(descriptionInput).toBeVisible();
|
||||
await expect(descriptionInput).toHaveAttribute('placeholder', 'Descriptive text');
|
||||
await expect(descriptionInput).toHaveValue('');
|
||||
|
||||
await expect(page.locator('label').filter({ hasText: 'Hide' })).toBeVisible();
|
||||
|
||||
// Check datasource selector
|
||||
const datasourceSelect = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsDataSourceSelect
|
||||
);
|
||||
await expect(datasourceSelect).toBeVisible();
|
||||
await expect(datasourceSelect).toHaveAttribute('placeholder', 'gdev-testdata');
|
||||
|
||||
await expect(page.locator('label').filter({ hasText: 'Refresh' })).toBeVisible();
|
||||
await expect(page.locator('label').filter({ hasText: 'On dashboard load' })).toBeVisible();
|
||||
|
||||
const regexInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2
|
||||
);
|
||||
await expect(regexInput).toBeVisible();
|
||||
await expect(regexInput).toHaveAttribute('placeholder', '/.*-(?<text>.*)-(?<value>.*)-.*/');
|
||||
await expect(regexInput).toHaveValue('');
|
||||
|
||||
const sortSelect = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsSortSelectV2
|
||||
);
|
||||
await expect(sortSelect).toBeVisible();
|
||||
const sortSingleValue = sortSelect.locator('div[class*="-singleValue"]');
|
||||
await expect(sortSingleValue).toHaveText('Disabled');
|
||||
|
||||
// Check Multi-value checkbox
|
||||
const multiValueLabel = page.locator('label').filter({ hasText: 'Multi-value' });
|
||||
const multiValueCheckbox = multiValueLabel.locator('input[type="checkbox"]');
|
||||
await expect(multiValueCheckbox).toBeChecked({
|
||||
checked: false,
|
||||
});
|
||||
|
||||
// Check Include All option checkbox
|
||||
const includeAllLabel = page.locator('label').filter({ hasText: 'Include All option' });
|
||||
const includeAllCheckbox = includeAllLabel.locator('input[type="checkbox"]');
|
||||
await expect(includeAllCheckbox).toBeChecked({
|
||||
checked: false,
|
||||
});
|
||||
|
||||
// Check preview and custom all input don't exist initially
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption
|
||||
)
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
|
||||
)
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
test('adding a single value query variable', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }),
|
||||
});
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
// Click the "New" button to add a variable
|
||||
await page.getByTestId(selectors.pages.Dashboard.Settings.Variables.List.newButton).click();
|
||||
|
||||
const labelInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2
|
||||
);
|
||||
await labelInput.fill('a label');
|
||||
const descriptionInput = page.locator('[placeholder="Descriptive text"]');
|
||||
await descriptionInput.fill('a description');
|
||||
const datasourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.inputV2);
|
||||
await datasourcePicker.fill('gdev-testdata');
|
||||
const datasourceList = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.dataSourceList);
|
||||
await datasourceList.getByText('gdev-testdata').click();
|
||||
const queryInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput
|
||||
);
|
||||
await queryInput.fill('*');
|
||||
const regexInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2
|
||||
);
|
||||
await regexInput.fill('/.*C.*/');
|
||||
await regexInput.blur();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
// Navigate back to dashboard
|
||||
const submitButton = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton
|
||||
);
|
||||
await submitButton.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
|
||||
// Check variable appears in submenu
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('a label'))
|
||||
).toBeVisible();
|
||||
|
||||
const submenuItems = dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItem);
|
||||
await expect(submenuItems).toHaveCount(4);
|
||||
const fourthItem = submenuItems.nth(3);
|
||||
const input = fourthItem.locator('input');
|
||||
await input.click();
|
||||
|
||||
// Check dropdown has 1 option containing 'C'
|
||||
const options = dashboardPage.getByGrafanaSelector(selectors.components.Select.option);
|
||||
await expect(options).toHaveCount(1);
|
||||
await expect(options).toContainText('C');
|
||||
});
|
||||
|
||||
test('adding a multi value query variable', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }),
|
||||
});
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
await page.getByTestId(selectors.pages.Dashboard.Settings.Variables.List.newButton).click();
|
||||
|
||||
const labelInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2
|
||||
);
|
||||
await labelInput.fill('a label');
|
||||
const descriptionInput = page.locator('[placeholder="Descriptive text"]');
|
||||
await descriptionInput.fill('a description');
|
||||
const datasourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.inputV2);
|
||||
await datasourcePicker.fill('gdev-testdata');
|
||||
const datasourceList = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.dataSourceList);
|
||||
await datasourceList.getByText('gdev-testdata').click();
|
||||
const queryInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput
|
||||
);
|
||||
await queryInput.fill('*');
|
||||
const regexInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2
|
||||
);
|
||||
await regexInput.fill('/.*C.*/');
|
||||
|
||||
// Enable Multi-value
|
||||
const multiValueLabel = page.locator('label').filter({ hasText: 'Multi-value' });
|
||||
const multiValueCheckbox = multiValueLabel.locator('input[type="checkbox"]');
|
||||
await multiValueCheckbox.click({ force: true });
|
||||
await expect(multiValueCheckbox).toBeChecked();
|
||||
|
||||
// Enable Include All option
|
||||
const includeAllLabel = page.locator('label').filter({ hasText: 'Include All option' });
|
||||
const includeAllCheckbox = includeAllLabel.locator('input[type="checkbox"]');
|
||||
await includeAllCheckbox.click({ force: true });
|
||||
await expect(includeAllCheckbox).toBeChecked();
|
||||
|
||||
const customAllInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput
|
||||
);
|
||||
await expect(customAllInput).toHaveValue('');
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption
|
||||
)
|
||||
).toHaveCount(2);
|
||||
|
||||
// Navigate back to dashboard
|
||||
const submitButton = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton
|
||||
);
|
||||
await submitButton.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
|
||||
// Check variable appears in submenu
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('a label'))
|
||||
).toBeVisible();
|
||||
|
||||
const submenuItems = dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItem);
|
||||
await expect(submenuItems).toHaveCount(4);
|
||||
|
||||
const fourthItem = submenuItems.nth(3);
|
||||
const input = fourthItem.locator('input');
|
||||
await input.click();
|
||||
|
||||
// Check dropdown has 3 options (All + filtered results)
|
||||
const options = dashboardPage.getByGrafanaSelector(selectors.components.Select.option);
|
||||
await expect(options).toHaveCount(3);
|
||||
|
||||
// Check toggle shows Selected (1)
|
||||
const toggleAll = dashboardPage.getByGrafanaSelector(selectors.components.Select.toggleAllOptions);
|
||||
await expect(toggleAll).toHaveText('Selected (1)');
|
||||
|
||||
// Check options contain 'All' and 'C'
|
||||
await expect(options.filter({ hasText: 'All' })).toBeVisible();
|
||||
// need to use regex here else this will also match "Selected"
|
||||
await expect(options.filter({ hasText: /^C$/ })).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,56 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
const DASHBOARD_NAME = 'Test variable output';
|
||||
|
||||
test.describe(
|
||||
'Variables - Text box',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('can add a new text box variable', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }),
|
||||
});
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
|
||||
// Create a new "text box" variable
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.CallToActionCard.buttonV2('Add variable')).click();
|
||||
const typeSelect = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2
|
||||
);
|
||||
await typeSelect.locator('input').fill('Textbox');
|
||||
await typeSelect.locator('input').press('Enter');
|
||||
const nameInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2
|
||||
);
|
||||
await nameInput.fill('VariableUnderTest');
|
||||
const labelInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2
|
||||
);
|
||||
await labelInput.fill('Variable under test');
|
||||
const textBoxInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.Settings.Variables.Edit.TextBoxVariable.textBoxOptionsQueryInputV2
|
||||
);
|
||||
await textBoxInput.fill('cat-dog');
|
||||
|
||||
// Navigate back to the dashboard and change the selected variable value
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton)
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
|
||||
.click();
|
||||
const submenuItem = dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItem);
|
||||
|
||||
const textInput = submenuItem.locator('input');
|
||||
await textInput.fill('dog-cat');
|
||||
await textInput.blur();
|
||||
|
||||
// Assert it was rendered
|
||||
await expect(page.locator('.markdown-html').first()).toContainText('VariableUnderTest: dog-cat');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,132 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'WVpf2jp7z/repeating-a-panel-horizontally';
|
||||
|
||||
test.describe(
|
||||
'Repeating a panel horizontally',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('should be able to repeat a panel horizontally', async ({ gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
let prevLeft = Number.NEGATIVE_INFINITY;
|
||||
let prevTop: number | null = null;
|
||||
const panelTitles = ['Panel Title 1', 'Panel Title 2', 'Panel Title 3'];
|
||||
|
||||
for (const title of panelTitles) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeVisible();
|
||||
|
||||
const boundingBox = await panel.boundingBox();
|
||||
expect(boundingBox).not.toBeNull();
|
||||
|
||||
if (boundingBox) {
|
||||
const { x: left, y: top } = boundingBox;
|
||||
expect(left).toBeGreaterThan(prevLeft);
|
||||
if (prevTop !== null) {
|
||||
expect(top).toBe(prevTop);
|
||||
}
|
||||
|
||||
prevLeft = left;
|
||||
prevTop = top;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('responds to changes to the variables', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
const panelTitles = ['Panel Title 1', 'Panel Title 2', 'Panel Title 3'];
|
||||
|
||||
// Verify all panels are initially visible
|
||||
for (const title of panelTitles) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeVisible();
|
||||
}
|
||||
|
||||
// Change to only show panels 1 + 3
|
||||
const horizontalVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('horizontal'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await horizontalVariable.click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('1'))
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('3'))
|
||||
.click();
|
||||
|
||||
// blur the dropdown
|
||||
await page.locator('body').click();
|
||||
|
||||
// Verify positioning of shown panels
|
||||
let prevLeft = Number.NEGATIVE_INFINITY;
|
||||
let prevTop: number | null = null;
|
||||
const panelsShown = ['Panel Title 1', 'Panel Title 3'];
|
||||
const panelsNotShown = ['Panel Title 2'];
|
||||
|
||||
for (const title of panelsShown) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeVisible();
|
||||
|
||||
const boundingBox = await panel.boundingBox();
|
||||
expect(boundingBox).not.toBeNull();
|
||||
|
||||
if (boundingBox) {
|
||||
const { x: left, y: top } = boundingBox;
|
||||
expect(left).toBeGreaterThan(prevLeft);
|
||||
if (prevTop !== null) {
|
||||
expect(top).toBe(prevTop);
|
||||
}
|
||||
|
||||
prevLeft = left;
|
||||
prevTop = top;
|
||||
}
|
||||
}
|
||||
|
||||
for (const title of panelsNotShown) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeHidden();
|
||||
}
|
||||
});
|
||||
|
||||
test('loads a dashboard based on the query params correctly', async ({ gotoDashboardPage, selectors }) => {
|
||||
// Have to manually add the queryParams to the url because they have the same name
|
||||
const dashboardPage = await gotoDashboardPage({ uid: `${PAGE_UNDER_TEST}?var-horizontal=1&var-horizontal=3` });
|
||||
|
||||
let prevLeft = Number.NEGATIVE_INFINITY;
|
||||
let prevTop: number | null = null;
|
||||
const panelsShown = ['Panel Title 1', 'Panel Title 3'];
|
||||
const panelsNotShown = ['Panel Title 2'];
|
||||
|
||||
// Check correct panels are displayed with proper positioning
|
||||
for (const title of panelsShown) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeVisible();
|
||||
|
||||
const boundingBox = await panel.boundingBox();
|
||||
expect(boundingBox).not.toBeNull();
|
||||
|
||||
if (boundingBox) {
|
||||
const { x: left, y: top } = boundingBox;
|
||||
expect(left).toBeGreaterThan(prevLeft);
|
||||
if (prevTop !== null) {
|
||||
expect(top).toBe(prevTop);
|
||||
}
|
||||
|
||||
prevLeft = left;
|
||||
prevTop = top;
|
||||
}
|
||||
}
|
||||
|
||||
for (const title of panelsNotShown) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeHidden();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,132 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'OY8Ghjt7k/repeating-a-panel-vertically';
|
||||
|
||||
test.describe(
|
||||
'Repeating a panel vertically',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('should be able to repeat a panel vertically', async ({ gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
let prevTop = Number.NEGATIVE_INFINITY;
|
||||
let prevLeft: number | null = null;
|
||||
const panelTitles = ['Panel Title 1', 'Panel Title 2', 'Panel Title 3'];
|
||||
|
||||
for (const title of panelTitles) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeVisible();
|
||||
|
||||
const boundingBox = await panel.boundingBox();
|
||||
expect(boundingBox).not.toBeNull();
|
||||
|
||||
if (boundingBox) {
|
||||
const { x: left, y: top } = boundingBox;
|
||||
expect(top).toBeGreaterThan(prevTop);
|
||||
if (prevLeft !== null) {
|
||||
expect(left).toBe(prevLeft);
|
||||
}
|
||||
|
||||
prevLeft = left;
|
||||
prevTop = top;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('responds to changes to the variables', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
const panelTitles = ['Panel Title 1', 'Panel Title 2', 'Panel Title 3'];
|
||||
|
||||
// Verify all panels are initially visible
|
||||
for (const title of panelTitles) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeVisible();
|
||||
}
|
||||
|
||||
// Change to only show panels 1 + 3
|
||||
const verticalVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('vertical'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await verticalVariable.click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('1'))
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('3'))
|
||||
.click();
|
||||
|
||||
// blur the dropdown
|
||||
await page.locator('body').click();
|
||||
|
||||
// Verify positioning of shown panels
|
||||
let prevTop = Number.NEGATIVE_INFINITY;
|
||||
let prevLeft: number | null = null;
|
||||
const panelsShown = ['Panel Title 1', 'Panel Title 3'];
|
||||
const panelsNotShown = ['Panel Title 2'];
|
||||
|
||||
for (const title of panelsShown) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeVisible();
|
||||
|
||||
const boundingBox = await panel.boundingBox();
|
||||
expect(boundingBox).not.toBeNull();
|
||||
|
||||
if (boundingBox) {
|
||||
const { x: left, y: top } = boundingBox;
|
||||
expect(top).toBeGreaterThan(prevTop);
|
||||
if (prevLeft !== null) {
|
||||
expect(left).toBe(prevLeft);
|
||||
}
|
||||
|
||||
prevLeft = left;
|
||||
prevTop = top;
|
||||
}
|
||||
}
|
||||
|
||||
for (const title of panelsNotShown) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeHidden();
|
||||
}
|
||||
});
|
||||
|
||||
test('loads a dashboard based on the query params correctly', async ({ gotoDashboardPage, selectors }) => {
|
||||
// Have to manually add the queryParams to the url because they have the same name
|
||||
const dashboardPage = await gotoDashboardPage({ uid: `${PAGE_UNDER_TEST}?var-vertical=1&var-vertical=3` });
|
||||
|
||||
let prevTop = Number.NEGATIVE_INFINITY;
|
||||
let prevLeft: number | null = null;
|
||||
const panelsShown = ['Panel Title 1', 'Panel Title 3'];
|
||||
const panelsNotShown = ['Panel Title 2'];
|
||||
|
||||
// Check correct panels are displayed with proper positioning
|
||||
for (const title of panelsShown) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeVisible();
|
||||
|
||||
const boundingBox = await panel.boundingBox();
|
||||
expect(boundingBox).not.toBeNull();
|
||||
|
||||
if (boundingBox) {
|
||||
const { x: left, y: top } = boundingBox;
|
||||
expect(top).toBeGreaterThan(prevTop);
|
||||
if (prevLeft !== null) {
|
||||
expect(left).toBe(prevLeft);
|
||||
}
|
||||
|
||||
prevLeft = left;
|
||||
prevTop = top;
|
||||
}
|
||||
}
|
||||
|
||||
for (const title of panelsNotShown) {
|
||||
const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title));
|
||||
await expect(panel).toBeHidden();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,114 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'dtpl2Ctnk/repeating-an-empty-row';
|
||||
|
||||
test.describe(
|
||||
'Repeating empty rows',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('should be able to repeat empty rows vertically', async ({ gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
let prevTop = Number.NEGATIVE_INFINITY;
|
||||
const rowTitles = ['Row title 1', 'Row title 2', 'Row title 3'];
|
||||
|
||||
for (const title of rowTitles) {
|
||||
const row = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(title));
|
||||
await expect(row).toBeVisible();
|
||||
|
||||
const boundingBox = await row.boundingBox();
|
||||
expect(boundingBox).not.toBeNull();
|
||||
|
||||
if (boundingBox) {
|
||||
const { y: top } = boundingBox;
|
||||
expect(top).toBeGreaterThan(prevTop);
|
||||
prevTop = top;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('responds to changes to the variables', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
const rowTitles = ['Row title 1', 'Row title 2', 'Row title 3'];
|
||||
|
||||
// Verify all rows are initially visible
|
||||
for (const title of rowTitles) {
|
||||
const row = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(title));
|
||||
await expect(row).toBeVisible();
|
||||
}
|
||||
|
||||
// Change to only show rows 1 + 3
|
||||
const rowVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('row'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await rowVariable.click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('1'))
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('3'))
|
||||
.click();
|
||||
|
||||
// blur the dropdown
|
||||
await page.locator('body').click();
|
||||
|
||||
// Verify positioning of shown rows
|
||||
let prevTop = Number.NEGATIVE_INFINITY;
|
||||
const rowsShown = ['Row title 1', 'Row title 3'];
|
||||
const rowsNotShown = ['Row title 2'];
|
||||
|
||||
for (const title of rowsShown) {
|
||||
const row = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(title));
|
||||
await expect(row).toBeVisible();
|
||||
|
||||
const boundingBox = await row.boundingBox();
|
||||
expect(boundingBox).not.toBeNull();
|
||||
|
||||
if (boundingBox) {
|
||||
const { y: top } = boundingBox;
|
||||
expect(top).toBeGreaterThan(prevTop);
|
||||
prevTop = top;
|
||||
}
|
||||
}
|
||||
|
||||
for (const title of rowsNotShown) {
|
||||
const row = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(title));
|
||||
await expect(row).toBeHidden();
|
||||
}
|
||||
});
|
||||
|
||||
test('loads a dashboard based on the query params correctly', async ({ gotoDashboardPage, selectors }) => {
|
||||
// Have to manually add the queryParams to the url because they have the same name
|
||||
const dashboardPage = await gotoDashboardPage({ uid: `${PAGE_UNDER_TEST}?var-row=1&var-row=3` });
|
||||
|
||||
let prevTop = Number.NEGATIVE_INFINITY;
|
||||
const rowsShown = ['Row title 1', 'Row title 3'];
|
||||
const rowsNotShown = ['Row title 2'];
|
||||
|
||||
// Check correct rows are displayed with proper positioning
|
||||
for (const title of rowsShown) {
|
||||
const row = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(title));
|
||||
await expect(row).toBeVisible();
|
||||
|
||||
const boundingBox = await row.boundingBox();
|
||||
expect(boundingBox).not.toBeNull();
|
||||
|
||||
if (boundingBox) {
|
||||
const { y: top } = boundingBox;
|
||||
expect(top).toBeGreaterThan(prevTop);
|
||||
prevTop = top;
|
||||
}
|
||||
}
|
||||
|
||||
for (const title of rowsNotShown) {
|
||||
const row = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title(title));
|
||||
await expect(row).toBeHidden();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,280 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = '-Y-tnEDWk/templating-nested-template-variables';
|
||||
|
||||
test.describe(
|
||||
'Variables - Set options from ui',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('clicking a value that is not part of dependents options should change these to All', async ({
|
||||
gotoDashboardPage,
|
||||
selectors,
|
||||
page,
|
||||
}) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({
|
||||
orgId: '1',
|
||||
'var-datacenter': 'A',
|
||||
'var-server': 'AA',
|
||||
'var-pod': 'AAA',
|
||||
}),
|
||||
});
|
||||
|
||||
const datacenterVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await expect(datacenterVariable).toBeVisible();
|
||||
await datacenterVariable.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A'))
|
||||
.click();
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B'))
|
||||
.click();
|
||||
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('B')
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
const serverVariableAllOptions = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('$__all')
|
||||
);
|
||||
await expect(serverVariableAllOptions).toHaveCount(2);
|
||||
|
||||
const firstServerDropdown = serverVariableAllOptions.nth(0);
|
||||
await expect(firstServerDropdown).toBeVisible();
|
||||
await firstServerDropdown.locator('input').click();
|
||||
|
||||
const selectOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option).locator('..');
|
||||
await expect(selectOptions).toHaveCount(10);
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Select.toggleAllOptions)).toHaveText(
|
||||
'Selected (1)'
|
||||
);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BA'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BB'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BC'))
|
||||
).toBeVisible();
|
||||
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
const podVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('pod'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await podVariable.click();
|
||||
|
||||
// length is 11 because of virtualized select options
|
||||
const podSelectOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option).locator('..');
|
||||
await expect(podSelectOptions).toHaveCount(11);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAA'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAB'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAC'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAD'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAE'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAF'))
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('adding a value that is not part of dependents options should add the new values dependant options', async ({
|
||||
gotoDashboardPage,
|
||||
selectors,
|
||||
page,
|
||||
}) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({
|
||||
orgId: '1',
|
||||
'var-datacenter': 'A',
|
||||
'var-server': 'AA',
|
||||
'var-pod': 'AAA',
|
||||
}),
|
||||
});
|
||||
|
||||
// Click on datacenter variable dropdown
|
||||
const datacenterVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A'))
|
||||
.locator('input');
|
||||
await expect(datacenterVariable).toBeVisible();
|
||||
await datacenterVariable.click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B'))
|
||||
.click();
|
||||
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Select.toggleAllOptions)).toHaveText(
|
||||
'Selected (2)'
|
||||
);
|
||||
|
||||
// Close dropdown
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
// Verify datacenter shows "A,B"
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A,B')
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
const serverVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('AA'))
|
||||
.locator('input');
|
||||
await expect(serverVariable).toBeVisible();
|
||||
await serverVariable.click();
|
||||
|
||||
const serverSelectOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option);
|
||||
await expect(serverSelectOptions).toHaveCount(11);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AA'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AB'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AC'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AD'))
|
||||
).toBeVisible();
|
||||
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
// Click on pod variable dropdown
|
||||
const podVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('AAA'))
|
||||
.locator('input');
|
||||
await expect(podVariable).toBeVisible();
|
||||
await podVariable.click();
|
||||
|
||||
// Verify pod dropdown has 10 options
|
||||
const podSelectOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option);
|
||||
await expect(podSelectOptions).toHaveCount(10);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAA'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAB'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAC'))
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('removing a value that is part of dependents options should remove the new values dependant options', async ({
|
||||
gotoDashboardPage,
|
||||
selectors,
|
||||
page,
|
||||
}) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
// can't use queryParams here because it won't work with the multiple values for each variable
|
||||
uid: `${PAGE_UNDER_TEST}?orgId=1&var-datacenter=A&var-datacenter=B&var-server=AA&var-server=BB&var-pod=AAA&var-pod=BBB`,
|
||||
});
|
||||
|
||||
// Click on datacenter variable dropdown
|
||||
const datacenterVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A,B'))
|
||||
.locator('input');
|
||||
await expect(datacenterVariable).toBeVisible();
|
||||
await datacenterVariable.click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A'))
|
||||
.click();
|
||||
|
||||
// Close dropdown
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('B')
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
// Click on server variable dropdown
|
||||
const serverVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('BB'))
|
||||
.locator('input');
|
||||
await expect(serverVariable).toBeVisible();
|
||||
await serverVariable.click();
|
||||
|
||||
const serverSelectOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option);
|
||||
await expect(serverSelectOptions).toHaveCount(10);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BA'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BB'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BC'))
|
||||
).toBeVisible();
|
||||
|
||||
// Close dropdown
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
// Click on pod variable dropdown
|
||||
const podVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('BBB'))
|
||||
.locator('input');
|
||||
await expect(podVariable).toBeVisible();
|
||||
await podVariable.click();
|
||||
|
||||
const podSelectOptions = dashboardPage.getByGrafanaSelector(selectors.components.Select.option);
|
||||
await expect(podSelectOptions).toHaveCount(10);
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBA'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBB'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBC'))
|
||||
).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,69 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const DASHBOARD_UID = 'ZqZnVvFZz';
|
||||
|
||||
test.use({
|
||||
featureToggles: {
|
||||
newDashboardSharingComponent: false, // Use legacy sharing component for this test
|
||||
},
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Snapshots',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('Create a snapshot dashboard', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: DASHBOARD_UID,
|
||||
});
|
||||
|
||||
const panelsToCheck = [
|
||||
'Raw Data Graph',
|
||||
'Last non-null',
|
||||
'min',
|
||||
'Max',
|
||||
'The data from graph above with seriesToColumns transform',
|
||||
];
|
||||
|
||||
// Open the sharing modal
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.shareDashboard).click();
|
||||
|
||||
// Select the snapshot tab
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Snapshot')).click();
|
||||
|
||||
// Publish snapshot
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.ShareDashboardModal.SnapshotScene.PublishSnapshot)
|
||||
.click();
|
||||
|
||||
// Copy link button should be visible
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.SnapshotScene.CopyUrlButton)
|
||||
).toBeVisible();
|
||||
|
||||
// Get the snapshot URL from the input field
|
||||
const urlInput = dashboardPage.getByGrafanaSelector(
|
||||
selectors.pages.ShareDashboardModal.SnapshotScene.CopyUrlInput
|
||||
);
|
||||
const snapshotUrl = await urlInput.inputValue();
|
||||
|
||||
// Extract the snapshot key from the URL and navigate to the snapshot
|
||||
const snapshotKey = getSnapshotKey(snapshotUrl);
|
||||
await page.goto(`/dashboard/snapshot/${snapshotKey}`);
|
||||
|
||||
// Validate the dashboard controls are rendered
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.Controls)).toBeVisible();
|
||||
|
||||
// Validate the panels are rendered
|
||||
for (const title of panelsToCheck) {
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title))).toBeVisible();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const getSnapshotKey = (url: string): string => {
|
||||
return url.split('/').pop() || '';
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const DASHBOARD_UID = 'yBCC3aKGk';
|
||||
|
||||
test.describe(
|
||||
'Templating dashboard links and variables',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('Tests dashboard links and variables in links', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: DASHBOARD_UID });
|
||||
|
||||
const verifyLinks = async (variableValue: string) => {
|
||||
const dashboardLinks = dashboardPage.getByGrafanaSelector(selectors.components.DashboardLinks.link);
|
||||
|
||||
let linkCount = 0;
|
||||
await expect
|
||||
.poll(async () => {
|
||||
linkCount = await dashboardLinks.count();
|
||||
return linkCount;
|
||||
})
|
||||
.toBeGreaterThan(13);
|
||||
|
||||
for (let i = 0; i < linkCount; i++) {
|
||||
const href = await dashboardLinks.nth(i).getAttribute('href');
|
||||
expect(href).toContain(variableValue);
|
||||
}
|
||||
};
|
||||
|
||||
// Click on dashboard links dropdown
|
||||
const dashboardLinksDropdown = dashboardPage.getByGrafanaSelector(selectors.components.DashboardLinks.dropDown);
|
||||
await expect(dashboardLinksDropdown).toBeVisible();
|
||||
await dashboardLinksDropdown.click();
|
||||
|
||||
// Verify links contain default variable value
|
||||
await verifyLinks('var-custom=$__all');
|
||||
|
||||
// Close dropdown
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
// Change variable value from $__all to p2
|
||||
const customVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('$__all'))
|
||||
.locator('input');
|
||||
await expect(customVariable).toBeVisible();
|
||||
await customVariable.click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('p2'))
|
||||
.click();
|
||||
|
||||
// Close dropdown
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
// Open dashboard links dropdown again
|
||||
await expect(dashboardLinksDropdown).toBeVisible();
|
||||
await dashboardLinksDropdown.click();
|
||||
|
||||
// Verify all links now contain the p2 value
|
||||
await verifyLinks('p2');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,56 @@
|
|||
import { Page } from 'playwright-core';
|
||||
|
||||
import { test, expect, E2ESelectorGroups, DashboardPage } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'AejrN1AMz';
|
||||
|
||||
test.describe(
|
||||
'TextBox - load options scenarios',
|
||||
{
|
||||
tag: ['@dashboards'],
|
||||
},
|
||||
() => {
|
||||
test('default options should be correct', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
await validateTextboxAndMarkup(page, dashboardPage, selectors, 'default value');
|
||||
});
|
||||
|
||||
test('loading variable from url should be correct', async ({ page, gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: PAGE_UNDER_TEST,
|
||||
queryParams: new URLSearchParams({
|
||||
'var-text': 'not default value',
|
||||
}),
|
||||
});
|
||||
|
||||
await validateTextboxAndMarkup(page, dashboardPage, selectors, 'not default value');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// Helper function to validate textbox and markup
|
||||
async function validateTextboxAndMarkup(
|
||||
page: Page,
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
value: string
|
||||
) {
|
||||
const submenuItem = dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItem);
|
||||
await expect(submenuItem).toBeVisible();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('text'))
|
||||
).toBeVisible();
|
||||
|
||||
const textInput = submenuItem.locator('input');
|
||||
await expect(textInput).toBeVisible();
|
||||
await expect(textInput).toHaveValue(value);
|
||||
|
||||
const textPanel = page.locator(selectors.components.Panels.Visualization.Text.container(''));
|
||||
await expect(textPanel).toBeVisible();
|
||||
|
||||
const headerElement = textPanel.locator('h1');
|
||||
await expect(headerElement).toBeVisible();
|
||||
await expect(headerElement).toHaveText(`variable: ${value}`);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
export function makeNewDashboardRequestBody(dashboardName: string, folderUid?: string) {
|
||||
return {
|
||||
dashboard: {
|
||||
annotations: {
|
||||
list: [
|
||||
{
|
||||
builtIn: 1,
|
||||
datasource: { type: 'grafana', uid: '-- Grafana --' },
|
||||
enable: true,
|
||||
hide: true,
|
||||
iconColor: 'rgba(0, 211, 255, 1)',
|
||||
name: 'Annotations & Alerts',
|
||||
type: 'dashboard',
|
||||
},
|
||||
],
|
||||
},
|
||||
editable: true,
|
||||
fiscalYearStartMonth: 0,
|
||||
graphTooltip: 0,
|
||||
links: [],
|
||||
liveNow: false,
|
||||
panels: [
|
||||
{
|
||||
datasource: { type: 'testdata', uid: '89_jzlT4k' },
|
||||
gridPos: { h: 9, w: 12, x: 0, y: 0 },
|
||||
id: 2,
|
||||
options: {
|
||||
code: {
|
||||
language: 'plaintext',
|
||||
showLineNumbers: false,
|
||||
showMiniMap: false,
|
||||
},
|
||||
content: '***A nice little happy empty dashboard***',
|
||||
mode: 'markdown',
|
||||
},
|
||||
pluginVersion: '9.4.0-pre',
|
||||
title: 'Nothing to see here',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
refresh: '',
|
||||
revision: 1,
|
||||
schemaVersion: 38,
|
||||
tags: [],
|
||||
templating: { list: [] },
|
||||
time: { from: 'now-6h', to: 'now' },
|
||||
timepicker: {},
|
||||
timezone: '',
|
||||
title: dashboardName,
|
||||
version: 0,
|
||||
uid: '',
|
||||
weekStart: '',
|
||||
},
|
||||
message: '',
|
||||
overwrite: false,
|
||||
folderUid,
|
||||
} as const;
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "grafana"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"cellOptions": {
|
||||
"type": "auto"
|
||||
},
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"cellHeight": "sm",
|
||||
"footer": {
|
||||
"countRows": false,
|
||||
"fields": "",
|
||||
"reducer": ["sum"],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "10.3.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"channel": "plugin/testdata/random-20Hz-stream",
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "grafana"
|
||||
},
|
||||
"queryType": "measurements",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Live",
|
||||
"type": "table"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 39,
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "New dashboard",
|
||||
"version": 0,
|
||||
"uid": "live-e2e-test",
|
||||
"weekStart": ""
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"cellOptions": {
|
||||
"type": "auto"
|
||||
},
|
||||
"inspect": false
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"title": "",
|
||||
"url": "/d/${__dashboard.uid}?var-instance=${__data.fields.test1}&${__url_time_range}"
|
||||
}
|
||||
],
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"cellHeight": "sm",
|
||||
"footer": {
|
||||
"countRows": false,
|
||||
"fields": "",
|
||||
"reducer": ["sum"],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "11.6.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "test1",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "9wvfgzurfzb, 9yy21uzzxypg, dr199bpvpcru, dre33fzyxcrz, gc6j7crvrcpf, u6g9zuxvxypv"
|
||||
}
|
||||
],
|
||||
"title": "Data links without slug",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "gdev-prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"barWidthFactor": 0.6,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 3,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"hideZeros": false,
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "11.6.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "gdev-prometheus"
|
||||
},
|
||||
"disableTextWrap": false,
|
||||
"editorMode": "builder",
|
||||
"expr": "counters_logins{geohash=\"$instance\"}",
|
||||
"fullMetaSearch": false,
|
||||
"includeNullMetadata": true,
|
||||
"instant": false,
|
||||
"legendFormat": "__auto",
|
||||
"range": true,
|
||||
"refId": "A",
|
||||
"useBackend": false
|
||||
}
|
||||
],
|
||||
"title": "Panel Title",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"preload": false,
|
||||
"refresh": "",
|
||||
"schemaVersion": 41,
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"text": "9wvfgzurfzb",
|
||||
"value": "9wvfgzurfzb"
|
||||
},
|
||||
"name": "instance",
|
||||
"options": [
|
||||
{
|
||||
"selected": true,
|
||||
"text": "9wvfgzurfzb",
|
||||
"value": "9wvfgzurfzb"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "9yy21uzzxypg",
|
||||
"value": "9yy21uzzxypg"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "dr199bpvpcru",
|
||||
"value": "dr199bpvpcru"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "dre33fzyxcrz",
|
||||
"value": "dre33fzyxcrz"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "gc6j7crvrcpf",
|
||||
"value": "gc6j7crvrcpf"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "u6g9zuxvxypv",
|
||||
"value": "u6g9zuxvxypv"
|
||||
}
|
||||
],
|
||||
"query": "9wvfgzurfzb, 9yy21uzzxypg, dr199bpvpcru, dre33fzyxcrz, gc6j7crvrcpf, u6g9zuxvxypv",
|
||||
"type": "custom"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "utc",
|
||||
"title": "Data Link without slug test",
|
||||
"uid": "data-link-no-slug",
|
||||
"version": 3
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"title": "Sandbox Panel test",
|
||||
"type": "sandbox-test-panel"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Sandbox Panel Test",
|
||||
"uid": "c46b2460-16b7-42a5-82d1-b07fbf431950",
|
||||
"version": 1,
|
||||
"weekStart": ""
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": null,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": ["lastNotNull"],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showThresholdLabels": false,
|
||||
"showThresholdMarkers": true,
|
||||
"text": {}
|
||||
},
|
||||
"pluginVersion": "8.3.0-pre",
|
||||
"title": "Gauge Example",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"datasource": null,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"colorMode": "value",
|
||||
"graphMode": "area",
|
||||
"justifyMode": "auto",
|
||||
"orientation": "auto",
|
||||
"reduceOptions": {
|
||||
"calcs": ["lastNotNull"],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"text": {},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "8.3.0-pre",
|
||||
"title": "Stat",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": null,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 16
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single"
|
||||
}
|
||||
},
|
||||
"title": "Time series example",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": false,
|
||||
"schemaVersion": 31,
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "2021-09-01T04:00:00.000Z",
|
||||
"to": "2021-09-15T04:00:00.000Z"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "E2E Test - Import Dashboard",
|
||||
"uid": "kquZN5H7k",
|
||||
"version": 4
|
||||
}
|
|
@ -0,0 +1,424 @@
|
|||
{
|
||||
"apiVersion": "dashboard.grafana.app/v2alpha1",
|
||||
"kind": "Dashboard",
|
||||
"metadata": {
|
||||
"name": "admjzp8",
|
||||
"namespace": "default",
|
||||
"uid": "hrbekBWXeM7nC7GtouIbcngFyuqt8Xx7KlE4AnTwV7AX",
|
||||
"resourceVersion": "1",
|
||||
"generation": 1,
|
||||
"creationTimestamp": "2025-05-16T09:41:56Z",
|
||||
"labels": {
|
||||
"grafana.app/deprecatedInternalID": "182"
|
||||
},
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "user:cejvsh18uudxcf",
|
||||
"grafana.app/updatedBy": "user:cejvsh18uudxcf",
|
||||
"grafana.app/updatedTimestamp": "2025-05-16T09:41:56Z",
|
||||
"grafana.app/folder": ""
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"annotations": [
|
||||
{
|
||||
"kind": "AnnotationQuery",
|
||||
"spec": {
|
||||
"builtIn": true,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts"
|
||||
}
|
||||
}
|
||||
],
|
||||
"cursorSync": "Off",
|
||||
"description": "",
|
||||
"editable": true,
|
||||
"elements": {
|
||||
"panel-1": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"data": {
|
||||
"kind": "QueryGroup",
|
||||
"spec": {
|
||||
"queries": [
|
||||
{
|
||||
"kind": "PanelQuery",
|
||||
"spec": {
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"hidden": false,
|
||||
"query": {
|
||||
"kind": "grafana-testdata-datasource",
|
||||
"spec": {}
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
}
|
||||
],
|
||||
"queryOptions": {},
|
||||
"transformations": []
|
||||
}
|
||||
},
|
||||
"description": "",
|
||||
"id": 1,
|
||||
"links": [],
|
||||
"title": "New panel",
|
||||
"vizConfig": {
|
||||
"kind": "timeseries",
|
||||
"spec": {
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"barWidthFactor": 0.6,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"hideZeros": false,
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "12.1.0-pre"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"panel-2": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"data": {
|
||||
"kind": "QueryGroup",
|
||||
"spec": {
|
||||
"queries": [
|
||||
{
|
||||
"kind": "PanelQuery",
|
||||
"spec": {
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"hidden": false,
|
||||
"query": {
|
||||
"kind": "grafana-testdata-datasource",
|
||||
"spec": {}
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
}
|
||||
],
|
||||
"queryOptions": {},
|
||||
"transformations": []
|
||||
}
|
||||
},
|
||||
"description": "",
|
||||
"id": 2,
|
||||
"links": [],
|
||||
"title": "New panel",
|
||||
"vizConfig": {
|
||||
"kind": "timeseries",
|
||||
"spec": {
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"barWidthFactor": 0.6,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"hideZeros": false,
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "12.1.0-pre"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"panel-3": {
|
||||
"kind": "Panel",
|
||||
"spec": {
|
||||
"data": {
|
||||
"kind": "QueryGroup",
|
||||
"spec": {
|
||||
"queries": [
|
||||
{
|
||||
"kind": "PanelQuery",
|
||||
"spec": {
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"hidden": false,
|
||||
"query": {
|
||||
"kind": "grafana-testdata-datasource",
|
||||
"spec": {}
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
}
|
||||
],
|
||||
"queryOptions": {},
|
||||
"transformations": []
|
||||
}
|
||||
},
|
||||
"description": "",
|
||||
"id": 3,
|
||||
"links": [],
|
||||
"title": "New panel",
|
||||
"vizConfig": {
|
||||
"kind": "timeseries",
|
||||
"spec": {
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"barWidthFactor": 0.6,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"hideZeros": false,
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "12.1.0-pre"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"kind": "GridLayout",
|
||||
"spec": {
|
||||
"items": [
|
||||
{
|
||||
"kind": "GridLayoutItem",
|
||||
"spec": {
|
||||
"element": {
|
||||
"kind": "ElementReference",
|
||||
"name": "panel-1"
|
||||
},
|
||||
"height": 8,
|
||||
"width": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "GridLayoutItem",
|
||||
"spec": {
|
||||
"element": {
|
||||
"kind": "ElementReference",
|
||||
"name": "panel-2"
|
||||
},
|
||||
"height": 8,
|
||||
"width": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "GridLayoutItem",
|
||||
"spec": {
|
||||
"element": {
|
||||
"kind": "ElementReference",
|
||||
"name": "panel-3"
|
||||
},
|
||||
"height": 8,
|
||||
"width": 12,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"preload": false,
|
||||
"tags": [],
|
||||
"timeSettings": {
|
||||
"autoRefresh": "",
|
||||
"autoRefreshIntervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
|
||||
"fiscalYearStartMonth": 0,
|
||||
"from": "now-6h",
|
||||
"hideTimepicker": false,
|
||||
"timezone": "browser",
|
||||
"to": "now"
|
||||
},
|
||||
"title": "New Test V2 Dashboard",
|
||||
"variables": []
|
||||
},
|
||||
"status": {}
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
{
|
||||
"results": {
|
||||
"A": {
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))",
|
||||
"refId": "A",
|
||||
"meta": { "custom": { "resultType": "matrix" } },
|
||||
"fields": [
|
||||
{ "name": "Time", "type": "time", "typeInfo": { "frame": "time.Time" } },
|
||||
{
|
||||
"name": "Value",
|
||||
"type": "number",
|
||||
"typeInfo": { "frame": "float64" },
|
||||
"labels": {},
|
||||
"config": {
|
||||
"displayNameFromDS": "histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
1633619595000, 1633619610000, 1633619625000, 1633619640000, 1633619655000, 1633619670000, 1633619685000,
|
||||
1633619700000, 1633619715000, 1633619730000, 1633619745000, 1633619760000, 1633619775000, 1633619790000,
|
||||
1633619805000, 1633619820000, 1633619835000, 1633619850000, 1633619865000, 1633619880000, 1633619895000
|
||||
],
|
||||
[
|
||||
0.07245212135073513, 0.07253198890830721, 0.07247862573797707, 0.07238248338231042, 0.07221687487740913,
|
||||
0.07223291298743946, 0.07225427016727755, 0.024531677091864545, 0.02317081920915543,
|
||||
0.07548902139580993, 0.0777721702857508, 0.07768649905047344, 0.07782257603228229, 0.07788810213200052,
|
||||
0.07791835055437593, 0.07798387201529966, 0.07790826751849372, 0.07794858648610933, 0.07778729925797964,
|
||||
0.07769657495236215, 0.077550401329267
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"schema": {
|
||||
"name": "histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))",
|
||||
"refId": "A",
|
||||
"meta": { "custom": { "resultType": "vector" } },
|
||||
"fields": [
|
||||
{ "name": "Time", "type": "time", "typeInfo": { "frame": "time.Time" } },
|
||||
{
|
||||
"name": "Value",
|
||||
"type": "number",
|
||||
"typeInfo": { "frame": "float64" },
|
||||
"labels": {},
|
||||
"config": {
|
||||
"displayNameFromDS": "histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[5m])) by (le))"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": { "values": [[1633619900000], [0.0775504013292671]] }
|
||||
},
|
||||
{
|
||||
"schema": {
|
||||
"name": "exemplar",
|
||||
"refId": "A",
|
||||
"meta": { "custom": { "resultType": "exemplar" } },
|
||||
"fields": [
|
||||
{ "name": "Time", "type": "time", "typeInfo": { "frame": "time.Time" } },
|
||||
{ "name": "Value", "type": "number", "typeInfo": { "frame": "float64" } },
|
||||
{ "name": "instance", "type": "string", "typeInfo": { "frame": "string" } },
|
||||
{ "name": "__name__", "type": "string", "typeInfo": { "frame": "string" } },
|
||||
{ "name": "job", "type": "string", "typeInfo": { "frame": "string" } },
|
||||
{ "name": "status_code", "type": "string", "typeInfo": { "frame": "string" } },
|
||||
{ "name": "method", "type": "string", "typeInfo": { "frame": "string" } },
|
||||
{ "name": "traceID", "type": "string", "typeInfo": { "frame": "string" } },
|
||||
{ "name": "route", "type": "string", "typeInfo": { "frame": "string" } },
|
||||
{ "name": "ws", "type": "string", "typeInfo": { "frame": "string" } },
|
||||
{ "name": "le", "type": "string", "typeInfo": { "frame": "string" } }
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
1633619598000, 1633619622000, 1633619625000, 1633619646000, 1633619658000, 1633619682000, 1633619695000,
|
||||
1633619712000, 1633619712000, 1633619724000, 1633619717000, 1633619742000, 1633619757000, 1633619771000,
|
||||
1633619784000, 1633619801000, 1633619806000, 1633619833000, 1633619833000, 1633619845000, 1633619862000,
|
||||
1633619877000, 1633619889000
|
||||
],
|
||||
[
|
||||
0.0146153, 0.0118506, 0.0473847, 0.026997, 0.0164318, 0.0113532, 0.0105197, 0.162789, 0.0556026,
|
||||
0.148856, 0.0433809, 0.0117758, 0.0114496, 0.0114099, 0.0421927, 0.0134148, 0.0152827, 0.6975967,
|
||||
0.0394788, 0.0137441, 0.0110939, 0.0104496, 0.0101284
|
||||
],
|
||||
[
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"db:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80",
|
||||
"app:80"
|
||||
],
|
||||
[
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket",
|
||||
"tns_request_duration_seconds_bucket"
|
||||
],
|
||||
[
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/db",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app",
|
||||
"tns/app"
|
||||
],
|
||||
[
|
||||
"302",
|
||||
"200",
|
||||
"200",
|
||||
"200",
|
||||
"200",
|
||||
"200",
|
||||
"200",
|
||||
"500",
|
||||
"200",
|
||||
"302",
|
||||
"208",
|
||||
"200",
|
||||
"200",
|
||||
"200",
|
||||
"200",
|
||||
"200",
|
||||
"302",
|
||||
"200",
|
||||
"200",
|
||||
"200",
|
||||
"200",
|
||||
"200",
|
||||
"200"
|
||||
],
|
||||
[
|
||||
"POST",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"POST",
|
||||
"POST",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"POST",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET",
|
||||
"GET"
|
||||
],
|
||||
[
|
||||
"6a3cf561ef6c32a0",
|
||||
"396bcdf29601a149",
|
||||
"57c04ef608f11158",
|
||||
"77c757dab83c665f",
|
||||
"3d1069567e873f5e",
|
||||
"b337949f6213efd",
|
||||
"21b20cbe533cf099",
|
||||
"2c10b3aa30fabd66",
|
||||
"42ac6088a757636b",
|
||||
"2f81158008cd4dcc",
|
||||
"320b803ad7323b37",
|
||||
"7f15fd82aeb8b361",
|
||||
"11c79266da8a74cd",
|
||||
"5a8571bdcc04c990",
|
||||
"3de3f4f42ccb93ae",
|
||||
"23343ac91cc0638",
|
||||
"5cea3aad17ab11c8",
|
||||
"5d334e2843d3405a",
|
||||
"3cf6834596d4b6b6",
|
||||
"1ab6cff012959723",
|
||||
"2f78bc2c398b8b20",
|
||||
"6d5862a70c3abd42",
|
||||
"f5421be4054f501"
|
||||
],
|
||||
[
|
||||
"post",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"post",
|
||||
"post",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"post",
|
||||
"metrics",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"root",
|
||||
"root"
|
||||
],
|
||||
[
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false",
|
||||
"false"
|
||||
],
|
||||
[
|
||||
"0.025",
|
||||
"0.025",
|
||||
"0.05",
|
||||
"0.05",
|
||||
"0.025",
|
||||
"0.025",
|
||||
"0.025",
|
||||
"0.25",
|
||||
"0.1",
|
||||
"0.25",
|
||||
"0.05",
|
||||
"0.025",
|
||||
"0.025",
|
||||
"0.025",
|
||||
"0.05",
|
||||
"0.025",
|
||||
"0.025",
|
||||
"1.0",
|
||||
"0.05",
|
||||
"0.025",
|
||||
"0.025",
|
||||
"0.025",
|
||||
"0.025"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,56 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PAGE_UNDER_TEST = 'a6801696-cc53-4196-b1f9-2403e3909185/panel-tests-dashlist-variables';
|
||||
|
||||
test.describe(
|
||||
'Panels test: DashList panel',
|
||||
{
|
||||
tag: '@panels',
|
||||
},
|
||||
() => {
|
||||
// this is to prevent the fix for https://github.com/grafana/grafana/issues/76800 from regressing
|
||||
test('should pass current variable values correctly when `Include current template variable values` is set', async ({
|
||||
gotoDashboardPage,
|
||||
selectors,
|
||||
page,
|
||||
}) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST });
|
||||
|
||||
// check the initial value of the urls contain the variable value correctly
|
||||
const panel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Panels.Panel.title('Include time range and variables enabled')
|
||||
);
|
||||
await expect(panel).toBeVisible();
|
||||
|
||||
const links = panel.locator('a');
|
||||
const linkCount = await links.count();
|
||||
for (let i = 0; i < linkCount; i++) {
|
||||
const href = await links.nth(i).getAttribute('href');
|
||||
expect(href).toContain('var-server=A');
|
||||
}
|
||||
|
||||
// update variable to b
|
||||
const serverVariable = dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('server'))
|
||||
.locator('..')
|
||||
.locator('input');
|
||||
await serverVariable.click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B'))
|
||||
.click();
|
||||
|
||||
// blur the dropdown
|
||||
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
|
||||
// check the urls are updated with the new variable value
|
||||
await expect(panel).toBeVisible();
|
||||
const updatedLinks = panel.locator('a');
|
||||
const updatedLinkCount = await updatedLinks.count();
|
||||
for (let i = 0; i < updatedLinkCount; i++) {
|
||||
const href = await updatedLinks.nth(i).getAttribute('href');
|
||||
expect(href).toContain('var-server=B');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,154 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import panelSandboxDashboard from '../dashboards/PanelSandboxDashboard.json';
|
||||
|
||||
const DASHBOARD_ID = 'c46b2460-16b7-42a5-82d1-b07fbf431950';
|
||||
|
||||
test.beforeAll(async ({ request }) => {
|
||||
// Import the dashboard
|
||||
await request.post('/api/dashboards/import', {
|
||||
data: {
|
||||
dashboard: panelSandboxDashboard,
|
||||
folderUid: '',
|
||||
overwrite: true,
|
||||
inputs: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'Panels test: Panel sandbox',
|
||||
{
|
||||
tag: ['@panels'],
|
||||
},
|
||||
() => {
|
||||
test.describe('Sandbox disabled', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem('grafana.featureToggles', 'pluginsFrontendSandbox=0');
|
||||
});
|
||||
await page.reload();
|
||||
});
|
||||
|
||||
test('Add iframes to body', async ({ page, gotoDashboardPage }) => {
|
||||
await gotoDashboardPage({
|
||||
uid: DASHBOARD_ID,
|
||||
});
|
||||
|
||||
// this button adds iframes to the body
|
||||
await page.locator('[data-testid="button-create-iframes"]').click();
|
||||
|
||||
const iframeIds = [
|
||||
'createElementIframe',
|
||||
'innerHTMLIframe',
|
||||
'appendIframe',
|
||||
'prependIframe',
|
||||
'afterIframe',
|
||||
'beforeIframe',
|
||||
'outerHTMLIframe',
|
||||
'parseFromStringIframe',
|
||||
'insertBeforeIframe',
|
||||
'replaceChildIframe',
|
||||
];
|
||||
|
||||
for (const id of iframeIds) {
|
||||
await expect(page.locator(`#${id}`)).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('Reaches out of panel div', async ({ page, gotoDashboardPage }) => {
|
||||
await gotoDashboardPage({
|
||||
uid: DASHBOARD_ID,
|
||||
});
|
||||
|
||||
// this button reaches out of the panel div and modifies the element dataset
|
||||
await page.locator('[data-testid="button-reach-out"]').click();
|
||||
await expect(page.locator('[data-sandbox-test="true"]')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Reaches out of the panel editor', async ({ gotoDashboardPage, page }) => {
|
||||
await gotoDashboardPage({
|
||||
uid: DASHBOARD_ID,
|
||||
queryParams: new URLSearchParams({ editPanel: '1' }),
|
||||
});
|
||||
|
||||
const input = page.locator('[data-testid="panel-editor-custom-editor-input"]');
|
||||
await expect(input).toBeEnabled();
|
||||
await expect(input).toHaveValue('');
|
||||
|
||||
await input.fill('x');
|
||||
await expect(input).toHaveValue('x');
|
||||
await expect(page.locator('[data-sandbox-test="panel-editor"]')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Sandbox enabled', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem('grafana.featureToggles', 'pluginsFrontendSandbox=1');
|
||||
});
|
||||
await page.reload();
|
||||
});
|
||||
|
||||
test('Does not add iframes to body', async ({ page, gotoDashboardPage }) => {
|
||||
await gotoDashboardPage({
|
||||
uid: DASHBOARD_ID,
|
||||
});
|
||||
|
||||
// this button adds 3 iframes to the body
|
||||
await page.locator('[data-testid="button-create-iframes"]').click();
|
||||
|
||||
const iframeIds = [
|
||||
'createElementIframe',
|
||||
'innerHTMLIframe',
|
||||
'appendIframe',
|
||||
'prependIframe',
|
||||
'afterIframe',
|
||||
'beforeIframe',
|
||||
'outerHTMLIframe',
|
||||
'parseFromStringIframe',
|
||||
'insertBeforeIframe',
|
||||
'replaceChildIframe',
|
||||
];
|
||||
|
||||
for (const id of iframeIds) {
|
||||
await expect(page.locator(`#${id}`)).toBeHidden();
|
||||
}
|
||||
});
|
||||
|
||||
test('Does not reaches out of panel div', async ({ page, gotoDashboardPage }) => {
|
||||
await gotoDashboardPage({
|
||||
uid: DASHBOARD_ID,
|
||||
});
|
||||
|
||||
// this button reaches out of the panel div and modifies the element dataset
|
||||
await page.locator('[data-testid="button-reach-out"]').click();
|
||||
await expect(page.locator('[data-sandbox-test="true"]')).toBeHidden();
|
||||
});
|
||||
|
||||
test('Does not Reaches out of the panel editor', async ({ gotoDashboardPage, page }) => {
|
||||
await gotoDashboardPage({
|
||||
uid: DASHBOARD_ID,
|
||||
queryParams: new URLSearchParams({ editPanel: '1' }),
|
||||
});
|
||||
|
||||
const input = page.locator('[data-testid="panel-editor-custom-editor-input"]');
|
||||
await expect(input).toBeEnabled();
|
||||
|
||||
await input.fill('x');
|
||||
await expect(page.locator('[data-sandbox-test="panel-editor"]')).toBeHidden();
|
||||
});
|
||||
|
||||
test('Can access specific window global variables', async ({ page, gotoDashboardPage }) => {
|
||||
await gotoDashboardPage({
|
||||
uid: DASHBOARD_ID,
|
||||
});
|
||||
|
||||
await page.locator('[data-testid="button-test-globals"]').click();
|
||||
await expect(page.locator('[data-sandbox-global="Prism"]')).toBeVisible();
|
||||
await expect(page.locator('[data-sandbox-global="jQuery"]')).toBeVisible();
|
||||
await expect(page.locator('[data-sandbox-global="location"]')).toBeVisible();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,137 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const DASHBOARD_ID = 'P2jR04WVk';
|
||||
|
||||
const MAP_LAYERS_TYPE = 'Map layers Layer type';
|
||||
const MAP_LAYERS_DATA = 'Map layers Data';
|
||||
const MAP_LAYERS_GEOJSON = 'Map layers GeoJSON URL';
|
||||
|
||||
test.describe(
|
||||
'Panels test: Geomap layer types',
|
||||
{
|
||||
tag: ['@panels'],
|
||||
},
|
||||
() => {
|
||||
test('Tests changing the layer type', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: DASHBOARD_ID,
|
||||
queryParams: new URLSearchParams({ editPanel: '1' }),
|
||||
});
|
||||
|
||||
await expect(page.locator('[data-testid="layer-drag-drop-list"]')).toBeVisible();
|
||||
const field = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE)
|
||||
);
|
||||
await expect(field).toBeVisible();
|
||||
await expect(page.locator('[data-testid="layer-drag-drop-list"]')).toContainText('markers');
|
||||
|
||||
// Heatmap
|
||||
const input = field.locator('input');
|
||||
await input.fill('Heatmap');
|
||||
await input.press('Enter');
|
||||
await expect(page.locator('[data-testid="layer-drag-drop-list"]')).toContainText('heatmap');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA))
|
||||
).toBeVisible();
|
||||
|
||||
// GeoJSON
|
||||
await input.fill('GeoJSON');
|
||||
await input.press('Enter');
|
||||
await expect(page.locator('[data-testid="layer-drag-drop-list"]')).toContainText('geojson');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA))
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_GEOJSON))
|
||||
).toBeVisible();
|
||||
|
||||
// Open Street Map
|
||||
await input.fill('Open Street Map');
|
||||
await input.press('Enter');
|
||||
await expect(page.locator('[data-testid="layer-drag-drop-list"]')).toContainText('osm-standard');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA))
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_GEOJSON))
|
||||
).toBeHidden();
|
||||
|
||||
// CARTO basemap
|
||||
await input.fill('CARTO basemap');
|
||||
await input.press('Enter');
|
||||
await expect(page.locator('[data-testid="layer-drag-drop-list"]')).toContainText('carto');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel('Map layers Show labels')
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Map layers Theme'))
|
||||
).toBeVisible();
|
||||
|
||||
// ArcGIS MapServer
|
||||
await input.fill('ArcGIS MapServer');
|
||||
await input.press('Enter');
|
||||
await expect(page.locator('[data-testid="layer-drag-drop-list"]')).toContainText('esri-xyz');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel('Map layers Server instance')
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
// XYZ Tile layer
|
||||
await input.fill('XYZ Tile layer');
|
||||
await input.press('Enter');
|
||||
await expect(page.locator('[data-testid="layer-drag-drop-list"]')).toContainText('xyz');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel('Map layers URL template')
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel('Map layers Attribution')
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
// Night / Day (Alpha)
|
||||
await input.fill('Night / Day');
|
||||
await input.press('Enter');
|
||||
await expect(page.locator('[data-testid="layer-drag-drop-list"]')).toContainText('dayNight');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Map layers Show'))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel('Map layers Night region color')
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel('Map layers Display sun')
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.General.content)).toBeVisible();
|
||||
|
||||
// Route (Alpha)
|
||||
await input.fill('Route');
|
||||
await input.press('Enter');
|
||||
await expect(page.locator('[data-testid="layer-drag-drop-list"]')).toContainText('route');
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA))
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel('Map layers Location Mode')
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Map layers Style'))
|
||||
).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.General.content)).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,83 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const DASHBOARD_ID = 'P2jR04WVk';
|
||||
|
||||
test.describe(
|
||||
'Panels test: Geomap layer controls options',
|
||||
{
|
||||
tag: ['@panels'],
|
||||
},
|
||||
() => {
|
||||
test('Tests map controls options', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: DASHBOARD_ID,
|
||||
queryParams: new URLSearchParams({ editPanel: '1' }),
|
||||
});
|
||||
|
||||
// Wait until the query editor has been loaded by ensuring that the QueryEditor select contains the text 'flight_info_by_state.csv'
|
||||
await expect(
|
||||
page.locator(selectors.components.Select.singleValue('')).getByText('flight_info_by_state.csv')
|
||||
).toBeVisible();
|
||||
|
||||
const mapControlsGroup = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.OptionsGroup.group('Map controls')
|
||||
);
|
||||
await expect(mapControlsGroup).toBeVisible();
|
||||
|
||||
// Show zoom field
|
||||
const showZoomField = dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.showZoomField);
|
||||
await expect(showZoomField).toBeVisible();
|
||||
const zoomCheckbox = showZoomField.locator('input[type="checkbox"]');
|
||||
await zoomCheckbox.check({ force: true });
|
||||
await expect(zoomCheckbox).toBeChecked();
|
||||
|
||||
// Show attribution
|
||||
const showAttributionField = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.showAttributionField
|
||||
);
|
||||
await expect(showAttributionField).toBeVisible();
|
||||
const attributionCheckbox = showAttributionField.locator('input[type="checkbox"]');
|
||||
await attributionCheckbox.check({ force: true });
|
||||
await expect(attributionCheckbox).toBeChecked();
|
||||
|
||||
// Show scale
|
||||
const showScaleField = dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.showScaleField);
|
||||
await expect(showScaleField).toBeVisible();
|
||||
const scaleCheckbox = showScaleField.locator('input[type="checkbox"]');
|
||||
await scaleCheckbox.check({ force: true });
|
||||
await expect(scaleCheckbox).toBeChecked();
|
||||
|
||||
// Show measure tool
|
||||
const showMeasureField = dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.showMeasureField);
|
||||
await expect(showMeasureField).toBeVisible();
|
||||
const measureCheckbox = showMeasureField.locator('input[type="checkbox"]');
|
||||
await measureCheckbox.check({ force: true });
|
||||
await expect(measureCheckbox).toBeChecked();
|
||||
|
||||
// Show debug
|
||||
const showDebugField = dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.showDebugField);
|
||||
await expect(showDebugField).toBeVisible();
|
||||
const debugCheckbox = showDebugField.locator('input[type="checkbox"]');
|
||||
await debugCheckbox.check({ force: true });
|
||||
await expect(debugCheckbox).toBeChecked();
|
||||
|
||||
const panelContent = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.content);
|
||||
await expect(panelContent).toBeVisible();
|
||||
|
||||
// Verify zoom
|
||||
await expect(panelContent.locator('.ol-zoom')).toBeVisible();
|
||||
|
||||
// Verify attribution
|
||||
await expect(panelContent.locator('.ol-attribution')).toBeVisible();
|
||||
|
||||
// Verify scale
|
||||
await expect(panelContent.locator('.ol-scale-line')).toBeVisible();
|
||||
|
||||
// Verify measure tool
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.measureButton)).toBeVisible();
|
||||
|
||||
// Verify debug tool
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.DebugOverlay.wrapper)).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,194 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const DASHBOARD_ID = 'P2jR04WVk';
|
||||
|
||||
test.describe(
|
||||
'Panels test: Geomap spatial operations',
|
||||
{
|
||||
tag: ['@panels'],
|
||||
},
|
||||
() => {
|
||||
test('Tests location auto option', async ({ gotoDashboardPage, selectors }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: DASHBOARD_ID,
|
||||
queryParams: new URLSearchParams({ editPanel: '1' }),
|
||||
});
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Transformations')).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Transforms.addTransformationButton).click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.TransformTab.newTransform('Spatial operations'))
|
||||
.click();
|
||||
|
||||
const actionLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.actionLabel
|
||||
);
|
||||
const actionInput = actionLabel.locator('input');
|
||||
await actionInput.fill('Prepare spatial field');
|
||||
await actionInput.press('Enter');
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Transforms.SpatialOperations.locationLabel)
|
||||
).toBeVisible();
|
||||
|
||||
const locationLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.locationLabel
|
||||
);
|
||||
const locationInput = locationLabel.locator('input');
|
||||
await locationInput.fill('Auto');
|
||||
await locationInput.press('Enter');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
|
||||
const tableHeader = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header);
|
||||
await expect(tableHeader).toBeVisible();
|
||||
await expect(tableHeader.getByText('Point')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Tests location coords option', async ({ gotoDashboardPage, dashboardPage, selectors }) => {
|
||||
await gotoDashboardPage({ uid: DASHBOARD_ID, queryParams: new URLSearchParams({ editPanel: '1' }) });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Transformations')).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Transforms.addTransformationButton).click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.TransformTab.newTransform('Spatial operations'))
|
||||
.click();
|
||||
|
||||
const actionLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.actionLabel
|
||||
);
|
||||
const actionInput = actionLabel.locator('input');
|
||||
await actionInput.fill('Prepare spatial field');
|
||||
await actionInput.press('Enter');
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Transforms.SpatialOperations.locationLabel)
|
||||
).toBeVisible();
|
||||
|
||||
const locationLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.locationLabel
|
||||
);
|
||||
const locationInput = locationLabel.locator('input');
|
||||
await locationInput.fill('Coords');
|
||||
await locationInput.press('Enter');
|
||||
|
||||
const latitudeField = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.location.coords.latitudeFieldLabel
|
||||
);
|
||||
await expect(latitudeField).toBeVisible();
|
||||
const latitudeInput = latitudeField.locator('input');
|
||||
await latitudeInput.fill('Lat');
|
||||
await latitudeInput.press('Enter');
|
||||
|
||||
const longitudeField = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.location.coords.longitudeFieldLabel
|
||||
);
|
||||
await expect(longitudeField).toBeVisible();
|
||||
const longitudeInput = longitudeField.locator('input');
|
||||
await longitudeInput.fill('Lng');
|
||||
await longitudeInput.press('Enter');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
|
||||
const tableHeader = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header);
|
||||
await expect(tableHeader).toBeVisible();
|
||||
await expect(tableHeader.getByText('Point')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Tests geoshash field column appears in table view', async ({
|
||||
gotoDashboardPage,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
}) => {
|
||||
await gotoDashboardPage({ uid: DASHBOARD_ID, queryParams: new URLSearchParams({ editPanel: '1' }) });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Transformations')).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Transforms.addTransformationButton).click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.TransformTab.newTransform('Spatial operations'))
|
||||
.click();
|
||||
|
||||
const actionLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.actionLabel
|
||||
);
|
||||
const actionInput = actionLabel.locator('input');
|
||||
await actionInput.fill('Prepare spatial field');
|
||||
await actionInput.press('Enter');
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Transforms.SpatialOperations.locationLabel)
|
||||
).toBeVisible();
|
||||
|
||||
const locationLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.locationLabel
|
||||
);
|
||||
const locationInput = locationLabel.locator('input');
|
||||
await locationInput.fill('Geohash');
|
||||
await locationInput.press('Enter');
|
||||
|
||||
const geohashField = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.location.geohash.geohashFieldLabel
|
||||
);
|
||||
await expect(geohashField).toBeVisible();
|
||||
const geohashFieldInput = geohashField.locator('input');
|
||||
await geohashFieldInput.fill('State');
|
||||
await geohashFieldInput.press('Enter');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
|
||||
const tableHeader = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header);
|
||||
await expect(tableHeader).toBeVisible();
|
||||
await expect(tableHeader.getByText('State 1')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Tests location lookup option', async ({ gotoDashboardPage, dashboardPage, selectors }) => {
|
||||
await gotoDashboardPage({ uid: DASHBOARD_ID, queryParams: new URLSearchParams({ editPanel: '1' }) });
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Transformations')).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Transforms.addTransformationButton).click();
|
||||
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.TransformTab.newTransform('Spatial operations'))
|
||||
.click();
|
||||
|
||||
const actionLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.actionLabel
|
||||
);
|
||||
const actionInput = actionLabel.locator('input');
|
||||
await actionInput.fill('Prepare spatial field');
|
||||
await actionInput.press('Enter');
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Transforms.SpatialOperations.locationLabel)
|
||||
).toBeVisible();
|
||||
|
||||
const locationLabel = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.locationLabel
|
||||
);
|
||||
const locationInput = locationLabel.locator('input');
|
||||
await locationInput.fill('Lookup');
|
||||
await locationInput.press('Enter');
|
||||
|
||||
const lookupField = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.location.lookup.lookupFieldLabel
|
||||
);
|
||||
await expect(lookupField).toBeVisible();
|
||||
const lookupFieldInput = lookupField.locator('input');
|
||||
await lookupFieldInput.fill('State');
|
||||
await lookupFieldInput.press('Enter');
|
||||
|
||||
const gazetteerField = dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.Transforms.SpatialOperations.location.lookup.gazetteerFieldLabel
|
||||
);
|
||||
await expect(gazetteerField).toBeVisible();
|
||||
const gazetteerFieldInput = gazetteerField.locator('input');
|
||||
await gazetteerFieldInput.fill('USA States');
|
||||
await gazetteerFieldInput.press('Enter');
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
|
||||
const tableHeader = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header);
|
||||
await expect(tableHeader).toBeVisible();
|
||||
await expect(tableHeader.getByText('Geometry')).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,109 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const PANEL_UNDER_TEST = 'Lines 500 data points';
|
||||
|
||||
test.describe(
|
||||
'Panels test: Panel edit base',
|
||||
{
|
||||
tag: ['@panels'],
|
||||
},
|
||||
() => {
|
||||
test('Tests various Panel edit scenarios', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: 'TkZXxlNG3' });
|
||||
|
||||
const panelTitle = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(PANEL_UNDER_TEST));
|
||||
await expect(panelTitle).toBeVisible();
|
||||
|
||||
// Check that the panel is visible
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.loadingBar(''))).toHaveCount(0);
|
||||
await expect(panelTitle.locator('[data-testid="uplot-main-div"]').first()).toBeVisible();
|
||||
|
||||
// Open panel menu and click edit
|
||||
await panelTitle.hover();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.menu(PANEL_UNDER_TEST)).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.menuItems('Edit')).click();
|
||||
|
||||
// New panel editor opens when navigating from Panel menu
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.General.content)).toBeVisible();
|
||||
|
||||
// Queries tab is rendered and open by default
|
||||
const dataPane = dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.DataPane.content);
|
||||
await expect(dataPane).toBeVisible();
|
||||
|
||||
// Check that Queries tab is visible and active
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Queries'))).toBeVisible();
|
||||
await expect(page.locator(selectors.components.Tab.active(''))).toContainText('Queries1'); // there's already a query so therefore Query + 1
|
||||
|
||||
// Check query content is visible and other tabs are not
|
||||
await expect(page.locator(`[data-testid="${selectors.components.QueryTab.content}"]`)).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.AlertTab.content)).toBeHidden();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelAlertTabContent.content)).toBeHidden();
|
||||
|
||||
// Can change to Transform tab
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Transformations')).click();
|
||||
await expect(page.locator(selectors.components.Tab.active(''))).toContainText('Transformations0'); // no transforms so Transform + 0
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Transforms.addTransformationButton)
|
||||
).toBeVisible();
|
||||
await expect(page.locator(`[data-testid="${selectors.components.QueryTab.content}"]`)).toBeHidden();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.AlertTab.content)).toBeHidden();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelAlertTabContent.content)).toBeHidden();
|
||||
|
||||
// Can change to Alerts tab (graph panel is the default vis so the alerts tab should be rendered)
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Alert')).click();
|
||||
await expect(page.locator(selectors.components.Tab.active(''))).toContainText('Alert0'); // no alert so Alert + 0
|
||||
await expect(page.locator(`[data-testid="${selectors.components.QueryTab.content}"]`)).toBeHidden();
|
||||
|
||||
// Go back to Queries tab
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Queries')).click();
|
||||
|
||||
// Check that Time series is chosen
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleVizPicker)).toContainText(
|
||||
'Time series'
|
||||
);
|
||||
|
||||
// Check that table view works
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.loadingBar(''))).toHaveCount(0);
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
|
||||
const tableHeader = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header);
|
||||
await expect(tableHeader).toBeVisible();
|
||||
await expect(tableHeader.getByText('A-series')).toBeVisible();
|
||||
|
||||
// Change to Text panel
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleVizPicker).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PluginVisualization.item('Text')).click();
|
||||
// Check current visualization shows Text
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleVizPicker)).toContainText(
|
||||
'Text'
|
||||
);
|
||||
|
||||
// Data pane should not be rendered
|
||||
await expect(dataPane).toBeHidden();
|
||||
|
||||
// Change to Table panel
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleVizPicker).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PluginVisualization.item('Table')).click();
|
||||
// Check current visualization shows Table
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleVizPicker)).toContainText(
|
||||
'Table'
|
||||
);
|
||||
|
||||
// Data pane should be rendered
|
||||
await expect(dataPane).toBeVisible();
|
||||
|
||||
// Field & Overrides tabs (need to switch to React based vis, i.e. Table)
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel('Table Show table header')
|
||||
)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(
|
||||
selectors.components.PanelEditor.OptionsPane.fieldLabel('Table Column width')
|
||||
)
|
||||
).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,118 @@
|
|||
import { Locator, Page } from 'playwright-core';
|
||||
|
||||
import { test, expect, DashboardPage, E2ESelectorGroups } from '@grafana/plugin-e2e';
|
||||
|
||||
test.describe(
|
||||
'Panels test: Queries',
|
||||
{
|
||||
tag: ['@panels'],
|
||||
},
|
||||
() => {
|
||||
test('Tests various Panel edit queries scenarios', async ({ selectors, gotoDashboardPage, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: '5SdHCadmz',
|
||||
queryParams: new URLSearchParams({ editPanel: '3' }),
|
||||
});
|
||||
|
||||
// New panel editor opens when navigating from Panel menu
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.General.content)).toBeVisible();
|
||||
|
||||
// Queries tab is rendered and open by default
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.DataPane.content)).toBeVisible();
|
||||
|
||||
// We expect row with refId A to exist and be visible
|
||||
const initialRows = dashboardPage.getByGrafanaSelector(selectors.components.QueryEditorRows.rows);
|
||||
await expect(initialRows).toHaveCount(1);
|
||||
|
||||
// Add query button should be visible and clicking on it should create a new row
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.QueryTab.addQuery).click();
|
||||
|
||||
// We expect row with refId A and B to exist and be visible
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.QueryEditorRows.rows)).toHaveCount(2);
|
||||
|
||||
// Remove refId A
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.QueryEditorRow.actionButton('Remove query'))
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// We expect row with refId B to exist and be visible
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.QueryEditorRows.rows)).toHaveCount(1);
|
||||
|
||||
// Duplicate refId B
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.QueryEditorRow.actionButton('Duplicate query'))
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// We expect row with refId B and A to exist and be visible
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.QueryEditorRows.rows)).toHaveCount(2);
|
||||
|
||||
// Change to CSV Metric Values scenario for A
|
||||
const scenarioSelect = dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.DataSource.TestData.QueryTab.scenarioSelectContainer)
|
||||
.first();
|
||||
await scenarioSelect.locator('input[id*="test-data-scenario-select-"]').first().click();
|
||||
await page.getByText('CSV Metric Values').first().click();
|
||||
|
||||
// Verify both queries are present in inspector
|
||||
await expectInspectorResultAndClose(page, dashboardPage, selectors, async (keys) => {
|
||||
const keyTexts = await keys.allTextContents();
|
||||
const length = keyTexts.length;
|
||||
const resultIds = new Set<string>([
|
||||
keyTexts[length - 2], // last 2
|
||||
keyTexts[length - 1], // last 2
|
||||
]);
|
||||
|
||||
expect(resultIds.has('A:')).toBe(true);
|
||||
expect(resultIds.has('B:')).toBe(true);
|
||||
});
|
||||
|
||||
// Hide response for row with refId A
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.QueryEditorRow.actionButton('Hide response'))
|
||||
.nth(1)
|
||||
.click();
|
||||
|
||||
await expectInspectorResultAndClose(page, dashboardPage, selectors, async (keys) => {
|
||||
const keyTexts = await keys.allTextContents();
|
||||
const length = keyTexts.length;
|
||||
expect(keyTexts[length - 1]).toBe('B:');
|
||||
});
|
||||
|
||||
// Show response for row with refId A
|
||||
await dashboardPage
|
||||
.getByGrafanaSelector(selectors.components.QueryEditorRow.actionButton('Hide response'))
|
||||
.nth(1)
|
||||
.click();
|
||||
|
||||
await expectInspectorResultAndClose(page, dashboardPage, selectors, async (keys) => {
|
||||
const keyTexts = await keys.allTextContents();
|
||||
const length = keyTexts.length;
|
||||
const resultIds = new Set<string>([
|
||||
keyTexts[length - 2], // last 2
|
||||
keyTexts[length - 1], // last 2
|
||||
]);
|
||||
|
||||
expect(resultIds.has('A:')).toBe(true);
|
||||
expect(resultIds.has('B:')).toBe(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const expectInspectorResultAndClose = async (
|
||||
page: Page,
|
||||
dashboardPage: DashboardPage,
|
||||
selectors: E2ESelectorGroups,
|
||||
expectCallback: (keys: Locator) => Promise<void>
|
||||
) => {
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.QueryTab.queryInspectorButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.PanelInspector.Query.refreshButton).click();
|
||||
|
||||
const keys = page.locator(selectors.components.PanelInspector.Query.jsonObjectKeys(''));
|
||||
await expect(keys.first()).toBeVisible();
|
||||
await expectCallback(keys);
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Drawer.General.close).click();
|
||||
};
|
|
@ -0,0 +1,44 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test.describe(
|
||||
'Panels test: Transformations',
|
||||
{
|
||||
tag: ['@panels'],
|
||||
},
|
||||
() => {
|
||||
test('Tests transformations editor', async ({ selectors, gotoDashboardPage }) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: 'TkZXxlNG3',
|
||||
queryParams: new URLSearchParams({ editPanel: '47' }),
|
||||
});
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Transformations')).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Transforms.addTransformationButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TransformTab.newTransform('Reduce')).click();
|
||||
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Transforms.Reduce.calculationsLabel)
|
||||
).toBeVisible();
|
||||
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Transforms.Reduce.modeLabel)).toBeVisible();
|
||||
});
|
||||
|
||||
test('Tests case where transformations can be disabled and not clear out panel data', async ({
|
||||
selectors,
|
||||
gotoDashboardPage,
|
||||
}) => {
|
||||
const dashboardPage = await gotoDashboardPage({
|
||||
uid: 'TkZXxlNG3',
|
||||
queryParams: new URLSearchParams({ editPanel: '47' }),
|
||||
});
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Transformations')).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Transforms.addTransformationButton).click();
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.TransformTab.newTransform('Reduce')).click();
|
||||
|
||||
await dashboardPage.getByGrafanaSelector(selectors.components.Transforms.disableTransformationButton).click();
|
||||
await expect(
|
||||
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.PanelDataErrorMessage)
|
||||
).toBeHidden();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,14 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test(
|
||||
'Smoke test: decoupled frontend plugin loads',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
async ({ createDataSourceConfigPage, page }) => {
|
||||
await createDataSourceConfigPage({ type: 'grafana-azure-monitor-datasource' });
|
||||
|
||||
await expect(await page.getByText('Type: Azure Monitor', { exact: true })).toBeVisible();
|
||||
await expect(await page.getByRole('heading', { name: 'Authentication', exact: true })).toBeVisible();
|
||||
}
|
||||
);
|
|
@ -0,0 +1,14 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test(
|
||||
'Smoke test: decoupled frontend plugin loads',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
async ({ createDataSourceConfigPage, page }) => {
|
||||
await createDataSourceConfigPage({ type: 'stackdriver' });
|
||||
|
||||
await expect(await page.getByText('Type: Google Cloud Monitoring', { exact: true })).toBeVisible();
|
||||
await expect(await page.getByText('Google JWT File', { exact: true })).toBeVisible();
|
||||
}
|
||||
);
|
|
@ -0,0 +1,14 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test(
|
||||
'Smoke test: decoupled frontend plugin loads',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
async ({ createDataSourceConfigPage, page }) => {
|
||||
await createDataSourceConfigPage({ type: 'cloudwatch' });
|
||||
|
||||
await expect(await page.getByText('Type: CloudWatch', { exact: true })).toBeVisible();
|
||||
await expect(await page.getByRole('heading', { name: 'Connection Details', exact: true })).toBeVisible();
|
||||
}
|
||||
);
|
|
@ -0,0 +1,14 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test(
|
||||
'Smoke test: decoupled frontend plugin loads',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
async ({ createDataSourceConfigPage, page }) => {
|
||||
await createDataSourceConfigPage({ type: 'elasticsearch' });
|
||||
|
||||
await expect(await page.getByText('Type: Elasticsearch', { exact: true })).toBeVisible();
|
||||
await expect(await page.getByRole('heading', { name: 'Connection', exact: true })).toBeVisible();
|
||||
}
|
||||
);
|
|
@ -0,0 +1,14 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test(
|
||||
'Smoke test: decoupled frontend plugin loads',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
async ({ createDataSourceConfigPage, page }) => {
|
||||
await createDataSourceConfigPage({ type: 'graphite' });
|
||||
|
||||
await expect(await page.getByText('Type: Graphite', { exact: true })).toBeVisible();
|
||||
await expect(await page.getByRole('heading', { name: 'HTTP', exact: true })).toBeVisible();
|
||||
}
|
||||
);
|
|
@ -0,0 +1,14 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test(
|
||||
'Smoke test: decoupled frontend plugin loads',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
async ({ createDataSourceConfigPage, page }) => {
|
||||
await createDataSourceConfigPage({ type: 'influxdb' });
|
||||
|
||||
await expect(await page.getByText('Type: InfluxDB', { exact: true })).toBeVisible();
|
||||
await expect(await page.getByRole('heading', { name: 'HTTP', exact: true })).toBeVisible();
|
||||
}
|
||||
);
|
|
@ -0,0 +1,14 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test(
|
||||
'Smoke test: plugin loads',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
async ({ createDataSourceConfigPage, page }) => {
|
||||
await createDataSourceConfigPage({ type: 'jaeger' });
|
||||
|
||||
await expect(await page.getByText('Type: Jaeger', { exact: true })).toBeVisible();
|
||||
await expect(await page.getByRole('heading', { name: 'Connection', exact: true })).toBeVisible();
|
||||
}
|
||||
);
|
|
@ -3,6 +3,6 @@ import { test, expect } from '@grafana/plugin-e2e';
|
|||
test('Smoke test: decoupled frontend plugin loads', async ({ createDataSourceConfigPage, page }) => {
|
||||
await createDataSourceConfigPage({ type: 'loki' });
|
||||
|
||||
await expect(await page.getByText('Type: Loki', { exact: true })).toBeVisible();
|
||||
await expect(await page.getByRole('heading', { name: 'Connection', exact: true })).toBeVisible();
|
||||
await expect(page.getByText('Type: Loki', { exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('heading', { name: 'Connection', exact: true })).toBeVisible();
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test(
|
||||
'Smoke test: decoupled frontend plugin loads',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
async ({ createDataSourceConfigPage, page }) => {
|
||||
await createDataSourceConfigPage({ type: 'mssql' });
|
||||
|
||||
await expect(await page.getByText('Type: Microsoft SQL Server', { exact: true })).toBeVisible();
|
||||
await expect(await page.getByRole('heading', { name: 'Connection', exact: true })).toBeVisible();
|
||||
}
|
||||
);
|
|
@ -0,0 +1,31 @@
|
|||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
import { tableNameWithSpecialCharacter } from './mocks/mysql.mocks';
|
||||
import { mockDataSourceRequest } from './utils';
|
||||
|
||||
test.beforeEach(mockDataSourceRequest);
|
||||
|
||||
test(
|
||||
'code editor autocomplete should handle table name escaping/quoting',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
async ({ explorePage, selectors, page }) => {
|
||||
await page.getByLabel('Code').check();
|
||||
|
||||
const editor = explorePage.getByGrafanaSelector(selectors.components.CodeEditor.container).getByRole('textbox');
|
||||
await editor.fill('S');
|
||||
await page.getByLabel('SELECT <column> FROM <table>').locator('a').click();
|
||||
await expect(page.getByLabel(tableNameWithSpecialCharacter)).toBeVisible();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await expect(editor).toHaveValue(`SELECT FROM grafana.\`${tableNameWithSpecialCharacter}\``);
|
||||
|
||||
for (let i = 0; i < tableNameWithSpecialCharacter.length + 2; i++) {
|
||||
await page.keyboard.press('Backspace');
|
||||
}
|
||||
|
||||
await page.keyboard.press('Control+I');
|
||||
await expect(page.getByLabel(tableNameWithSpecialCharacter)).toBeVisible();
|
||||
}
|
||||
);
|
|
@ -0,0 +1,176 @@
|
|||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import { normalTableName } from './mocks/mysql.mocks';
|
||||
import { mockDataSourceRequest } from './utils';
|
||||
|
||||
test.beforeEach(mockDataSourceRequest);
|
||||
|
||||
test.describe(
|
||||
'mysql',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
() => {
|
||||
test('visual query builder should handle macros', async ({ explorePage, page }) => {
|
||||
await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.headerTableSelector).click();
|
||||
await page.getByText(normalTableName, { exact: true }).click();
|
||||
|
||||
// Open Data operations
|
||||
await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.selectAggregation).click();
|
||||
const select = page.getByLabel('Select options menu');
|
||||
await select.locator(page.getByText('$__timeGroupAlias')).click();
|
||||
|
||||
// Open column selector
|
||||
await explorePage
|
||||
.getByGrafanaSelector(selectors.components.SQLQueryEditor.selectFunctionParameter('Column'))
|
||||
.click();
|
||||
await select.locator(page.getByText('createdAt')).click();
|
||||
|
||||
// Open Interval selector
|
||||
await explorePage
|
||||
.getByGrafanaSelector(selectors.components.SQLQueryEditor.selectFunctionParameter('Interval'))
|
||||
.click();
|
||||
await select.locator(page.getByText('$__interval')).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Add column' }).click();
|
||||
|
||||
await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.selectAggregation).nth(1).click();
|
||||
await select.locator(page.getByText('AVG')).click();
|
||||
|
||||
await explorePage
|
||||
.getByGrafanaSelector(selectors.components.SQLQueryEditor.selectFunctionParameter('Column'))
|
||||
.nth(1)
|
||||
.click();
|
||||
await select.locator(page.getByText('bigint')).click();
|
||||
|
||||
// Validate the query
|
||||
await expect(
|
||||
explorePage.getByGrafanaSelector(selectors.components.CodeEditor.container).getByRole('textbox')
|
||||
).toHaveValue(
|
||||
`SELECT\n $__timeGroupAlias(createdAt, $__interval),\n AVG(\`bigint\`)\nFROM\n DataMaker.normalTable\nLIMIT\n 50`
|
||||
);
|
||||
});
|
||||
|
||||
test('visual query builder should handle time filter macro', async ({ explorePage, page }) => {
|
||||
await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.headerTableSelector).click();
|
||||
await page.getByText(normalTableName, { exact: true }).click();
|
||||
|
||||
// Open column selector
|
||||
await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.selectColumn).click();
|
||||
const select = page.getByLabel('Select options menu');
|
||||
await select.locator(page.getByText('createdAt')).click();
|
||||
|
||||
// Toggle where row
|
||||
await page.getByLabel('Filter').last().click();
|
||||
|
||||
// Click add filter button
|
||||
await page.getByRole('button', { name: 'Add filter' }).click();
|
||||
await page.getByRole('button', { name: 'Add filter' }).click(); // For some reason we need to click twice
|
||||
|
||||
// Open field selector
|
||||
await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.filterField).click();
|
||||
await select.locator(page.getByText('createdAt')).click();
|
||||
|
||||
// Open operator selector
|
||||
await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.filterOperator).click();
|
||||
await select.locator(page.getByText('Macros')).click();
|
||||
|
||||
// Open macros value selector
|
||||
await explorePage.getByGrafanaSelector('Macros value selector').click();
|
||||
await select.locator(page.getByText('timeFilter', { exact: true })).click();
|
||||
|
||||
// Validate that the timeFilter macro was added
|
||||
await expect(
|
||||
explorePage.getByGrafanaSelector(selectors.components.CodeEditor.container).getByRole('textbox')
|
||||
).toHaveValue(
|
||||
`SELECT\n createdAt\nFROM\n DataMaker.normalTable\nWHERE\n $__timeFilter(createdAt)\nLIMIT\n 50`
|
||||
);
|
||||
|
||||
// Validate that the timeFilter macro was removed when changed to equals operator
|
||||
await explorePage.getByGrafanaSelector(selectors.components.SQLQueryEditor.filterOperator).click();
|
||||
await select.locator(page.getByText('==')).click();
|
||||
|
||||
await explorePage.getByGrafanaSelector(selectors.components.DateTimePicker.input).click();
|
||||
await explorePage.getByGrafanaSelector(selectors.components.DateTimePicker.input).blur();
|
||||
|
||||
await expect(
|
||||
explorePage.getByGrafanaSelector(selectors.components.CodeEditor.container).getByRole('textbox')
|
||||
).not.toHaveValue(`SELECT\n createdAt\nFROM\n DataMaker.normalTable\nWHERE\n createdAt = NULL\nLIMIT\n 50`);
|
||||
});
|
||||
|
||||
test('visual query builder should not crash when filter is set to select_any_in', async ({ explorePage, page }) => {
|
||||
const queryParams = new URLSearchParams();
|
||||
queryParams.set('schemaVersion', '1');
|
||||
queryParams.set('orgId', '1');
|
||||
const panes = {
|
||||
mmm: {
|
||||
datasource: 'P4FDCC188E688367F',
|
||||
queries: [
|
||||
{
|
||||
refId: 'A',
|
||||
datasource: {
|
||||
type: 'mysql',
|
||||
uid: 'P4FDCC188E688367F',
|
||||
},
|
||||
format: 'table',
|
||||
rawSql: "SELECT * FROM DataMaker.normalTable WHERE name IN ('a') LIMIT 50 ",
|
||||
editorMode: 'builder',
|
||||
sql: {
|
||||
columns: [
|
||||
{
|
||||
type: 'function',
|
||||
parameters: [
|
||||
{
|
||||
type: 'functionParameter',
|
||||
name: '*',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
groupBy: [
|
||||
{
|
||||
type: 'groupBy',
|
||||
property: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
],
|
||||
limit: 50,
|
||||
whereJsonTree: {
|
||||
id: 'baa99aa9-0123-4456-b89a-b195d1dcfc6a',
|
||||
type: 'group',
|
||||
children1: [
|
||||
{
|
||||
type: 'rule',
|
||||
id: 'bb9a8bba-89ab-4cde-b012-3195d1dd2c91',
|
||||
properties: {
|
||||
fieldSrc: 'field',
|
||||
field: 'name',
|
||||
operator: 'select_any_in',
|
||||
value: ['a'],
|
||||
valueSrc: ['value'],
|
||||
valueType: ['text'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
whereString: "name IN ('a')",
|
||||
},
|
||||
dataset: 'DataMaker',
|
||||
table: 'normalTable',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
queryParams.set('panes', JSON.stringify(panes));
|
||||
|
||||
await explorePage.goto({ queryParams });
|
||||
|
||||
// Validate the query
|
||||
await expect(
|
||||
explorePage.getByGrafanaSelector(selectors.components.CodeEditor.container).getByRole('textbox')
|
||||
).toHaveValue(`SELECT\n *\nFROM\n DataMaker.normalTable\nWHERE\n name IN ('a')\nLIMIT\n 50`);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,14 @@
|
|||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
test(
|
||||
'Smoke test: decoupled frontend plugin loads',
|
||||
{
|
||||
tag: '@plugins',
|
||||
},
|
||||
async ({ createDataSourceConfigPage, page }) => {
|
||||
await createDataSourceConfigPage({ type: 'opentsdb' });
|
||||
|
||||
await expect(await page.getByText('Type: OpenTSDB', { exact: true })).toBeVisible();
|
||||
await expect(await page.getByRole('heading', { name: 'HTTP', exact: true })).toBeVisible();
|
||||
}
|
||||
);
|
|
@ -0,0 +1,31 @@
|
|||
import * as e2e from '@grafana/e2e-selectors';
|
||||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
// let's disable the feature toggles for now, otherwise the getAlertRuleQueryRow fails and I don't see any other way to get the query row
|
||||
test.use({ featureToggles: { alertingQueryAndExpressionsStepMode: false, alertingNotificationsStepMode: false } });
|
||||
|
||||
test.describe('plugin-e2e-api-tests admin', { tag: ['@plugins'] }, () => {
|
||||
test('should evaluate to false if entire request returns 200 but partial query result is invalid', async ({
|
||||
page,
|
||||
alertRuleEditPage,
|
||||
}) => {
|
||||
await alertRuleEditPage.alertRuleNameField.fill('Test Alert Rule');
|
||||
|
||||
//add working query
|
||||
const queryA = alertRuleEditPage.getAlertRuleQueryRow('A');
|
||||
await queryA.datasource.set('gdev-prometheus');
|
||||
await queryA.locator.getByLabel('Code').click();
|
||||
await page.waitForFunction(() => window.monaco);
|
||||
await queryA.getByGrafanaSelector(e2e.selectors.components.QueryField.container).click();
|
||||
await page.keyboard.insertText('topk(5, max(scrape_duration_seconds) by (job))');
|
||||
|
||||
//add broken query
|
||||
const newQuery = await alertRuleEditPage.clickAddQueryRow();
|
||||
await newQuery.datasource.set('gdev-prometheus');
|
||||
await newQuery.locator.getByLabel('Code').click();
|
||||
await newQuery.getByGrafanaSelector(e2e.selectors.components.QueryField.container).click();
|
||||
await page.keyboard.insertText('topk(5,');
|
||||
|
||||
await expect(alertRuleEditPage.evaluate()).not.toBeOK();
|
||||
});
|
||||
});
|
|
@ -41,13 +41,15 @@ const scenarios: Scenario[] = [
|
|||
},
|
||||
];
|
||||
|
||||
for (const scenario of scenarios) {
|
||||
test(`annotation query data with ${scenario.name}`, async ({ annotationEditPage, page }) => {
|
||||
annotationEditPage.mockQueryDataResponse(scenario.mock, scenario.status);
|
||||
await annotationEditPage.datasource.set('gdev-testdata');
|
||||
await page.getByLabel('Scenario').last().fill('CSV Content');
|
||||
await page.keyboard.press('Tab');
|
||||
await annotationEditPage.runQuery();
|
||||
await expect(annotationEditPage).toHaveAlert(scenario.severity, { hasText: scenario.text });
|
||||
});
|
||||
}
|
||||
test.describe('plugin-e2e-api-tests admin', { tag: ['@plugins'] }, () => {
|
||||
for (const scenario of scenarios) {
|
||||
test(`annotation query data with ${scenario.name}`, async ({ annotationEditPage, page }) => {
|
||||
annotationEditPage.mockQueryDataResponse(scenario.mock, scenario.status);
|
||||
await annotationEditPage.datasource.set('gdev-testdata');
|
||||
await page.getByLabel('Scenario').last().fill('CSV Content');
|
||||
await page.keyboard.press('Tab');
|
||||
await annotationEditPage.runQuery();
|
||||
await expect(annotationEditPage).toHaveAlert(scenario.severity, { hasText: scenario.text });
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
const REACT_TABLE_DASHBOARD = { uid: 'U_bZIMRMk' };
|
||||
|
||||
test.describe(
|
||||
'plugin-e2e-api-tests admin',
|
||||
{
|
||||
tag: ['@plugins'],
|
||||
},
|
||||
() => {
|
||||
test('add panel in already existing dashboard', async ({ gotoDashboardPage, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage(REACT_TABLE_DASHBOARD);
|
||||
await dashboardPage.addPanel();
|
||||
await expect(page.url()).toContain('editPanel');
|
||||
});
|
||||
|
||||
test('add panel in new dashboard', async ({ dashboardPage, page }) => {
|
||||
const panelEditPage = await dashboardPage.addPanel();
|
||||
await expect(panelEditPage.panel.locator).toBeVisible();
|
||||
await expect(page.url()).toContain('editPanel');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,48 @@
|
|||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
import { formatExpectError } from '../errors';
|
||||
|
||||
test.describe(
|
||||
'plugin-e2e-api-tests admin',
|
||||
{
|
||||
tag: ['@plugins'],
|
||||
},
|
||||
() => {
|
||||
test.describe('test createDataSourceConfigPage fixture, saveAndTest and toBeOK matcher', () => {
|
||||
test('invalid credentials should return an error', async ({ createDataSourceConfigPage, page }) => {
|
||||
const configPage = await createDataSourceConfigPage({ type: 'prometheus' });
|
||||
await page.getByPlaceholder('http://localhost:9090').fill('http://localhost:9090');
|
||||
await expect(
|
||||
configPage.saveAndTest(),
|
||||
formatExpectError('Expected save data source config to fail when Prometheus server is not running')
|
||||
).not.toBeOK();
|
||||
});
|
||||
|
||||
test('valid credentials should return a 200 status code', async ({ createDataSourceConfigPage, page }) => {
|
||||
const configPage = await createDataSourceConfigPage({ type: 'prometheus' });
|
||||
configPage.mockHealthCheckResponse({ status: 200 });
|
||||
await page.getByPlaceholder('http://localhost:9090').fill('http://localhost:9090');
|
||||
await expect(
|
||||
configPage.saveAndTest(),
|
||||
formatExpectError('Expected data source config to be successfully saved')
|
||||
).toBeOK();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('test data source with frontend only health check', () => {
|
||||
test('valid credentials should display a success alert on the page', async ({
|
||||
createDataSourceConfigPage,
|
||||
page,
|
||||
}) => {
|
||||
const configPage = await createDataSourceConfigPage({ type: 'zipkin' });
|
||||
configPage.mockHealthCheckResponse({ message: 'Data source is working', status: 'OK' }, 200);
|
||||
await page.getByPlaceholder('http://localhost:9411').fill('http://localhost:9411');
|
||||
await expect(configPage.saveAndTest()).toBeOK();
|
||||
await expect(
|
||||
configPage,
|
||||
formatExpectError('Expected data source config to display success alert after save')
|
||||
).toHaveAlert('success', { hasNotText: 'Datasource updated' });
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,19 @@
|
|||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
import { formatExpectError } from '../errors';
|
||||
|
||||
test.describe(
|
||||
'plugin-e2e-api-tests admin',
|
||||
{
|
||||
tag: ['@plugins'],
|
||||
},
|
||||
() => {
|
||||
test('query data response should be OK when query is valid', async ({ explorePage }) => {
|
||||
await explorePage.datasource.set('gdev-testdata');
|
||||
await expect(
|
||||
explorePage.runQuery(),
|
||||
formatExpectError('Expected Explore query to execute successfully')
|
||||
).toBeOK();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,25 @@
|
|||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
const TRUTHY_CUSTOM_TOGGLE = 'custom_toggle1';
|
||||
const FALSY_CUSTOM_TOGGLE = 'custom_toggle2';
|
||||
|
||||
// override the feature toggles defined in playwright.config.ts only for tests in this file
|
||||
test.use({
|
||||
featureToggles: {
|
||||
[TRUTHY_CUSTOM_TOGGLE]: true,
|
||||
[FALSY_CUSTOM_TOGGLE]: false,
|
||||
},
|
||||
});
|
||||
|
||||
test.describe(
|
||||
'plugin-e2e-api-tests admin',
|
||||
{
|
||||
tag: ['@plugins'],
|
||||
},
|
||||
() => {
|
||||
test('should set and check feature toggles correctly', async ({ isFeatureToggleEnabled }) => {
|
||||
expect(await isFeatureToggleEnabled(TRUTHY_CUSTOM_TOGGLE)).toBeTruthy();
|
||||
expect(await isFeatureToggleEnabled(FALSY_CUSTOM_TOGGLE)).toBeFalsy();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,95 @@
|
|||
import * as e2e from '@grafana/e2e-selectors';
|
||||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
test.describe(
|
||||
'plugin-e2e-api-tests admin',
|
||||
{
|
||||
tag: ['@plugins'],
|
||||
},
|
||||
() => {
|
||||
test.describe('Loki editor', () => {
|
||||
test('Autocomplete features should work as expected.', async ({ page }) => {
|
||||
// Go to loki datasource in explore
|
||||
await page.goto(
|
||||
'/explore?schemaVersion=1&panes=%7B%22iap%22:%7B%22datasource%22:%22gdev-loki%22,%22queries%22:%5B%7B%22refId%22:%22A%22,%22expr%22:%22%22,%22queryType%22:%22range%22,%22datasource%22:%7B%22type%22:%22loki%22,%22uid%22:%22gdev-loki%22%7D,%22editorMode%22:%22builder%22%7D%5D,%22range%22:%7B%22from%22:%22now-1h%22,%22to%22:%22now%22%7D%7D%7D&orgId=1'
|
||||
);
|
||||
|
||||
const queryEditor = page.getByTestId(e2e.selectors.components.QueryField.container);
|
||||
const queryEditorRows = page.getByTestId('query-editor-rows');
|
||||
|
||||
async function assertQueryEditorEmpty() {
|
||||
const queryEditorEmptyText = /^Enter to Rename.+/;
|
||||
await expect(queryEditor).toHaveText(queryEditorEmptyText);
|
||||
}
|
||||
|
||||
async function clearInput() {
|
||||
// Clear focused input
|
||||
// Monaco appears to need some time to init keybindings after a change, adding this timeout to prevent flake
|
||||
await page.waitForTimeout(100);
|
||||
await page.keyboard.press('ControlOrMeta+A');
|
||||
await page.keyboard.press('Backspace');
|
||||
}
|
||||
|
||||
// assert that the query builder is shown by default
|
||||
await expect(page.getByText('Label filters')).toHaveCount(1);
|
||||
|
||||
// switch to code editor
|
||||
await page.getByLabel('Code').click();
|
||||
|
||||
await page.waitForFunction(() => window.monaco);
|
||||
await expect(queryEditor).toHaveCount(1);
|
||||
await assertQueryEditorEmpty();
|
||||
|
||||
// assert editor automatically adds close paren
|
||||
await queryEditor.click();
|
||||
await page.keyboard.type('time(');
|
||||
await expect(queryEditor).toContainText('time()');
|
||||
|
||||
// removes closing brace when opening brace is removed
|
||||
await clearInput();
|
||||
await assertQueryEditorEmpty();
|
||||
await page.keyboard.type('avg_over_time(');
|
||||
await expect(queryEditor).toContainText('avg_over_time()');
|
||||
await page.keyboard.press('Backspace');
|
||||
await expect(queryEditor).not.toContainText('avg_over_time()');
|
||||
await expect(queryEditor).toContainText('avg_over_time');
|
||||
|
||||
// keeps closing brace when opening brace is removed and inner values exist
|
||||
await clearInput();
|
||||
await assertQueryEditorEmpty();
|
||||
await page.keyboard.type('time(test');
|
||||
await page.keyboard.press('ArrowLeft');
|
||||
await page.keyboard.press('ArrowLeft');
|
||||
await page.keyboard.press('ArrowLeft');
|
||||
await page.keyboard.press('ArrowLeft');
|
||||
await page.keyboard.press('Backspace');
|
||||
await expect(queryEditor).toContainText('timetest');
|
||||
|
||||
// overrides an automatically inserted paren
|
||||
await clearInput();
|
||||
await assertQueryEditorEmpty();
|
||||
await page.keyboard.type('time()');
|
||||
await expect(queryEditor).toContainText('time()');
|
||||
|
||||
// does not override manually inserted braces
|
||||
await clearInput();
|
||||
await assertQueryEditorEmpty();
|
||||
await page.keyboard.type('))');
|
||||
await expect(queryEditor).toContainText('))');
|
||||
|
||||
// Should execute the query when enter with shift is pressed
|
||||
await clearInput();
|
||||
await assertQueryEditorEmpty();
|
||||
await page.keyboard.press('Shift+Enter');
|
||||
await expect(page.getByTestId('explore-no-data')).toHaveCount(1);
|
||||
|
||||
// Suggestions plugin
|
||||
await clearInput();
|
||||
await assertQueryEditorEmpty();
|
||||
await page.keyboard.type('av');
|
||||
await expect(queryEditorRows.getByLabel(/avg, docs:/)).toHaveCount(1);
|
||||
await expect(queryEditorRows.getByLabel(/avg_over_time, docs:/)).toHaveCount(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,105 @@
|
|||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
import { formatExpectError } from '../errors';
|
||||
import { successfulDataQuery } from '../mocks/queries';
|
||||
|
||||
const REACT_TABLE_DASHBOARD = { uid: 'U_bZIMRMk' };
|
||||
|
||||
test.describe(
|
||||
'plugin-e2e-api-tests admin',
|
||||
{
|
||||
tag: ['@plugins'],
|
||||
},
|
||||
() => {
|
||||
test.describe('panel edit page', () => {
|
||||
test('table panel data assertions with provisioned dashboard', async ({ gotoPanelEditPage }) => {
|
||||
const panelEditPage = await gotoPanelEditPage({ dashboard: REACT_TABLE_DASHBOARD, id: '4' });
|
||||
await expect(
|
||||
panelEditPage.panel.locator,
|
||||
formatExpectError('Could not locate panel in panel edit page')
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
panelEditPage.panel.fieldNames,
|
||||
formatExpectError('Could not locate header elements in table panel')
|
||||
).toContainText(['Field', 'Max', 'Mean', 'Last']);
|
||||
});
|
||||
|
||||
test('table panel data assertions', async ({ panelEditPage }) => {
|
||||
await panelEditPage.mockQueryDataResponse(successfulDataQuery, 200);
|
||||
await panelEditPage.datasource.set('gdev-testdata');
|
||||
await panelEditPage.setVisualization('Table');
|
||||
await panelEditPage.refreshPanel();
|
||||
await expect(
|
||||
panelEditPage.panel.locator,
|
||||
formatExpectError('Could not locate panel in panel edit page')
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
panelEditPage.panel.fieldNames,
|
||||
formatExpectError('Could not locate header elements in table panel')
|
||||
).toContainText(['col1', 'col2']);
|
||||
await expect(
|
||||
panelEditPage.panel.data,
|
||||
formatExpectError('Could not locate headers in table panel')
|
||||
).toContainText(['val1', 'val2', 'val3', 'val4']);
|
||||
});
|
||||
|
||||
test('timeseries panel - table view assertions', async ({ panelEditPage }) => {
|
||||
await panelEditPage.mockQueryDataResponse(successfulDataQuery, 200);
|
||||
await panelEditPage.datasource.set('gdev-testdata');
|
||||
await panelEditPage.setVisualization('Time series');
|
||||
await panelEditPage.refreshPanel();
|
||||
await panelEditPage.toggleTableView();
|
||||
await expect(
|
||||
panelEditPage.panel.locator,
|
||||
formatExpectError('Could not locate panel in panel edit page')
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
panelEditPage.panel.fieldNames,
|
||||
formatExpectError('Could not locate header elements in table panel')
|
||||
).toContainText(['col1', 'col2']);
|
||||
await expect(
|
||||
panelEditPage.panel.data,
|
||||
formatExpectError('Could not locate data elements in table panel')
|
||||
).toContainText(['val1', 'val2', 'val3', 'val4']);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('dashboard page', () => {
|
||||
test('getting panel by title', async ({ gotoDashboardPage }) => {
|
||||
const dashboardPage = await gotoDashboardPage(REACT_TABLE_DASHBOARD);
|
||||
await dashboardPage.goto();
|
||||
const panel = await dashboardPage.getPanelByTitle('Colored background');
|
||||
await expect(panel.fieldNames).toContainText(['Field', 'Max', 'Mean', 'Last']);
|
||||
});
|
||||
|
||||
test('getting panel by id', async ({ gotoDashboardPage }) => {
|
||||
const dashboardPage = await gotoDashboardPage(REACT_TABLE_DASHBOARD);
|
||||
await dashboardPage.goto();
|
||||
const panel = await dashboardPage.getPanelByTitle('Colored background');
|
||||
await expect(
|
||||
panel.fieldNames,
|
||||
formatExpectError('Could not locate header elements in table panel')
|
||||
).toContainText(['Field', 'Max', 'Mean', 'Last']);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('explore page', () => {
|
||||
test('table panel', async ({ explorePage }) => {
|
||||
const url =
|
||||
'left=%7B"datasource":"grafana","queries":%5B%7B"queryType":"randomWalk","refId":"A","datasource":%7B"type":"datasource","uid":"grafana"%7D%7D%5D,"range":%7B"from":"1547161200000","to":"1576364400000"%7D%7D&orgId=1';
|
||||
await explorePage.goto({
|
||||
queryParams: new URLSearchParams(url),
|
||||
});
|
||||
await expect(
|
||||
explorePage.timeSeriesPanel.locator,
|
||||
formatExpectError('Could not locate time series panel in explore page')
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
explorePage.tablePanel.locator,
|
||||
formatExpectError('Could not locate table panel in explore page')
|
||||
).toBeVisible();
|
||||
await expect(explorePage.tablePanel.fieldNames).toContainText(['time', 'A-series']);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,214 @@
|
|||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
import { formatExpectError } from '../errors';
|
||||
import { successfulDataQuery } from '../mocks/queries';
|
||||
import { scenarios } from '../mocks/resources';
|
||||
|
||||
const PANEL_TITLE = 'Table panel E2E test';
|
||||
const TABLE_VIZ_NAME = 'Table';
|
||||
const TIME_SERIES_VIZ_NAME = 'Time series';
|
||||
const STANDARD_OTIONS_CATEGORY = 'Standard options';
|
||||
const DISPLAY_NAME_LABEL = 'Display name';
|
||||
const REACT_TABLE_DASHBOARD = { uid: 'U_bZIMRMk' };
|
||||
|
||||
test.describe(
|
||||
'plugin-e2e-api-tests admin',
|
||||
{
|
||||
tag: ['@plugins'],
|
||||
},
|
||||
() => {
|
||||
test.describe('query editor query data', () => {
|
||||
test('query data response should be OK when query is valid', async ({ panelEditPage }) => {
|
||||
await panelEditPage.datasource.set('gdev-testdata');
|
||||
await expect(
|
||||
panelEditPage.refreshPanel(),
|
||||
formatExpectError('Expected panel query to execute successfully')
|
||||
).toBeOK();
|
||||
});
|
||||
|
||||
test('query data response should not be OK and panel error should be displayed when query is invalid', async ({
|
||||
panelEditPage,
|
||||
}) => {
|
||||
await panelEditPage.datasource.set('gdev-testdata');
|
||||
const queryEditorRow = await panelEditPage.getQueryEditorRow('A');
|
||||
await queryEditorRow.getByLabel('Labels').fill('invalid-label-format');
|
||||
await expect(panelEditPage.refreshPanel(), formatExpectError('Expected panel query to fail')).not.toBeOK();
|
||||
await expect(
|
||||
panelEditPage.panel.getErrorIcon(),
|
||||
formatExpectError('Expected panel error to be displayed after query execution')
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('query editor with mocked responses', () => {
|
||||
test('and resource `scenarios` is mocked', async ({ selectors, dashboardPage }) => {
|
||||
await dashboardPage.mockResourceResponse('scenarios', scenarios);
|
||||
const panelEditPage = await dashboardPage.addPanel();
|
||||
await panelEditPage.datasource.set('gdev-testdata');
|
||||
const queryEditorRow = await panelEditPage.getQueryEditorRow('A');
|
||||
await queryEditorRow.getByLabel('Scenario').last().click();
|
||||
await expect(
|
||||
panelEditPage.getByGrafanaSelector(selectors.components.Select.option),
|
||||
formatExpectError('Expected certain select options to be displayed after clicking on the select input')
|
||||
).toHaveText(scenarios.map((s) => s.name));
|
||||
});
|
||||
|
||||
test('mocked query data response', async ({ panelEditPage, selectors }) => {
|
||||
await panelEditPage.mockQueryDataResponse(successfulDataQuery, 200);
|
||||
await panelEditPage.datasource.set('gdev-testdata');
|
||||
await panelEditPage.setVisualization(TABLE_VIZ_NAME);
|
||||
await panelEditPage.refreshPanel();
|
||||
await expect(
|
||||
panelEditPage.panel.getErrorIcon(),
|
||||
formatExpectError('Did not expect panel error to be displayed after query execution')
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
panelEditPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.body),
|
||||
formatExpectError('Expected certain select options to be displayed after clicking on the select input')
|
||||
).toHaveText('val1val2val3val4');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('edit panel plugin settings', () => {
|
||||
test('change viz to table panel, set panel title and collapse section', async ({
|
||||
panelEditPage,
|
||||
selectors,
|
||||
page,
|
||||
}) => {
|
||||
await panelEditPage.setVisualization(TABLE_VIZ_NAME);
|
||||
await expect(
|
||||
panelEditPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleVizPicker),
|
||||
formatExpectError('Expected panel visualization to be set to table')
|
||||
).toHaveText(TABLE_VIZ_NAME);
|
||||
await panelEditPage.setPanelTitle(PANEL_TITLE);
|
||||
await expect(
|
||||
panelEditPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(PANEL_TITLE)),
|
||||
formatExpectError('Expected panel title to be updated')
|
||||
).toBeVisible();
|
||||
await panelEditPage.collapseSection(STANDARD_OTIONS_CATEGORY);
|
||||
await expect(
|
||||
page.getByText(DISPLAY_NAME_LABEL),
|
||||
formatExpectError('Expected section to be collapsed')
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Select time zone in timezone picker', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const axisOptions = await panelEditPage.getCustomOptions('Axis');
|
||||
const timeZonePicker = axisOptions.getSelect('Time zone');
|
||||
|
||||
await timeZonePicker.selectOption('Europe/Stockholm');
|
||||
await expect(timeZonePicker).toHaveSelected('Europe/Stockholm');
|
||||
});
|
||||
|
||||
test('select unit in unit picker', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const standardOptions = panelEditPage.getStandardOptions();
|
||||
const unitPicker = standardOptions.getUnitPicker('Unit');
|
||||
|
||||
await unitPicker.selectOption('Misc > Pixels');
|
||||
|
||||
await expect(unitPicker).toHaveSelected('Pixels');
|
||||
});
|
||||
|
||||
test('enter value in number input', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const axisOptions = panelEditPage.getCustomOptions('Axis');
|
||||
const lineWith = axisOptions.getNumberInput('Soft min');
|
||||
|
||||
await lineWith.fill('10');
|
||||
|
||||
await expect(lineWith).toHaveValue('10');
|
||||
});
|
||||
|
||||
test('enter value in slider', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const graphOptions = panelEditPage.getCustomOptions('Graph styles');
|
||||
const lineWidth = graphOptions.getSliderInput('Line width');
|
||||
|
||||
await lineWidth.fill('10');
|
||||
|
||||
await expect(lineWidth).toHaveValue('10');
|
||||
});
|
||||
|
||||
test('select value in single value select', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const standardOptions = panelEditPage.getStandardOptions();
|
||||
const colorSchemeSelect = standardOptions.getSelect('Color scheme');
|
||||
|
||||
await colorSchemeSelect.selectOption('Classic palette');
|
||||
await expect(colorSchemeSelect).toHaveSelected('Classic palette');
|
||||
});
|
||||
|
||||
test('clear input', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const panelOptions = panelEditPage.getPanelOptions();
|
||||
const title = panelOptions.getTextInput('Title');
|
||||
|
||||
await expect(title).toHaveValue('New panel');
|
||||
await title.clear();
|
||||
await expect(title).toHaveValue('');
|
||||
});
|
||||
|
||||
test('enter value in input', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const panelOptions = panelEditPage.getPanelOptions();
|
||||
const description = panelOptions.getTextInput('Description');
|
||||
|
||||
await expect(description).toHaveValue('');
|
||||
await description.fill('This is a panel');
|
||||
await expect(description).toHaveValue('This is a panel');
|
||||
});
|
||||
|
||||
test('unchecking switch', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const axisOptions = panelEditPage.getCustomOptions('Axis');
|
||||
const showBorder = axisOptions.getSwitch('Show border');
|
||||
|
||||
await expect(showBorder).toBeChecked({ checked: false });
|
||||
await showBorder.check();
|
||||
await expect(showBorder).toBeChecked();
|
||||
|
||||
await showBorder.uncheck();
|
||||
await expect(showBorder).toBeChecked({ checked: false });
|
||||
});
|
||||
|
||||
test('checking switch', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const axisOptions = panelEditPage.getCustomOptions('Axis');
|
||||
const showBorder = axisOptions.getSwitch('Show border');
|
||||
|
||||
await expect(showBorder).toBeChecked({ checked: false });
|
||||
await showBorder.check();
|
||||
await expect(showBorder).toBeChecked();
|
||||
});
|
||||
|
||||
test('re-selecting value in radio button group', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const axisOptions = panelEditPage.getCustomOptions('Axis');
|
||||
const placement = axisOptions.getRadioGroup('Placement');
|
||||
|
||||
await placement.check('Right');
|
||||
await expect(placement).toHaveChecked('Right');
|
||||
|
||||
await placement.check('Auto');
|
||||
await expect(placement).toHaveChecked('Auto');
|
||||
});
|
||||
|
||||
test('selecting value in radio button group', async ({ panelEditPage }) => {
|
||||
await panelEditPage.setVisualization(TIME_SERIES_VIZ_NAME);
|
||||
const axisOptions = panelEditPage.getCustomOptions('Axis');
|
||||
const placement = axisOptions.getRadioGroup('Placement');
|
||||
|
||||
await placement.check('Right');
|
||||
await expect(placement).toHaveChecked('Right');
|
||||
});
|
||||
});
|
||||
|
||||
test('backToDashboard method should navigate to dashboard page', async ({ gotoPanelEditPage, page }) => {
|
||||
const panelEditPage = await gotoPanelEditPage({ dashboard: REACT_TABLE_DASHBOARD, id: '4' });
|
||||
await panelEditPage.backToDashboard();
|
||||
await expect(page.url()).not.toContain('editPanel');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,25 @@
|
|||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
import { formatExpectError } from '../errors';
|
||||
import { prometheusLabels } from '../mocks/resources';
|
||||
|
||||
test.describe(
|
||||
'plugin-e2e-api-tests admin',
|
||||
{
|
||||
tag: ['@plugins'],
|
||||
},
|
||||
() => {
|
||||
test('variable query with mocked response', async ({ variableEditPage, page }) => {
|
||||
variableEditPage.mockResourceResponse('api/v1/labels?*', prometheusLabels);
|
||||
variableEditPage.mockResourceResponse('suggestions*', prometheusLabels);
|
||||
await variableEditPage.datasource.set('gdev-prometheus');
|
||||
await variableEditPage.getByGrafanaSelector('Query type').fill('Label names');
|
||||
await page.keyboard.press('Tab');
|
||||
await variableEditPage.runQuery();
|
||||
await expect(
|
||||
variableEditPage,
|
||||
formatExpectError('Expected variable edit page to display certain label names after query execution')
|
||||
).toDisplayPreviews(prometheusLabels.data);
|
||||
});
|
||||
}
|
||||
);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue