E2E: Add performance monitoring E2E test (#109417)
* initial workflow * add bench reporter * add pr trigger * update workflow * actually add the test * debug ..... * more debug * this it??? * Debugging GHA workflow https newline tree cache node modules try bench again fix path * Add RequestsRecorder and tweak GHA workflow * More debugging GHA workflow cache cache key try renaming path specify env var correctly * Run tests on both envs, wait for requests to finish fix status cat stats cat stats * prefix metric names * cleanup * cleanup gha * codeowners * make gen-jsonnet * quote things * Add host_type label for categorising the various hosts * rename pw_boot_time_seconds to pw_test_run_time_seconds * change prefix * codeowners, gha name * remove node-modules caching * fix codeowners
This commit is contained in:
@@ -499,6 +499,7 @@
|
||||
/e2e-playwright/various-suite/trace-view-scrolling.spec.ts @grafana/observability-traces-and-profiling
|
||||
/e2e-playwright/various-suite/verify-i18n.spec.ts @grafana/grafana-frontend-platform
|
||||
/e2e-playwright/various-suite/visualization-suggestions.spec.ts @grafana/dashboards-squad
|
||||
/e2e-playwright/various-suite/perf-test.spec.ts @grafana/grafana-frontend-platform
|
||||
|
||||
# Packages
|
||||
/packages/README.md @grafana/grafana-frontend-platform
|
||||
@@ -1253,6 +1254,7 @@ embed.go @grafana/grafana-as-code
|
||||
/.github/workflows/analytics-events-report.yml @grafana/grafana-frontend-platform
|
||||
/.github/workflows/pr-e2e-tests.yml @grafana/grafana-developer-enablement-squad
|
||||
/.github/workflows/skye-add-to-project.yml @grafana/grafana-frontend-platform
|
||||
/.github/workflows/frontend-perf-tests.yaml @grafana/grafana-frontend-platform
|
||||
/.github/zizmor.yml @grafana/grafana-developer-enablement-squad
|
||||
/.github/license_finder.yaml @bergquist
|
||||
/.github/actionlint.yaml @grafana/grafana-developer-enablement-squad
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
name: Frontend performance tests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 * * * *" # Run hourly
|
||||
workflow_dispatch: # Allow manual triggering
|
||||
|
||||
jobs:
|
||||
performance-tests:
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
runs-on: ubuntu-x64-large
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- id: get-secrets
|
||||
uses: grafana/shared-workflows/actions/get-vault-secrets@62722333225a1fae03ae27a63d638f9bc2176edb #get-vault-secrets-v1.2.0
|
||||
with:
|
||||
repo_secrets: |
|
||||
PROMETHEUS_URL=frontend_perf_tests:prometheus_push_url
|
||||
PROMETHEUS_USER=frontend_perf_tests:prometheus_user
|
||||
PROMETHEUS_TOKEN=frontend_perf_tests:prometheus_token
|
||||
FSPERFBASELINE_USERNAME=frontend_perf_tests:fsperfbaseline_username
|
||||
FSPERFBASELINE_PASSWORD=frontend_perf_tests:fsperfbaseline_password
|
||||
FSPERF_USERNAME=frontend_perf_tests:fsperf_username
|
||||
FSPERF_PASSWORD=frontend_perf_tests:fsperf_password
|
||||
|
||||
- name: Authenticate Docker
|
||||
uses: grafana/shared-workflows/actions/login-to-gar@main
|
||||
id: login-to-gar
|
||||
with:
|
||||
registry: 'us-docker.pkg.dev'
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Yarn install
|
||||
run: yarn install --immutable
|
||||
env:
|
||||
PUPPETEER_SKIP_DOWNLOAD: true
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
|
||||
- name: Install Playwright browsers
|
||||
run: yarn playwright install chromium --with-deps
|
||||
|
||||
- name: Run Playwright tests (fsperfbaseline)
|
||||
id: pw-fsperfbaseline
|
||||
continue-on-error: true
|
||||
env:
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME: ./fsperfbaseline-pw-results.json
|
||||
METRICS_OUTPUT_PATH: ./fsperfbaseline-metrics.txt
|
||||
GRAFANA_URL: https://fsperfbaseline.grafana-dev.net
|
||||
GRAFANA_ADMIN_USER: ${{ env.FSPERFBASELINE_USERNAME }}
|
||||
GRAFANA_ADMIN_PASSWORD: ${{ env.FSPERFBASELINE_PASSWORD }}
|
||||
run: yarn e2e:playwright --grep @performance --reporter json
|
||||
|
||||
- name: Run Playwright tests (fsperf)
|
||||
id: pw-fsperf
|
||||
continue-on-error: true
|
||||
env:
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME: ./fsperf-pw-results.json
|
||||
METRICS_OUTPUT_PATH: ./fsperf-metrics.txt
|
||||
GRAFANA_URL: https://fsperf.grafana-dev.net
|
||||
GRAFANA_ADMIN_USER: ${{ env.FSPERF_USERNAME }}
|
||||
GRAFANA_ADMIN_PASSWORD: ${{ env.FSPERF_PASSWORD }}
|
||||
run: yarn e2e:playwright --grep @performance --reporter json
|
||||
|
||||
- name: Bench report (fsperfbaseline)
|
||||
id: bench-fsperfbaseline
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
run: |
|
||||
docker run \
|
||||
--rm \
|
||||
--volume="./fsperfbaseline-pw-results.json:/pw-results.json" \
|
||||
--volume="./fsperfbaseline-metrics.txt:/metrics.txt" \
|
||||
-e PROMETHEUS_URL="$PROMETHEUS_URL" \
|
||||
-e PROMETHEUS_USER="$PROMETHEUS_USER" \
|
||||
-e PROMETHEUS_PASSWORD="$PROMETHEUS_TOKEN" \
|
||||
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.6.0 report \
|
||||
--grafana-url "http://fsperfbaseline.grafana-dev.net" \
|
||||
--test-suite-name "FrontendPerfTests" \
|
||||
--report-input playwright \
|
||||
--report-output log \
|
||||
--prometheus-metrics \
|
||||
--run-metrics-file "/metrics.txt" \
|
||||
--log-level DEBUG \
|
||||
/pw-results.json
|
||||
|
||||
- name: Bench report (fsperf)
|
||||
id: bench-fsperf
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
run: |
|
||||
docker run \
|
||||
--rm \
|
||||
--volume="./fsperf-pw-results.json:/pw-results.json" \
|
||||
--volume="./fsperf-metrics.txt:/metrics.txt" \
|
||||
-e PROMETHEUS_URL="$PROMETHEUS_URL" \
|
||||
-e PROMETHEUS_USER="$PROMETHEUS_USER" \
|
||||
-e PROMETHEUS_PASSWORD="$PROMETHEUS_TOKEN" \
|
||||
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.6.0 report \
|
||||
--grafana-url "http://fsperf.grafana-dev.net" \
|
||||
--test-suite-name "FrontendPerfTests" \
|
||||
--report-input playwright \
|
||||
--report-output log \
|
||||
--prometheus-metrics \
|
||||
--run-metrics-file "/metrics.txt" \
|
||||
--log-level DEBUG \
|
||||
/pw-results.json
|
||||
|
||||
- name: Check status
|
||||
env:
|
||||
FSPERF_PW_OUTCOME: ${{ steps.pw-fsperf.outcome }}
|
||||
FSPERF_PW_BASELINE_OUTCOME: ${{ steps.pw-fsperfbaseline.outcome }}
|
||||
FSPERF_BENCH_OUTCOME: ${{ steps.bench-fsperf.outcome }}
|
||||
FSPERF_BENCH_BASELINE_OUTCOME: ${{ steps.bench-fsperfbaseline.outcome }}
|
||||
|
||||
run: |
|
||||
echo "FSPERF_PW_OUTCOME: $FSPERF_PW_OUTCOME"
|
||||
echo "FSPERF_PW_BASELINE_OUTCOME: $FSPERF_PW_BASELINE_OUTCOME"
|
||||
echo "FSPERF_BENCH_OUTCOME: $FSPERF_BENCH_OUTCOME"
|
||||
echo "FSPERF_BENCH_BASELINE_OUTCOME: $FSPERF_BENCH_BASELINE_OUTCOME"
|
||||
|
||||
if [[ "$FSPERF_PW_OUTCOME" != "success" || "$FSPERF_PW_BASELINE_OUTCOME" != "success" || "$FSPERF_BENCH_OUTCOME" != "success" || "$FSPERF_BENCH_BASELINE_OUTCOME" != "success" ]]; then
|
||||
echo "One or more test steps failed."
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"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,
|
||||
"id": 10,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"maxDataPoints": 10,
|
||||
"options": {
|
||||
"counters": {
|
||||
"dataChanged": true,
|
||||
"render": true,
|
||||
"schemaChanged": true
|
||||
},
|
||||
"mode": "events",
|
||||
"stateView": ""
|
||||
},
|
||||
"pluginVersion": "12.2.0-16975765900",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Panel Title",
|
||||
"type": "debug"
|
||||
}
|
||||
],
|
||||
"preload": false,
|
||||
"refresh": "",
|
||||
"schemaVersion": 41,
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "browser",
|
||||
"title": "Mostly blank dashboard",
|
||||
"uid": "bds35fot3cv7kb",
|
||||
"version": 3
|
||||
}
|
||||
@@ -69,6 +69,7 @@
|
||||
"loki_fakedata": (import '../dev-dashboards/datasource-loki/loki_fakedata.json'),
|
||||
"loki_query_splitting": (import '../dev-dashboards/datasource-loki/loki_query_splitting.json'),
|
||||
"migrations": (import '../dev-dashboards/migrations/migrations.json'),
|
||||
"mostly-blank-dashboard": (import '../dev-dashboards/scenarios/mostly-blank-dashboard.json'),
|
||||
"mssql_fakedata": (import '../dev-dashboards/datasource-mssql/mssql_fakedata.json'),
|
||||
"mssql_unittest": (import '../dev-dashboards/datasource-mssql/mssql_unittest.json'),
|
||||
"mysql_fakedata": (import '../dev-dashboards/datasource-mysql/mysql_fakedata.json'),
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
import { Page, Response, Request } from '@playwright/test';
|
||||
import * as prom from 'prom-client';
|
||||
|
||||
/**
|
||||
* Records and tracks network request body sizes.
|
||||
*
|
||||
* You must call listen() before page.goto() for accurate results - this ensures all responses
|
||||
* for a page are tracked.
|
||||
*/
|
||||
export class RequestsRecorder {
|
||||
#page: Page;
|
||||
|
||||
#documentUrl: string | undefined;
|
||||
|
||||
#requestsInFlight = 0;
|
||||
|
||||
#currentRequests: Set<Request> = new Set<Request>();
|
||||
|
||||
#inflatedSizeBytesCounter: prom.Counter<'type' | 'host_type'>;
|
||||
#transferSizeBytesCounter: prom.Counter<'type' | 'host_type'>;
|
||||
#requestCountCounter: prom.Counter<'type' | 'host_type'>;
|
||||
#resolve?: () => void;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.#page = page;
|
||||
|
||||
this.#inflatedSizeBytesCounter = new prom.Counter({
|
||||
name: 'fe_perf_inflated_size_bytes',
|
||||
help: 'The size of the inflated response body in bytes',
|
||||
labelNames: ['type', 'host_type'],
|
||||
registers: [],
|
||||
});
|
||||
|
||||
this.#transferSizeBytesCounter = new prom.Counter({
|
||||
name: 'fe_perf_transfer_size_bytes',
|
||||
help: 'The size of the transfered response body in bytes',
|
||||
labelNames: ['type', 'host_type'],
|
||||
registers: [],
|
||||
});
|
||||
|
||||
this.#requestCountCounter = new prom.Counter({
|
||||
name: 'fe_perf_request_count',
|
||||
help: 'The number of requests made',
|
||||
labelNames: ['type', 'host_type'],
|
||||
registers: [],
|
||||
});
|
||||
}
|
||||
|
||||
listen() {
|
||||
const handler = this.#handleResponse.bind(this);
|
||||
const reqHandler = this.#handleRequest.bind(this);
|
||||
this.#page.on('request', reqHandler);
|
||||
this.#page.on('response', handler);
|
||||
|
||||
return () => {
|
||||
this.#page.off('response', handler);
|
||||
this.#page.off('request', reqHandler);
|
||||
|
||||
if (this.#requestsInFlight === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
console.log('waiting for', this.#requestsInFlight, 'requests to finish');
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
this.#resolve = resolve;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
async #handleRequest(request: Request) {
|
||||
const type = request.resourceType();
|
||||
|
||||
// Once we've recieved a document response, create a list of requests that we'll count the responses of.
|
||||
// We also want to count the document request itself.
|
||||
if (this.#documentUrl || type === 'document') {
|
||||
this.#currentRequests.add(request);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The Playwright page object has have multiple page loads, and responses for one page may come in after the page
|
||||
* has been navigated away from. Attempting to get the body of these responses will results in an error, but is also
|
||||
* not what we want to track.
|
||||
*
|
||||
* To solve this, we wait for the 'document' response to come in (a new page has loaded) and then keep track of all
|
||||
* future requests. We only record responses for requests that were made after the document response.
|
||||
*/
|
||||
async #handleResponse(response: Response) {
|
||||
const request = response.request();
|
||||
|
||||
const url = response.url();
|
||||
const type = response.request().resourceType();
|
||||
|
||||
// Record when a document response comes in so we can keep track of future requests
|
||||
if (type === 'document') {
|
||||
if (this.#documentUrl) {
|
||||
console.warn('recieved additional document response', url);
|
||||
}
|
||||
|
||||
this.#documentUrl = url;
|
||||
}
|
||||
|
||||
// Disregard responses that for requests that were initiated before the current page
|
||||
if (!this.#currentRequests.has(request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#requestsInFlight += 1;
|
||||
const hostType = getHostType(response.url(), this.#documentUrl ?? '');
|
||||
|
||||
// Attempting to get the body of an empty response results in an error, so guess if the response will be empty or not
|
||||
const statusCode = response.status();
|
||||
const noBodyStatusCode = statusCode <= 199 || statusCode === 204 || statusCode === 205 || statusCode === 304;
|
||||
|
||||
if (!noBodyStatusCode) {
|
||||
const body = await response.body();
|
||||
this.#inflatedSizeBytesCounter.inc({ type, host_type: hostType }, body.length);
|
||||
}
|
||||
|
||||
const sizes = await response.request().sizes();
|
||||
|
||||
this.#transferSizeBytesCounter.inc(
|
||||
{ type, host_type: hostType },
|
||||
sizes.responseBodySize + sizes.responseHeadersSize
|
||||
);
|
||||
this.#requestCountCounter.inc({ type, host_type: hostType });
|
||||
|
||||
this.#requestsInFlight -= 1;
|
||||
|
||||
if (this.#requestsInFlight === 0 && this.#resolve) {
|
||||
this.#resolve();
|
||||
}
|
||||
}
|
||||
|
||||
getMetrics() {
|
||||
return [this.#inflatedSizeBytesCounter, this.#transferSizeBytesCounter, this.#requestCountCounter];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of setting the request host as a label, which may have too high cardinality, we categorise
|
||||
* the host into a limited set of types.
|
||||
*/
|
||||
function getHostType(requestUrl: string, documentUrl: string) {
|
||||
const url = new URL(requestUrl);
|
||||
const hostname = url.hostname; // FYI `hostname` doesn't include port, `host` does
|
||||
|
||||
const documentHost = new URL(documentUrl).hostname;
|
||||
|
||||
if (hostname.match(/^grafana-assets\.grafana(-\w+)?\.net$/)) {
|
||||
return 'assets_cdn';
|
||||
}
|
||||
|
||||
if (hostname.match(/^plugins-cdn\.grafana(-\w+)?\.net$/)) {
|
||||
return 'plugin_cdn';
|
||||
}
|
||||
|
||||
if (hostname === documentHost) {
|
||||
return 'self';
|
||||
}
|
||||
|
||||
return 'other';
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import fs from 'fs';
|
||||
import * as prom from 'prom-client';
|
||||
|
||||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import { RequestsRecorder } from '../utils/RequestsRecorder';
|
||||
|
||||
const DASH_PATH = '/d/bds35fot3cv7kb/mostly-blank-dashboard';
|
||||
|
||||
test('payload-size', { tag: '@performance' }, async ({ page }) => {
|
||||
const promRegistry = new prom.Registry();
|
||||
|
||||
const testRunTimeGauge = new prom.Gauge({
|
||||
name: 'fe_perf_test_run_time_seconds',
|
||||
help: 'The time it took for the performance test to run',
|
||||
registers: [promRegistry],
|
||||
});
|
||||
|
||||
const usedJSHeapSizeGauge = new prom.Gauge({
|
||||
name: 'fe_perf_used_js_heap_size_bytes',
|
||||
help: 'The amount of memory used by the JavaScript heap',
|
||||
registers: [promRegistry],
|
||||
});
|
||||
|
||||
const recorder = new RequestsRecorder(page);
|
||||
const stopListening = recorder.listen();
|
||||
|
||||
let start = performance.now();
|
||||
|
||||
await page.goto(DASH_PATH);
|
||||
|
||||
let el = page.getByTestId('data-testid header-container');
|
||||
await el.waitFor();
|
||||
await expect(el).toBeVisible();
|
||||
|
||||
let end = performance.now();
|
||||
|
||||
let client = await page.context().newCDPSession(page);
|
||||
await client.send('HeapProfiler.collectGarbage');
|
||||
let usedJSHeapSize = (await client.send('Runtime.getHeapUsage')).usedSize;
|
||||
|
||||
const responseMetrics = recorder.getMetrics();
|
||||
for (const metric of responseMetrics) {
|
||||
promRegistry.registerMetric(metric);
|
||||
}
|
||||
|
||||
testRunTimeGauge.set(Math.round(end - start) / 1000);
|
||||
usedJSHeapSizeGauge.set(+usedJSHeapSize.toFixed(1));
|
||||
|
||||
const instance = new URL(process.env.GRAFANA_URL || 'http://undefined').host;
|
||||
promRegistry.setDefaultLabels({ instance });
|
||||
const metricsText = await promRegistry.metrics();
|
||||
console.log(metricsText);
|
||||
fs.writeFileSync(process.env.METRICS_OUTPUT_PATH || '/tmp/asset-metrics.txt', metricsText);
|
||||
|
||||
await stopListening();
|
||||
page.close();
|
||||
});
|
||||
@@ -234,6 +234,7 @@
|
||||
"postcss-reporter": "7.1.0",
|
||||
"postcss-scss": "4.0.9",
|
||||
"prettier": "3.6.2",
|
||||
"prom-client": "^15.1.3",
|
||||
"publint": "^0.3.12",
|
||||
"react-refresh": "0.14.0",
|
||||
"react-select-event": "5.5.1",
|
||||
|
||||
+61
-165
@@ -1,4 +1,4 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
import { defineConfig, devices, PlaywrightTestConfig, Project } from '@playwright/test';
|
||||
import path, { dirname } from 'path';
|
||||
|
||||
import { PluginOptions } from '@grafana/plugin-e2e';
|
||||
@@ -7,6 +7,19 @@ const testDirRoot = 'e2e-playwright';
|
||||
const pluginDirRoot = path.join(testDirRoot, 'plugin-e2e');
|
||||
const DEFAULT_URL = 'http://localhost:3001';
|
||||
|
||||
function withAuth(project: Project): Project {
|
||||
project.dependencies ??= [];
|
||||
project.use ??= {};
|
||||
|
||||
project.dependencies = project.dependencies.concat('authenticate');
|
||||
project.use = {
|
||||
...project.use,
|
||||
storageState: `playwright/.auth/${process.env.GRAFANA_ADMIN_USER || 'admin'}.json`,
|
||||
};
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
export default defineConfig<PluginOptions>({
|
||||
fullyParallel: true,
|
||||
/* Retry on CI only */
|
||||
@@ -20,6 +33,7 @@ export default defineConfig<PluginOptions>({
|
||||
timeout: 10_000,
|
||||
},
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
baseURL: process.env.GRAFANA_URL ?? DEFAULT_URL,
|
||||
trace: 'retain-on-failure',
|
||||
httpCredentials: {
|
||||
@@ -59,15 +73,10 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
},
|
||||
// Run all tests in parallel using user with admin role
|
||||
{
|
||||
withAuth({
|
||||
name: 'admin',
|
||||
testDir: path.join(pluginDirRoot, '/plugin-e2e-api-tests/as-admin-user'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
}),
|
||||
// Run all tests in parallel using user with viewer role
|
||||
{
|
||||
name: 'viewer',
|
||||
@@ -78,210 +87,97 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
dependencies: ['createUserAndAuthenticate'],
|
||||
},
|
||||
{
|
||||
withAuth({
|
||||
name: 'elasticsearch',
|
||||
testDir: path.join(pluginDirRoot, '/elasticsearch'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'mysql',
|
||||
testDir: path.join(pluginDirRoot, '/mysql'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'mssql',
|
||||
testDir: path.join(pluginDirRoot, '/mssql'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'extensions-test-app',
|
||||
testDir: path.join(testDirRoot, '/test-plugins/grafana-extensionstest-app'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'grafana-e2etest-datasource',
|
||||
testDir: path.join(testDirRoot, '/test-plugins/grafana-test-datasource'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'cloudwatch',
|
||||
testDir: path.join(pluginDirRoot, '/cloudwatch'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'azuremonitor',
|
||||
testDir: path.join(pluginDirRoot, '/azuremonitor'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'cloudmonitoring',
|
||||
testDir: path.join(pluginDirRoot, '/cloudmonitoring'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'graphite',
|
||||
testDir: path.join(pluginDirRoot, '/graphite'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'influxdb',
|
||||
testDir: path.join(pluginDirRoot, '/influxdb'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'opentsdb',
|
||||
testDir: path.join(pluginDirRoot, '/opentsdb'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'jaeger',
|
||||
testDir: path.join(pluginDirRoot, '/jaeger'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'grafana-postgresql-datasource',
|
||||
testDir: path.join(pluginDirRoot, '/grafana-postgresql-datasource'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'canvas',
|
||||
testDir: path.join(testDirRoot, '/canvas'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'zipkin',
|
||||
testDir: path.join(pluginDirRoot, '/zipkin'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'unauthenticated',
|
||||
testDir: path.join(testDirRoot, '/unauthenticated'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
},
|
||||
{
|
||||
withAuth({
|
||||
name: 'various',
|
||||
testDir: path.join(testDirRoot, '/various-suite'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'panels',
|
||||
testDir: path.join(testDirRoot, '/panels-suite'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'smoke',
|
||||
testDir: path.join(testDirRoot, '/smoke-tests-suite'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'dashboards',
|
||||
testDir: path.join(testDirRoot, '/dashboards-suite'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'loki',
|
||||
testDir: path.join(testDirRoot, '/loki'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'cloud-plugins',
|
||||
testDir: path.join(testDirRoot, '/cloud-plugins-suite'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
{
|
||||
}),
|
||||
withAuth({
|
||||
name: 'dashboard-new-layouts',
|
||||
testDir: path.join(testDirRoot, '/dashboard-new-layouts'),
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
},
|
||||
dependencies: ['authenticate'],
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -18,6 +18,9 @@ queryService=true
|
||||
[environment]
|
||||
stack_id = 12345
|
||||
|
||||
[panels]
|
||||
enable_alpha = true
|
||||
|
||||
[plugins]
|
||||
allow_loading_unsigned_plugins=grafana-extensionstest-app,grafana-extensionexample1-app,grafana-extensionexample2-app,grafana-extensionexample3-app,grafana-e2etest-datasource
|
||||
|
||||
|
||||
@@ -5851,7 +5851,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@opentelemetry/api@npm:1.9.0, @opentelemetry/api@npm:^1.3.0, @opentelemetry/api@npm:^1.9.0":
|
||||
"@opentelemetry/api@npm:1.9.0, @opentelemetry/api@npm:^1.3.0, @opentelemetry/api@npm:^1.4.0, @opentelemetry/api@npm:^1.9.0":
|
||||
version: 1.9.0
|
||||
resolution: "@opentelemetry/api@npm:1.9.0"
|
||||
checksum: 10/a607f0eef971893c4f2ee2a4c2069aade6ec3e84e2a1f5c2aac19f65c5d9eeea41aa72db917c1029faafdd71789a1a040bdc18f40d63690e22ccae5d7070f194
|
||||
@@ -12201,6 +12201,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bintrees@npm:1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "bintrees@npm:1.0.2"
|
||||
checksum: 10/071896cea5ea5413316c8436e95799444c208630d5c539edd8a7089fc272fc5d3634aa4a2e4847b28350dda1796162e14a34a0eda53108cc5b3c2ff6a036c1fa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bl@npm:^4.0.3, bl@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "bl@npm:4.1.0"
|
||||
@@ -18542,6 +18549,7 @@ __metadata:
|
||||
postcss-scss: "npm:4.0.9"
|
||||
prettier: "npm:3.6.2"
|
||||
prismjs: "npm:1.30.0"
|
||||
prom-client: "npm:^15.1.3"
|
||||
publint: "npm:^0.3.12"
|
||||
rc-slider: "npm:11.1.8"
|
||||
rc-tree: "npm:5.13.1"
|
||||
@@ -26297,6 +26305,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prom-client@npm:^15.1.3":
|
||||
version: 15.1.3
|
||||
resolution: "prom-client@npm:15.1.3"
|
||||
dependencies:
|
||||
"@opentelemetry/api": "npm:^1.4.0"
|
||||
tdigest: "npm:^0.1.1"
|
||||
checksum: 10/eba75e15ab896845d39359e3a4d6f7913ea05339b3122d8dde8c8c374669ad1a1d1ab2694ab2101c420bd98086a564e4f2a18aa29018fc14a4732e57c1c19aec
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"promise-all-reject-late@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "promise-all-reject-late@npm:1.0.1"
|
||||
@@ -30724,6 +30742,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tdigest@npm:^0.1.1":
|
||||
version: 0.1.2
|
||||
resolution: "tdigest@npm:0.1.2"
|
||||
dependencies:
|
||||
bintrees: "npm:1.0.2"
|
||||
checksum: 10/45be99fa52dab74b8edafe150e473cdc45aa1352c75ed516a39905f350a08c3175f6555598111042c3677ba042d7e3cae6b5ce4c663fe609bc634f326aabc9d6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"teex@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "teex@npm:1.0.1"
|
||||
|
||||
Reference in New Issue
Block a user