mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugin extensions: Add e2e tests (#89048)
* add custom plugins * update bundles * provision app plugins and their dashboards * add one more script that run e2e tests using e2e test server * add e2e tests * regenerate jsonnet dashboards * ignore custom plugins and playwright report * use minified * cleanup tests * update codeowners * add leading slash * document new script * document custom-plugins * cleanup * twist modules * add readme
This commit is contained in:
parent
a9171aa9fe
commit
72241dbf5f
@ -13,6 +13,8 @@ node_modules
|
||||
/public/lib/monaco
|
||||
/scripts/grafana-server/tmp
|
||||
vendor
|
||||
e2e/custom-plugins
|
||||
playwright-report
|
||||
|
||||
# TS generate from cue by cuetsy
|
||||
**/*.gen.ts
|
||||
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -223,6 +223,7 @@
|
||||
/devenv/local-npm/ @grafana/frontend-ops
|
||||
/devenv/vscode/ @grafana/frontend-ops
|
||||
/devenv/setup.sh @grafana/grafana-backend-services-squad
|
||||
/devenv/plugins.yaml @grafana/plugins-platform-frontend
|
||||
|
||||
# Emails
|
||||
/emails/ @grafana/alerting-frontend
|
||||
|
@ -235,7 +235,7 @@ yarn e2e:dev
|
||||
|
||||
#### To run the Playwright tests:
|
||||
|
||||
**Note:** If you're using VS Code as your development editor, it's recommended to install the [Playwright test extension](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright). It allows you to run, debug and generate Playwright tests from within the editor. For more information about the extension and how to install it, refer to the [Playwright documentation](https://playwright.dev/docs/getting-started-vscode).
|
||||
**Note:** If you're using VS Code as your development editor, it's recommended to install the [Playwright test extension](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright). It allows you to run, debug and generate Playwright tests from within the editor. For more information about the extension and how to use reports to analyze failing tests, refer to the [Playwright documentation](https://playwright.dev/docs/getting-started-vscode).
|
||||
|
||||
Each version of Playwright needs specific versions of browser binaries to operate. You need to use the Playwright CLI to install these browsers.
|
||||
|
||||
@ -243,22 +243,16 @@ Each version of Playwright needs specific versions of browser binaries to operat
|
||||
yarn playwright install chromium
|
||||
```
|
||||
|
||||
To run all tests in a headless Chromium browser and display results in the terminal:
|
||||
To run all tests in a headless Chromium browser and display results in the terminal. This assumes you have Grafana running on port 3000.
|
||||
|
||||
```
|
||||
yarn e2e:playwright
|
||||
```
|
||||
|
||||
For a better developer experience, open the Playwright UI where you can visually walk through each step of the test and see what was happening before, during, and after each step.
|
||||
The following script starts a Grafana [development server](https://github.com/grafana/grafana/blob/main/scripts/grafana-server/start-server) (same server that is being used when running e2e tests in Drone CI) on port 3001 and runs the Playwright tests. The development server is provisioned with the [devenv](https://github.com/grafana/grafana/blob/main/contribute/developer-guide.md#add-data-sources) dashboards, data sources and apps.
|
||||
|
||||
```
|
||||
yarn e2e:playwright:ui
|
||||
```
|
||||
|
||||
To open the HTML reporter for the last test run session:
|
||||
|
||||
```
|
||||
yarn e2e:playwright:report
|
||||
yarn e2e:playwright:server
|
||||
```
|
||||
|
||||
## Configure Grafana for development
|
||||
|
@ -33,3 +33,5 @@ Playwright end-to-end tests for plugins should be added to the [`e2e/plugin-e2e`
|
||||
The script above assumes you have Grafana running on `localhost:3000`. You may change this by providing environment variables.
|
||||
|
||||
`HOST=127.0.0.1 PORT=3001 yarn e2e:playwright`
|
||||
|
||||
- `yarn e2e:playwright:server` will start a Grafana [development server](https://github.com/grafana/grafana/blob/main/scripts/grafana-server/start-server) on port 3001 and run the Playwright tests. The development server is provisioned with the [devenv](https://github.com/grafana/grafana/blob/main/contribute/developer-guide.md#add-data-sources) dashboards, data sources and apps.
|
||||
|
342
devenv/dev-dashboards/extensions/link-onclick-extensions.json
Normal file
342
devenv/dev-dashboards/extensions/link-onclick-extensions.json
Normal file
@ -0,0 +1,342 @@
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"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": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"title": "Link with one query",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"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": 6,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"cellHeight": "sm",
|
||||
"footer": {
|
||||
"countRows": false,
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true,
|
||||
"showRowNums": false
|
||||
},
|
||||
"pluginVersion": "10.1.0-55406pre",
|
||||
"title": "No extensions",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
}
|
||||
},
|
||||
"mappings": []
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 6,
|
||||
"x": 6,
|
||||
"y": 8
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"pieType": "pie",
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"seriesCount": 4
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"hide": false,
|
||||
"refId": "B",
|
||||
"scenarioId": "random_walk",
|
||||
"seriesCount": 1
|
||||
}
|
||||
],
|
||||
"title": "Link with new name",
|
||||
"type": "piechart"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"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": 8,
|
||||
"w": 6,
|
||||
"x": 0,
|
||||
"y": 16
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"seriesCount": 1
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"hide": false,
|
||||
"refId": "B",
|
||||
"scenarioId": "random_walk",
|
||||
"seriesCount": 1
|
||||
}
|
||||
],
|
||||
"title": "Link with defaults",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Link Extensions (onClick)",
|
||||
"uid": "dbfb47c5-e5e5-4d28-8ac7-35f349b95946",
|
||||
"version": 1,
|
||||
"weekStart": ""
|
||||
}
|
237
devenv/dev-dashboards/extensions/link-path-extensions.json
Normal file
237
devenv/dev-dashboards/extensions/link-path-extensions.json
Normal file
@ -0,0 +1,237 @@
|
||||
{
|
||||
"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": 1,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"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": 6,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"cellHeight": "sm",
|
||||
"footer": {
|
||||
"countRows": false,
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true,
|
||||
"showRowNums": false
|
||||
},
|
||||
"pluginVersion": "9.5.0-53420pre",
|
||||
"title": "No extensions",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
}
|
||||
},
|
||||
"mappings": []
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 6,
|
||||
"x": 6,
|
||||
"y": 0
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"pieType": "pie",
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"seriesCount": 4
|
||||
}
|
||||
],
|
||||
"title": "Link with new name",
|
||||
"type": "piechart"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"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": 8,
|
||||
"w": 6,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"title": "Link with defaults",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Link Extensions (path)",
|
||||
"uid": "d1fbb077-cd44-4738-8c8a-d4e66748b719",
|
||||
"version": 3,
|
||||
"weekStart": ""
|
||||
}
|
@ -59,6 +59,8 @@
|
||||
"join-by-field": (import '../dev-dashboards/transforms/join-by-field.json'),
|
||||
"join-by-labels": (import '../dev-dashboards/transforms/join-by-labels.json'),
|
||||
"lazy_loading": (import '../dev-dashboards/panel-common/lazy_loading.json'),
|
||||
"link-onclick-extensions": (import '../dev-dashboards/extensions/link-onclick-extensions.json'),
|
||||
"link-path-extensions": (import '../dev-dashboards/extensions/link-path-extensions.json'),
|
||||
"linked-viz": (import '../dev-dashboards/panel-common/linked-viz.json'),
|
||||
"live-flakey": (import '../dev-dashboards/live/live-flakey.json'),
|
||||
"live-flakey-refresh": (import '../dev-dashboards/live/live-flakey-refresh.json'),
|
||||
|
19
devenv/plugins.yaml
Normal file
19
devenv/plugins.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
apiVersion: 1
|
||||
|
||||
apps:
|
||||
- type: myorg-extensions-app
|
||||
org_id: 1
|
||||
org_name: Main Org.
|
||||
disabled: false
|
||||
- type: myorg-a-app
|
||||
org_id: 1
|
||||
org_name: Main Org.
|
||||
disabled: false
|
||||
- type: myorg-b-app
|
||||
org_id: 1
|
||||
org_name: Main Org.
|
||||
disabled: false
|
||||
- type: myorg-extensionpoint-app
|
||||
org_id: 1
|
||||
org_name: Main Org.
|
||||
disabled: false
|
5
e2e/custom-plugins/README.md
Normal file
5
e2e/custom-plugins/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Custom plugins
|
||||
|
||||
Plugins in this directory will be installed when the e2e [test server](https://github.com/grafana/grafana/blob/main/scripts/grafana-server/start-server) is started. Optionally, you can provision the plugin by adding configuration to the [datasources.yaml](https://github.com/grafana/grafana/blob/extensions/add-e2e-tests/devenv/datasources.yaml) or to the [plugins.yaml](https://github.com/grafana/grafana/blob/extensions/add-e2e-tests/devenv/plugins.yaml).
|
||||
|
||||
These plugins are not being built as part of CI. Plugins in this directory are being version controlled, so make sure the bundle size is small. Only use dependencies provided by the runtime (see list of runtime dependencies [here](https://github.com/grafana/plugin-tools/blob/08b67179bdbf8847788c54aadb22654aa1a7c060/packages/create-plugin/templates/common/.config/webpack/webpack.config.ts#L36)).
|
12
e2e/custom-plugins/app-with-extension-point/README.md
Normal file
12
e2e/custom-plugins/app-with-extension-point/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# App with extension point
|
||||
|
||||
This app was initially copied from the [app-with-extension-point](https://github.com/grafana/grafana-plugin-examples/tree/main/examples/app-with-extension-point) example plugin. The plugin bundle is using AMD, but it's not minified and the plugin feature set is small so it should be possible to make changes in this file if necessary.
|
||||
|
||||
To test this app:
|
||||
|
||||
```sh
|
||||
# start e2e test instance (it will install this plugin)
|
||||
PORT=3000 ./scripts/grafana-server/start-server
|
||||
# run Playwright tests using Playwright VSCode extension or with the following script
|
||||
yarn e2e:playwright
|
||||
```
|
141
e2e/custom-plugins/app-with-extension-point/module.js
Normal file
141
e2e/custom-plugins/app-with-extension-point/module.js
Normal file
@ -0,0 +1,141 @@
|
||||
define(['@grafana/data', 'react', '@grafana/ui', '@grafana/runtime'], function (data, React, UI, runtime) {
|
||||
'use strict';
|
||||
|
||||
const styles = {
|
||||
container: 'main-app-body',
|
||||
actions: { button: 'action-button' },
|
||||
modal: { container: 'container', open: 'open-link' },
|
||||
appA: { container: 'a-app-body' },
|
||||
appB: { modal: 'b-app-modal' },
|
||||
};
|
||||
|
||||
function ModalComponent({ onDismiss, title, path }) {
|
||||
return React.createElement(
|
||||
UI.Modal,
|
||||
{ 'data-testid': styles.modal.container, title, isOpen: true, onDismiss },
|
||||
React.createElement(
|
||||
UI.VerticalGroup,
|
||||
{ spacing: 'sm' },
|
||||
React.createElement('p', null, 'Do you want to proceed in the current tab or open a new tab?')
|
||||
),
|
||||
React.createElement(
|
||||
UI.Modal.ButtonRow,
|
||||
null,
|
||||
React.createElement(UI.Button, { onClick: onDismiss, fill: 'outline', variant: 'secondary' }, 'Cancel'),
|
||||
React.createElement(
|
||||
UI.Button,
|
||||
{
|
||||
type: 'submit',
|
||||
variant: 'secondary',
|
||||
onClick: function () {
|
||||
window.open(data.locationUtil.assureBaseUrl(path), '_blank');
|
||||
onDismiss();
|
||||
},
|
||||
icon: 'external-link-alt',
|
||||
},
|
||||
'Open in new tab'
|
||||
),
|
||||
React.createElement(
|
||||
UI.Button,
|
||||
{
|
||||
'data-testid': styles.modal.open,
|
||||
type: 'submit',
|
||||
variant: 'primary',
|
||||
onClick: function () {
|
||||
runtime.locationService.push(path);
|
||||
},
|
||||
icon: 'apps',
|
||||
},
|
||||
'Open'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function ActionComponent({ extensions }) {
|
||||
const options = React.useMemo(
|
||||
function () {
|
||||
return extensions.reduce(function (acc, extension) {
|
||||
if (runtime.isPluginExtensionLink(extension)) {
|
||||
acc.push({ label: extension.title, title: extension.title, value: extension });
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
},
|
||||
[extensions]
|
||||
);
|
||||
|
||||
const [selected, setSelected] = React.useState();
|
||||
|
||||
return options.length === 0
|
||||
? React.createElement(UI.Button, null, 'Run default action')
|
||||
: React.createElement(
|
||||
React.Fragment,
|
||||
null,
|
||||
React.createElement(
|
||||
UI.ButtonGroup,
|
||||
null,
|
||||
React.createElement(
|
||||
UI.ToolbarButton,
|
||||
{
|
||||
key: 'default-action',
|
||||
variant: 'canvas',
|
||||
onClick: function () {
|
||||
alert('You triggered the default action');
|
||||
},
|
||||
},
|
||||
'Run default action'
|
||||
),
|
||||
React.createElement(UI.ButtonSelect, {
|
||||
'data-testid': styles.actions.button,
|
||||
key: 'select-extension',
|
||||
variant: 'canvas',
|
||||
options: options,
|
||||
onChange: function (e) {
|
||||
const extension = e.value;
|
||||
if (runtime.isPluginExtensionLink(extension)) {
|
||||
if (extension.path) setSelected(extension);
|
||||
if (extension.onClick) extension.onClick();
|
||||
}
|
||||
},
|
||||
})
|
||||
),
|
||||
selected &&
|
||||
selected.path &&
|
||||
React.createElement(ModalComponent, {
|
||||
title: selected.title,
|
||||
path: selected.path,
|
||||
onDismiss: function () {
|
||||
setSelected(undefined);
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
class RootComponent extends React.PureComponent {
|
||||
render() {
|
||||
const { extensions } = runtime.getPluginExtensions({
|
||||
extensionPointId: 'plugins/myorg-extensionpoint-app/actions',
|
||||
context: {},
|
||||
});
|
||||
|
||||
return React.createElement(
|
||||
'div',
|
||||
{ 'data-testid': styles.container, style: { marginTop: '5%' } },
|
||||
React.createElement(
|
||||
UI.HorizontalGroup,
|
||||
{ align: 'flex-start', justify: 'center' },
|
||||
React.createElement(
|
||||
UI.HorizontalGroup,
|
||||
null,
|
||||
React.createElement('span', null, 'Hello Grafana! These are the actions you can trigger from this plugin'),
|
||||
React.createElement(ActionComponent, { extensions: extensions })
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const plugin = new data.AppPlugin().setRootPage(RootComponent);
|
||||
return { plugin: plugin };
|
||||
});
|
38
e2e/custom-plugins/app-with-extension-point/plugin.json
Normal file
38
e2e/custom-plugins/app-with-extension-point/plugin.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/grafana/grafana/main/docs/sources/developers/plugins/plugin.schema.json",
|
||||
"type": "app",
|
||||
"name": "Extension Point App",
|
||||
"id": "myorg-extensionpoint-app",
|
||||
"preload": true,
|
||||
"info": {
|
||||
"keywords": ["app"],
|
||||
"description": "Show case how to add an extension point to your plugin",
|
||||
"author": {
|
||||
"name": "Myorg"
|
||||
},
|
||||
"logos": {
|
||||
"small": "img/logo.svg",
|
||||
"large": "img/logo.svg"
|
||||
},
|
||||
"screenshots": [],
|
||||
"version": "1.0.0",
|
||||
"updated": "2024-06-11"
|
||||
},
|
||||
"includes": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "Default",
|
||||
"path": "/a/myorg-extensionpoint-app",
|
||||
"role": "Admin",
|
||||
"addToNav": true,
|
||||
"defaultNav": true
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"grafanaDependency": ">=10.3.3",
|
||||
"plugins": []
|
||||
},
|
||||
"generated": {
|
||||
"extensions": []
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
define(['@grafana/data', 'react'], function (data, React) {
|
||||
'use strict';
|
||||
|
||||
const styles = {
|
||||
container: 'a-app-body',
|
||||
};
|
||||
|
||||
class RootComponent extends React.PureComponent {
|
||||
render() {
|
||||
return React.createElement(
|
||||
'div',
|
||||
{ 'data-testid': styles.container, className: 'page-container' },
|
||||
'Hello Grafana!'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const plugin = new data.AppPlugin().setRootPage(RootComponent).configureExtensionLink({
|
||||
title: 'Go to A',
|
||||
description: 'Navigating to plugin A',
|
||||
extensionPointId: 'plugins/myorg-extensionpoint-app/actions',
|
||||
path: '/a/myorg-a-app/',
|
||||
});
|
||||
|
||||
return { plugin: plugin };
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/grafana/grafana/main/docs/sources/developers/plugins/plugin.schema.json",
|
||||
"type": "app",
|
||||
"name": "A App",
|
||||
"id": "myorg-a-app",
|
||||
"preload": true,
|
||||
"info": {
|
||||
"keywords": ["app"],
|
||||
"description": "Will extend root app with ui extensions",
|
||||
"author": {
|
||||
"name": "Myorg"
|
||||
},
|
||||
"logos": {
|
||||
"small": "img/logo.svg",
|
||||
"large": "img/logo.svg"
|
||||
},
|
||||
"screenshots": [],
|
||||
"version": "%VERSION%",
|
||||
"updated": "%TODAY%"
|
||||
},
|
||||
"includes": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "Default",
|
||||
"path": "/a/myorg-a-app",
|
||||
"role": "Admin",
|
||||
"addToNav": false,
|
||||
"defaultNav": false
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"grafanaDependency": ">=10.3.3",
|
||||
"plugins": []
|
||||
},
|
||||
"generated": {
|
||||
"extensions": [
|
||||
{
|
||||
"extensionPointId": "plugins/myorg-extensionpoint-app/actions",
|
||||
"title": "Go to A",
|
||||
"description": "Navigating to pluging A",
|
||||
"type": "link"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
define(['react', '@grafana/data'], function (React, data) {
|
||||
'use strict';
|
||||
|
||||
class RootComponent extends React.PureComponent {
|
||||
render() {
|
||||
return React.createElement('div', { className: 'page-container' }, 'Hello Grafana!');
|
||||
}
|
||||
}
|
||||
|
||||
const modalId = 'b-app-modal';
|
||||
|
||||
const plugin = new data.AppPlugin().setRootPage(RootComponent).configureExtensionLink({
|
||||
title: 'Open from B',
|
||||
description: 'Open a modal from plugin B',
|
||||
extensionPointId: 'plugins/myorg-extensionpoint-app/actions',
|
||||
onClick: function (e, { openModal }) {
|
||||
openModal({
|
||||
title: 'Modal from app B',
|
||||
body: function () {
|
||||
return React.createElement('div', { 'data-testid': modalId }, 'From plugin B');
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return { plugin: plugin };
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/grafana/grafana/main/docs/sources/developers/plugins/plugin.schema.json",
|
||||
"type": "app",
|
||||
"name": "B App",
|
||||
"id": "myorg-b-app",
|
||||
"preload": true,
|
||||
"info": {
|
||||
"keywords": ["app"],
|
||||
"description": "Will extend root app with ui extensions",
|
||||
"author": {
|
||||
"name": "Myorg"
|
||||
},
|
||||
"logos": {
|
||||
"small": "img/logo.svg",
|
||||
"large": "img/logo.svg"
|
||||
},
|
||||
"screenshots": [],
|
||||
"version": "%VERSION%",
|
||||
"updated": "%TODAY%"
|
||||
},
|
||||
"includes": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "Default",
|
||||
"path": "/a/myorg-b-app",
|
||||
"role": "Admin",
|
||||
"addToNav": false,
|
||||
"defaultNav": false
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"grafanaDependency": ">=10.3.3",
|
||||
"plugins": []
|
||||
},
|
||||
"generated": {
|
||||
"extensions": [
|
||||
{
|
||||
"extensionPointId": "plugins/myorg-extensionpoint-app/actions",
|
||||
"title": "Open from B",
|
||||
"description": "Open a modal from plugin B",
|
||||
"type": "link"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
12
e2e/custom-plugins/app-with-extensions/README.md
Normal file
12
e2e/custom-plugins/app-with-extensions/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# App with extensions
|
||||
|
||||
This app was initially copied from the [app-with-extensions](https://github.com/grafana/grafana-plugin-examples/tree/main/examples/app-with-extensions) example plugin. The plugin bundle is using AMD, but it's not minified and the plugin feature set is small so it should be possible to make changes in this file if necessary.
|
||||
|
||||
To test this app:
|
||||
|
||||
```sh
|
||||
# start e2e test instance (it will install this plugin)
|
||||
PORT=3000 ./scripts/grafana-server/start-server
|
||||
# run Playwright tests using Playwright VSCode extension or with the following script
|
||||
yarn e2e:playwright
|
||||
```
|
216
e2e/custom-plugins/app-with-extensions/module.js
Normal file
216
e2e/custom-plugins/app-with-extensions/module.js
Normal file
@ -0,0 +1,216 @@
|
||||
define(['react', '@grafana/data', '@grafana/ui', '@grafana/runtime', '@emotion/css', 'rxjs'], function (
|
||||
React,
|
||||
data,
|
||||
ui,
|
||||
runtime,
|
||||
css,
|
||||
rxjs
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
const styles = {
|
||||
modalBody: 'ape-modal-body',
|
||||
mainPageContainer: 'ape-main-page-container',
|
||||
};
|
||||
|
||||
class RootComponent extends React.PureComponent {
|
||||
render() {
|
||||
return React.createElement(
|
||||
'div',
|
||||
{ 'data-testid': styles.mainPageContainer, className: 'page-container' },
|
||||
'Hello Grafana!'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const asyncWrapper = (fn) => {
|
||||
return function () {
|
||||
const gen = fn.apply(this, arguments);
|
||||
return new Promise((resolve, reject) => {
|
||||
function step(key, arg) {
|
||||
let info, value;
|
||||
try {
|
||||
info = gen[key](arg);
|
||||
value = info.value;
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
if (info.done) {
|
||||
resolve(value);
|
||||
} else {
|
||||
Promise.resolve(value).then(next, throw_);
|
||||
}
|
||||
}
|
||||
function next(value) {
|
||||
step('next', value);
|
||||
}
|
||||
function throw_(value) {
|
||||
step('throw', value);
|
||||
}
|
||||
next();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const getStyles = (theme) => ({
|
||||
colorWeak: css.css`color: ${theme.colors.text.secondary};`,
|
||||
marginTop: css.css`margin-top: ${theme.spacing(3)};`,
|
||||
});
|
||||
|
||||
const updatePlugin = asyncWrapper(function* (pluginId, settings) {
|
||||
const response = runtime
|
||||
.getBackendSrv()
|
||||
.fetch({ url: `/api/plugins/${pluginId}/settings`, method: 'POST', data: settings });
|
||||
return rxjs.lastValueFrom(response);
|
||||
});
|
||||
|
||||
const handleUpdate = asyncWrapper(function* (pluginId, settings) {
|
||||
try {
|
||||
yield updatePlugin(pluginId, settings);
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
console.error('Error while updating the plugin', error);
|
||||
}
|
||||
});
|
||||
|
||||
const configPageBody = ({ plugin }) => {
|
||||
const styles = getStyles(ui.useStyles2());
|
||||
const { enabled, jsonData } = plugin.meta;
|
||||
return React.createElement(
|
||||
'div',
|
||||
null,
|
||||
React.createElement(ui.Legend, null, 'Enable / Disable '),
|
||||
!enabled &&
|
||||
React.createElement(
|
||||
React.Fragment,
|
||||
null,
|
||||
React.createElement('div', { className: styles.colorWeak }, 'The plugin is currently not enabled.'),
|
||||
React.createElement(
|
||||
ui.Button,
|
||||
{
|
||||
className: styles.marginTop,
|
||||
variant: 'primary',
|
||||
onClick: () => handleUpdate(plugin.meta.id, { enabled: true, pinned: true, jsonData: jsonData }),
|
||||
},
|
||||
'Enable plugin'
|
||||
)
|
||||
),
|
||||
enabled &&
|
||||
React.createElement(
|
||||
React.Fragment,
|
||||
null,
|
||||
React.createElement('div', { className: styles.colorWeak }, 'The plugin is currently enabled.'),
|
||||
React.createElement(
|
||||
ui.Button,
|
||||
{
|
||||
className: styles.marginTop,
|
||||
variant: 'destructive',
|
||||
onClick: () => handleUpdate(plugin.meta.id, { enabled: false, pinned: false, jsonData: jsonData }),
|
||||
},
|
||||
'Disable plugin'
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const selectQueryModal = ({ targets = [], onDismiss }) => {
|
||||
const [selectedQuery, setSelectedQuery] = React.useState(targets[0]);
|
||||
return React.createElement(
|
||||
'div',
|
||||
{ 'data-testid': styles.modalBody },
|
||||
React.createElement(
|
||||
'p',
|
||||
null,
|
||||
'Please select the query you would like to use to create "something" in the plugin.'
|
||||
),
|
||||
React.createElement(
|
||||
ui.HorizontalGroup,
|
||||
null,
|
||||
targets.map((query) =>
|
||||
React.createElement(ui.FilterPill, {
|
||||
key: query.refId,
|
||||
label: query.refId,
|
||||
selected: query.refId === (selectedQuery ? selectedQuery.refId : null),
|
||||
onClick: () => setSelectedQuery(query),
|
||||
})
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
ui.Modal.ButtonRow,
|
||||
null,
|
||||
React.createElement(ui.Button, { variant: 'secondary', fill: 'outline', onClick: onDismiss }, 'Cancel'),
|
||||
React.createElement(
|
||||
ui.Button,
|
||||
{
|
||||
disabled: !Boolean(selectedQuery),
|
||||
onClick: () => {
|
||||
onDismiss && onDismiss();
|
||||
alert(`You selected query "${selectedQuery.refId}"`);
|
||||
},
|
||||
},
|
||||
'OK'
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const plugin = new data.AppPlugin()
|
||||
.setRootPage(RootComponent)
|
||||
.addConfigPage({
|
||||
title: 'Configuration',
|
||||
icon: 'cog',
|
||||
body: configPageBody,
|
||||
id: 'configuration',
|
||||
})
|
||||
.configureExtensionLink({
|
||||
title: 'Open from time series or pie charts (path)',
|
||||
description: 'This link will only be visible on time series and pie charts',
|
||||
extensionPointId: data.PluginExtensionPoints.DashboardPanelMenu,
|
||||
path: `/a/myorg-extensions-app/`,
|
||||
configure: (context) => {
|
||||
if (context.dashboard?.title === 'Link Extensions (path)') {
|
||||
switch (context.pluginId) {
|
||||
case 'timeseries':
|
||||
return {};
|
||||
case 'piechart':
|
||||
return { title: `Open from ${context.pluginId}` };
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
.configureExtensionLink({
|
||||
title: 'Open from time series or pie charts (onClick)',
|
||||
description: 'This link will only be visible on time series and pie charts',
|
||||
extensionPointId: data.PluginExtensionPoints.DashboardPanelMenu,
|
||||
onClick: (_, { context, openModal }) => {
|
||||
const targets = context?.targets || [];
|
||||
const title = context?.title;
|
||||
if (!targets.length) return;
|
||||
if (targets.length > 1) {
|
||||
openModal({
|
||||
title: `Select query from "${title}"`,
|
||||
body: (props) => React.createElement(selectQueryModal, { ...props, targets: targets }),
|
||||
});
|
||||
} else {
|
||||
alert(`You selected query "${targets[0].refId}"`);
|
||||
}
|
||||
},
|
||||
configure: (context) => {
|
||||
if (context.dashboard?.title === 'Link Extensions (onClick)') {
|
||||
switch (context.pluginId) {
|
||||
case 'timeseries':
|
||||
return {};
|
||||
case 'piechart':
|
||||
return { title: `Open from ${context.pluginId}` };
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return { plugin: plugin };
|
||||
});
|
49
e2e/custom-plugins/app-with-extensions/plugin.json
Normal file
49
e2e/custom-plugins/app-with-extensions/plugin.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/grafana/grafana/main/docs/sources/developers/plugins/plugin.schema.json",
|
||||
"type": "app",
|
||||
"name": "Extensions App",
|
||||
"id": "myorg-extensions-app",
|
||||
"preload": true,
|
||||
"info": {
|
||||
"keywords": ["app"],
|
||||
"description": "Example on how to extend grafana ui from a plugin",
|
||||
"author": {
|
||||
"name": "Myorg"
|
||||
},
|
||||
"logos": {
|
||||
"small": "img/logo.svg",
|
||||
"large": "img/logo.svg"
|
||||
},
|
||||
"screenshots": [],
|
||||
"version": "1.0.0",
|
||||
"updated": "2024-06-11"
|
||||
},
|
||||
"includes": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "Default",
|
||||
"path": "/a/myorg-extensions-app",
|
||||
"role": "Admin",
|
||||
"addToNav": true,
|
||||
"defaultNav": true
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"grafanaDependency": ">=10.3.3",
|
||||
"plugins": []
|
||||
},
|
||||
"extensions": [
|
||||
{
|
||||
"extensionPointId": "grafana/dashboard/panel/menu",
|
||||
"type": "link",
|
||||
"title": "Open from time series or pie charts (path)",
|
||||
"description": "This link will only be visible on time series and pie charts"
|
||||
},
|
||||
{
|
||||
"extensionPointId": "grafana/dashboard/panel/menu",
|
||||
"type": "link",
|
||||
"title": "Open from time series or pie charts (onClick)",
|
||||
"description": "This link will only be visible on time series and pie charts"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const testIds = {
|
||||
container: 'main-app-body',
|
||||
actions: {
|
||||
button: 'action-button',
|
||||
},
|
||||
modal: {
|
||||
container: 'container',
|
||||
open: 'open-link',
|
||||
},
|
||||
appA: {
|
||||
container: 'a-app-body',
|
||||
},
|
||||
appB: {
|
||||
modal: 'b-app-modal',
|
||||
},
|
||||
};
|
||||
|
||||
const pluginId = 'myorg-extensionpoint-app';
|
||||
|
||||
test('should extend the actions menu with a link to a-app plugin', async ({ page }) => {
|
||||
await page.goto(`/a/${pluginId}/one`);
|
||||
await page.getByTestId(testIds.actions.button).click();
|
||||
await page.getByTestId(testIds.container).getByText('Go to A').click();
|
||||
await page.getByTestId(testIds.modal.open).click();
|
||||
await expect(page.getByTestId(testIds.appA.container)).toBeVisible();
|
||||
});
|
||||
|
||||
test('should extend the actions menu with a command triggered from b-app plugin', async ({ page }) => {
|
||||
await page.goto(`/a/${pluginId}/one`);
|
||||
await page.getByTestId(testIds.actions.button).click();
|
||||
await page.getByTestId(testIds.container).getByText('Open from B').click();
|
||||
await expect(page.getByTestId(testIds.appB.modal)).toBeVisible();
|
||||
});
|
@ -0,0 +1,38 @@
|
||||
import { expect, test } from '@grafana/plugin-e2e';
|
||||
|
||||
const panelTitle = 'Link with defaults';
|
||||
const extensionTitle = 'Open from time series...';
|
||||
const testIds = {
|
||||
modal: {
|
||||
container: 'ape-modal-body',
|
||||
},
|
||||
mainPage: {
|
||||
container: 'ape-main-page-container',
|
||||
},
|
||||
};
|
||||
|
||||
const linkOnClickDashboardUid = 'dbfb47c5-e5e5-4d28-8ac7-35f349b95946';
|
||||
const linkPathDashboardUid = 'd1fbb077-cd44-4738-8c8a-d4e66748b719';
|
||||
|
||||
test('should add link extension (path) with defaults to time series panel', async ({ gotoDashboardPage, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: linkPathDashboardUid });
|
||||
const panel = await dashboardPage.getPanelByTitle(panelTitle);
|
||||
await panel.clickOnMenuItem(extensionTitle, { parentItem: 'Extensions' });
|
||||
await expect(page.getByTestId(testIds.mainPage.container)).toBeVisible();
|
||||
});
|
||||
|
||||
test('should add link extension (onclick) with defaults to time series panel', async ({ gotoDashboardPage, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: linkOnClickDashboardUid });
|
||||
const panel = await dashboardPage.getPanelByTitle(panelTitle);
|
||||
await panel.clickOnMenuItem(extensionTitle, { parentItem: 'Extensions' });
|
||||
await expect(page.getByRole('dialog')).toContainText('Select query from "Link with defaults"');
|
||||
});
|
||||
|
||||
test('should add link extension (onclick) with new title to pie chart panel', async ({ gotoDashboardPage, page }) => {
|
||||
const panelTitle = 'Link with new name';
|
||||
const extensionTitle = 'Open from piechart';
|
||||
const dashboardPage = await gotoDashboardPage({ uid: linkOnClickDashboardUid });
|
||||
const panel = await dashboardPage.getPanelByTitle(panelTitle);
|
||||
await panel.clickOnMenuItem(extensionTitle, { parentItem: 'Extensions' });
|
||||
await expect(page.getByRole('dialog')).toContainText('Select query from "Link with new name"');
|
||||
});
|
23
e2e/plugin-e2e/start-and-run-suite
Executable file
23
e2e/plugin-e2e/start-and-run-suite
Executable file
@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
. scripts/grafana-server/variables
|
||||
|
||||
LICENSE_PATH=""
|
||||
|
||||
if [ "$1" = "enterprise" ]; then
|
||||
if [ "$2" != "dev" ] && [ "$2" != "debug" ]; then
|
||||
LICENSE_PATH=$2/license.jwt
|
||||
else
|
||||
LICENSE_PATH=$3/license.jwt
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$BASE_URL" != "" ]; then
|
||||
echo -e "BASE_URL set, skipping starting server"
|
||||
else
|
||||
# Start it in the background
|
||||
./scripts/grafana-server/start-server $LICENSE_PATH 2>&1 > scripts/grafana-server/server.log &
|
||||
./scripts/grafana-server/wait-for-grafana
|
||||
fi
|
||||
|
||||
PORT=3001 HOST=localhost yarn playwright test
|
@ -18,8 +18,7 @@
|
||||
"e2e:enterprise:dev": "./e2e/start-and-run-suite enterprise dev",
|
||||
"e2e:enterprise:debug": "./e2e/start-and-run-suite enterprise debug",
|
||||
"e2e:playwright": "yarn playwright test",
|
||||
"e2e:playwright:ui": "yarn playwright test --ui",
|
||||
"e2e:playwright:report": "yarn playwright show-report",
|
||||
"e2e:playwright:server": "./e2e/plugin-e2e/start-and-run-suite",
|
||||
"test": "jest --notify --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
"test:coverage:changes": "jest --coverage --changedSince=origin/main",
|
||||
|
@ -31,6 +31,7 @@ mkdir $PROV_DIR
|
||||
mkdir $PROV_DIR/datasources
|
||||
mkdir $PROV_DIR/dashboards
|
||||
mkdir $PROV_DIR/alerting
|
||||
mkdir $PROV_DIR/plugins
|
||||
|
||||
cp ./scripts/grafana-server/custom.ini $RUNDIR/conf/custom.ini
|
||||
cp ./conf/defaults.ini $RUNDIR/conf/defaults.ini
|
||||
@ -51,6 +52,7 @@ echo -e "Copy provisioning setup from devenv"
|
||||
cp devenv/datasources.yaml $PROV_DIR/datasources
|
||||
cp devenv/dashboards.yaml $PROV_DIR/dashboards
|
||||
cp devenv/alert_rules.yaml $PROV_DIR/alerting
|
||||
cp devenv/plugins.yaml $PROV_DIR/plugins
|
||||
|
||||
cp -r devenv $RUNDIR
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user