mirror of
https://github.com/grafana/grafana.git
synced 2025-09-11 20:22:53 +08:00
Playwright: Fix remaining various-suite tests (#110667)
* get trace-view-scrolling working in playwright * almost fix frontend-sandbox-datasource test * properly fix frontend-sandbox-datasource tests * convert migrate-to-cloud to playwright * prometheus-variable-editor tests in playwright * enable prometheus-config tests in playwright * run on frontend changes * skip test * remove various-suite
This commit is contained in:
2
.github/workflows/pr-e2e-tests.yml
vendored
2
.github/workflows/pr-e2e-tests.yml
vendored
@ -208,8 +208,6 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- suite: various-suite
|
||||
path: e2e/various-suite
|
||||
- suite: various-suite (old arch)
|
||||
path: e2e/old-arch/various-suite
|
||||
flags: --flags="--env dashboardScene=false"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,28 +1,15 @@
|
||||
import { random } from 'lodash';
|
||||
|
||||
import { test, expect, DataSourceConfigPage } from '@grafana/plugin-e2e';
|
||||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const DATASOURCE_ID = 'sandbox-test-datasource';
|
||||
let DATASOURCE_CONNECTION_ID = '';
|
||||
const DATASOURCE_TYPED_NAME = 'SandboxDatasourceInstance';
|
||||
|
||||
// Originally skipped due to flakiness/race conditions with same old arch test e2e/various-suite/frontend-sandbox-datasource.spec.ts
|
||||
// TODO: fix and remove skip
|
||||
test.describe.skip(
|
||||
test.describe(
|
||||
'Datasource sandbox',
|
||||
{
|
||||
tag: ['@various', '@wip'],
|
||||
tag: ['@various'],
|
||||
},
|
||||
() => {
|
||||
let configPage: DataSourceConfigPage;
|
||||
test.beforeEach(async ({ createDataSourceConfigPage }) => {
|
||||
// Add the datasource
|
||||
configPage = await createDataSourceConfigPage({
|
||||
type: 'sandbox',
|
||||
name: DATASOURCE_TYPED_NAME,
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Config Editor', () => {
|
||||
test.describe('Sandbox disabled', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
@ -31,9 +18,19 @@ test.describe.skip(
|
||||
});
|
||||
});
|
||||
|
||||
test('Should not render a sandbox wrapper around the datasource config editor', async ({ page }) => {
|
||||
test('Should not render a sandbox wrapper around the datasource config editor', async ({
|
||||
page,
|
||||
createDataSource,
|
||||
}) => {
|
||||
const TIMESTAMP = Date.now();
|
||||
const DATASOURCE_TYPED_NAME = `SandboxDatasourceInstance-${TIMESTAMP}`;
|
||||
// Add the datasource
|
||||
const response = await createDataSource({
|
||||
type: DATASOURCE_ID,
|
||||
name: DATASOURCE_TYPED_NAME,
|
||||
});
|
||||
const DATASOURCE_CONNECTION_ID = response.uid;
|
||||
await page.goto(`/connections/datasources/edit/${DATASOURCE_CONNECTION_ID}`);
|
||||
await page.waitForTimeout(300); // wait to prevent false positives because playwright checks too fast
|
||||
|
||||
const sandboxDiv = page.locator(`div[data-plugin-sandbox="${DATASOURCE_ID}"]`);
|
||||
await expect(sandboxDiv).toBeHidden();
|
||||
@ -47,12 +44,33 @@ test.describe.skip(
|
||||
});
|
||||
});
|
||||
|
||||
test('Should render a sandbox wrapper around the datasource config editor', async ({ page, selectors }) => {
|
||||
test('Should render a sandbox wrapper around the datasource config editor', async ({
|
||||
page,
|
||||
createDataSource,
|
||||
}) => {
|
||||
const TIMESTAMP = Date.now();
|
||||
const DATASOURCE_TYPED_NAME = `SandboxDatasourceInstance-${TIMESTAMP}`;
|
||||
// Add the datasource
|
||||
const response = await createDataSource({
|
||||
type: DATASOURCE_ID,
|
||||
name: DATASOURCE_TYPED_NAME,
|
||||
});
|
||||
const DATASOURCE_CONNECTION_ID = response.uid;
|
||||
await page.goto(`/connections/datasources/edit/${DATASOURCE_CONNECTION_ID}`);
|
||||
|
||||
const sandboxDiv = page.locator(`div[data-plugin-sandbox="${DATASOURCE_ID}"]`);
|
||||
await expect(sandboxDiv).toBeVisible();
|
||||
});
|
||||
|
||||
test('Should store values in jsonData and secureJsonData correctly', async ({ page }) => {
|
||||
test('Should store values in jsonData and secureJsonData correctly', async ({ page, createDataSource }) => {
|
||||
const TIMESTAMP = Date.now();
|
||||
const DATASOURCE_TYPED_NAME = `SandboxDatasourceInstance-${TIMESTAMP}`;
|
||||
// Add the datasource
|
||||
const response = await createDataSource({
|
||||
type: DATASOURCE_ID,
|
||||
name: DATASOURCE_TYPED_NAME,
|
||||
});
|
||||
const DATASOURCE_CONNECTION_ID = response.uid;
|
||||
await page.goto(`/connections/datasources/edit/${DATASOURCE_CONNECTION_ID}`);
|
||||
|
||||
const valueToStore = 'test' + random(100);
|
||||
@ -62,10 +80,10 @@ test.describe.skip(
|
||||
await queryInput.fill(valueToStore);
|
||||
await expect(queryInput).toHaveValue(valueToStore);
|
||||
|
||||
const saveButton = page.getByTestId('data-testid Data source settings page Save and test button');
|
||||
const saveButton = page.getByTestId('data-testid Data source settings page Save and Test button');
|
||||
await saveButton.click();
|
||||
|
||||
const alert = page.locator('[data-testid="data-testid Alert"]');
|
||||
const alert = page.getByTestId('data-testid Data source settings page Alert');
|
||||
await expect(alert).toBeVisible();
|
||||
await expect(alert).toContainText('Sandbox Success');
|
||||
|
||||
@ -85,7 +103,20 @@ test.describe.skip(
|
||||
});
|
||||
});
|
||||
|
||||
test('Should not wrap the query editor in a sandbox wrapper', async ({ page, dashboardPage, selectors }) => {
|
||||
test('Should not wrap the query editor in a sandbox wrapper', async ({
|
||||
page,
|
||||
createDataSource,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
}) => {
|
||||
const TIMESTAMP = Date.now();
|
||||
const DATASOURCE_TYPED_NAME = `SandboxDatasourceInstance-${TIMESTAMP}`;
|
||||
// Add the datasource
|
||||
const response = await createDataSource({
|
||||
type: DATASOURCE_ID,
|
||||
name: DATASOURCE_TYPED_NAME,
|
||||
});
|
||||
const DATASOURCE_CONNECTION_ID = response.uid;
|
||||
await page.goto('/explore');
|
||||
|
||||
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
||||
@ -103,12 +134,19 @@ test.describe.skip(
|
||||
);
|
||||
await expect(breadcrumb).toBeVisible();
|
||||
|
||||
await page.waitForTimeout(300); // wait to prevent false positives because playwright checks too fast
|
||||
const sandboxDiv = page.locator(`div[data-plugin-sandbox="${DATASOURCE_ID}"]`);
|
||||
await expect(sandboxDiv).toBeHidden();
|
||||
});
|
||||
|
||||
test('Should accept values when typed', async ({ page, dashboardPage, selectors }) => {
|
||||
test('Should accept values when typed', async ({ page, createDataSource, dashboardPage, selectors }) => {
|
||||
const TIMESTAMP = Date.now();
|
||||
const DATASOURCE_TYPED_NAME = `SandboxDatasourceInstance-${TIMESTAMP}`;
|
||||
// Add the datasource
|
||||
const response = await createDataSource({
|
||||
type: DATASOURCE_ID,
|
||||
name: DATASOURCE_TYPED_NAME,
|
||||
});
|
||||
const DATASOURCE_CONNECTION_ID = response.uid;
|
||||
await page.goto('/explore');
|
||||
|
||||
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
||||
@ -142,7 +180,20 @@ test.describe.skip(
|
||||
});
|
||||
});
|
||||
|
||||
test('Should wrap the query editor in a sandbox wrapper', async ({ page, dashboardPage, selectors }) => {
|
||||
test('Should wrap the query editor in a sandbox wrapper', async ({
|
||||
page,
|
||||
createDataSource,
|
||||
dashboardPage,
|
||||
selectors,
|
||||
}) => {
|
||||
const TIMESTAMP = Date.now();
|
||||
const DATASOURCE_TYPED_NAME = `SandboxDatasourceInstance-${TIMESTAMP}`;
|
||||
// Add the datasource
|
||||
const response = await createDataSource({
|
||||
type: DATASOURCE_ID,
|
||||
name: DATASOURCE_TYPED_NAME,
|
||||
});
|
||||
const DATASOURCE_CONNECTION_ID = response.uid;
|
||||
await page.goto('/explore');
|
||||
|
||||
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
||||
@ -164,7 +215,15 @@ test.describe.skip(
|
||||
await expect(sandboxDiv).toBeVisible();
|
||||
});
|
||||
|
||||
test('Should accept values when typed', async ({ page, dashboardPage, selectors }) => {
|
||||
test('Should accept values when typed', async ({ page, createDataSource, dashboardPage, selectors }) => {
|
||||
const TIMESTAMP = Date.now();
|
||||
const DATASOURCE_TYPED_NAME = `SandboxDatasourceInstance-${TIMESTAMP}`;
|
||||
// Add the datasource
|
||||
const response = await createDataSource({
|
||||
type: DATASOURCE_ID,
|
||||
name: DATASOURCE_TYPED_NAME,
|
||||
});
|
||||
const DATASOURCE_CONNECTION_ID = response.uid;
|
||||
await page.goto('/explore');
|
||||
|
||||
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
||||
|
@ -3,7 +3,7 @@ import { test, expect } from '@grafana/plugin-e2e';
|
||||
test.describe(
|
||||
'Migrate to Cloud (On-prem)',
|
||||
{
|
||||
tag: ['@various', '@wip'],
|
||||
tag: ['@various'],
|
||||
},
|
||||
() => {
|
||||
test.describe('with mocked calls to the API backend', () => {
|
||||
@ -59,8 +59,7 @@ test.describe(
|
||||
total: SNAPSHOT_RESULTS.length,
|
||||
};
|
||||
|
||||
// TODO: fix the test. It makes it most of the way through. Probably a network mock issue.
|
||||
test.skip('creates and uploads a snapshot successfully', async ({ page }) => {
|
||||
test('creates and uploads a snapshot successfully', async ({ page }) => {
|
||||
// Visit the migrate to cloud onprem page
|
||||
await page.goto('/admin/migrate-to-cloud');
|
||||
|
||||
@ -75,7 +74,7 @@ test.describe(
|
||||
await tokenInput.fill('test');
|
||||
|
||||
// Mock API responses
|
||||
await page.route(/api\/cloudmigration\/migration/, async (route) => {
|
||||
await page.route(/api\/cloudmigration\/migration$/, async (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
@ -93,7 +92,7 @@ test.describe(
|
||||
}
|
||||
});
|
||||
|
||||
await page.route(/api\/cloudmigration\/migration\/${SESSION_UID}\/snapshots\?page=1&limit=1/, async (route) => {
|
||||
await page.route(`api/cloudmigration/migration/${SESSION_UID}/snapshots?page=1&limit=1*`, async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
@ -141,14 +140,14 @@ test.describe(
|
||||
'migrate-to-cloud-configure-snapshot-checkbox-resource-mute_timing'
|
||||
);
|
||||
await muteTimingCheckbox.uncheck({ force: true });
|
||||
await expect(muteTimingCheckbox).not.toBeChecked();
|
||||
await expect(muteTimingCheckbox).toBeChecked({ checked: false });
|
||||
|
||||
// Validate that those resources are now unchecked
|
||||
for (const resourceType of ['alert_rule', 'alert_rule_group', 'include-all']) {
|
||||
const checkbox = page.getByTestId(
|
||||
`migrate-to-cloud-configure-snapshot-checkbox-resource-${resourceType.toLowerCase()}`
|
||||
);
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
await expect(checkbox).toBeChecked({ checked: false });
|
||||
}
|
||||
|
||||
// Check everything again because we can
|
||||
@ -164,7 +163,7 @@ test.describe(
|
||||
}
|
||||
|
||||
// Mock snapshot creation
|
||||
await page.route(/api\/cloudmigration\/migration\/${SESSION_UID}\/snapshot/, async (route) => {
|
||||
await page.route(new RegExp(`api\/cloudmigration\/migration\/${SESSION_UID}\/snapshot$`), async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
@ -174,7 +173,7 @@ test.describe(
|
||||
});
|
||||
});
|
||||
|
||||
await page.route(/api\/cloudmigration\/migration\/${SESSION_UID}\/snapshots\?page=1&limit=1/, async (route) => {
|
||||
await page.route(`api/cloudmigration/migration/${SESSION_UID}/snapshots?page=1&limit=1*`, async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
@ -194,7 +193,7 @@ test.describe(
|
||||
|
||||
let getSnapshotCalled = false;
|
||||
await page.route(
|
||||
/api\/cloudmigration\/migration\/${SESSION_UID}\/snapshot\/${SNAPSHOT_UID1}\?resultPage=1&resultLimit=50/,
|
||||
`api/cloudmigration/migration/${SESSION_UID}/snapshot/${SNAPSHOT_UID1}?resultPage=1&resultLimit=50*`,
|
||||
async (route) => {
|
||||
if (!getSnapshotCalled) {
|
||||
getSnapshotCalled = true;
|
||||
@ -238,9 +237,12 @@ test.describe(
|
||||
await expect(buildSnapshotButton).toBeVisible();
|
||||
await buildSnapshotButton.click();
|
||||
|
||||
const uploadButton = page.getByTestId('migrate-to-cloud-summary-upload-snapshot-button');
|
||||
await expect(uploadButton).toBeVisible();
|
||||
|
||||
// Mock upload
|
||||
const uploadSnapshot = await page.route(
|
||||
/api\/cloudmigration\/migration\/${SESSION_UID}\/snapshot\/${SNAPSHOT_UID1}\/upload/,
|
||||
await page.route(
|
||||
`api/cloudmigration/migration/${SESSION_UID}/snapshot/${SNAPSHOT_UID1}/upload`,
|
||||
async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
@ -250,17 +252,8 @@ test.describe(
|
||||
}
|
||||
);
|
||||
|
||||
// Upload the snapshot
|
||||
const uploadButton = page.getByTestId('migrate-to-cloud-summary-upload-snapshot-button');
|
||||
await expect(uploadButton).toBeVisible();
|
||||
|
||||
await uploadButton.focus();
|
||||
await uploadButton.click({ force: true });
|
||||
|
||||
// Mock uploading status
|
||||
const getSnapshotListUploading = await page.route(
|
||||
/api\/cloudmigration\/migration\/${SESSION_UID}\/snapshots\?page=1&limit=1/,
|
||||
async (route) => {
|
||||
await page.route(`api/cloudmigration/migration/${SESSION_UID}/snapshots?page=1&limit=1*`, async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
@ -276,13 +269,12 @@ test.describe(
|
||||
],
|
||||
}),
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Simulate the snapshot being uploaded
|
||||
let getSnapshotUploadingCalls = 0;
|
||||
const getSnapshotUploading = await page.route(
|
||||
/api\/cloudmigration\/migration\/${SESSION_UID}\/snapshot\/${SNAPSHOT_UID1}\?resultPage=1&resultLimit=50/,
|
||||
await page.route(
|
||||
`api/cloudmigration/migration/${SESSION_UID}/snapshot/${SNAPSHOT_UID1}?resultPage=1&resultLimit=50*`,
|
||||
async (route) => {
|
||||
if (getSnapshotUploadingCalls <= 1) {
|
||||
await route.fulfill({
|
||||
@ -312,14 +304,11 @@ test.describe(
|
||||
results: SNAPSHOT_RESULTS.map((r) => ({ ...r, status: 'OK' })),
|
||||
stats: {
|
||||
types: STATS.types,
|
||||
statuses: SNAPSHOT_RESULTS.reduce(
|
||||
(acc, r) => {
|
||||
statuses: SNAPSHOT_RESULTS.reduce<Record<string, number>>((acc, r) => {
|
||||
const status = (r as { status?: string }).status || 'UNKNOWN';
|
||||
acc[status] = (acc[status] || 0) + 1;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>
|
||||
),
|
||||
}, {}),
|
||||
total: SNAPSHOT_RESULTS.length,
|
||||
},
|
||||
}),
|
||||
@ -328,12 +317,12 @@ test.describe(
|
||||
}
|
||||
);
|
||||
|
||||
await uploadSnapshot;
|
||||
await getSnapshotListUploading;
|
||||
await getSnapshotUploading;
|
||||
// Upload the snapshot
|
||||
await uploadButton.focus();
|
||||
await uploadButton.click({ force: true });
|
||||
|
||||
// At least some of the items are marked with "Uploaded to cloud" status
|
||||
await expect(page.getByText('Uploaded to cloud')).toBeVisible();
|
||||
await expect(page.getByText('Uploaded to cloud')).toHaveCount(22);
|
||||
|
||||
// We can now reconfigure the snapshot
|
||||
const reconfigureButton = page.getByTestId('migrate-to-cloud-summary-reconfigure-snapshot-button');
|
||||
|
@ -1,30 +1,25 @@
|
||||
import { Page } from 'playwright-core';
|
||||
|
||||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
// Todo: Fix datasource creation
|
||||
test.describe.skip(
|
||||
test.describe(
|
||||
'Prometheus config',
|
||||
{
|
||||
tag: ['@various'],
|
||||
},
|
||||
() => {
|
||||
const DATASOURCE_ID = 'Prometheus';
|
||||
const DATASOURCE_TYPED_NAME = 'PrometheusDatasourceInstance';
|
||||
|
||||
test.beforeEach(async ({ page, selectors, createDataSourceConfigPage }) => {
|
||||
// Navigate to add data source page
|
||||
await page.goto('/datasources/new');
|
||||
|
||||
// Select the Prometheus data source
|
||||
const prometheusPlugin = page.getByRole('button', { name: DATASOURCE_ID });
|
||||
await prometheusPlugin.scrollIntoViewIfNeeded();
|
||||
await expect(prometheusPlugin).toBeVisible();
|
||||
await prometheusPlugin.click();
|
||||
});
|
||||
const DATASOURCE_PREFIX = 'PrometheusConfig';
|
||||
|
||||
test('should have the following components: connection settings, managed alerts, scrape interval, query timeout, default editor, disable metric lookup, prometheus type, cache level, incremental querying, disable recording rules, custom query parameters, http method', async ({
|
||||
page,
|
||||
selectors,
|
||||
createDataSourceConfigPage,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||
const configPage = await createDataSourceConfigPage({
|
||||
type: 'prometheus',
|
||||
name: DATASOURCE_NAME,
|
||||
});
|
||||
// connection settings
|
||||
const connectionSettings = page.getByLabel(
|
||||
selectors.components.DataSource.Prometheus.configPage.connectionSettings
|
||||
@ -33,72 +28,85 @@ test.describe.skip(
|
||||
|
||||
// managed alerts
|
||||
const manageAlerts = page.locator(`#${selectors.components.DataSource.Prometheus.configPage.manageAlerts}`);
|
||||
await manageAlerts.scrollIntoViewIfNeeded();
|
||||
await expect(manageAlerts).toBeVisible();
|
||||
|
||||
// scrape interval
|
||||
const scrapeInterval = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.scrapeInterval);
|
||||
await scrapeInterval.scrollIntoViewIfNeeded();
|
||||
const scrapeInterval = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.scrapeInterval
|
||||
);
|
||||
await expect(scrapeInterval).toBeVisible();
|
||||
|
||||
// query timeout
|
||||
const queryTimeout = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.queryTimeout);
|
||||
await queryTimeout.scrollIntoViewIfNeeded();
|
||||
const queryTimeout = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.queryTimeout
|
||||
);
|
||||
await expect(queryTimeout).toBeVisible();
|
||||
|
||||
// default editor
|
||||
const defaultEditor = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.defaultEditor);
|
||||
await defaultEditor.scrollIntoViewIfNeeded();
|
||||
const defaultEditor = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.defaultEditor
|
||||
);
|
||||
await expect(defaultEditor).toBeVisible();
|
||||
|
||||
// disable metric lookup
|
||||
const disableMetricLookup = page.locator(
|
||||
`#${selectors.components.DataSource.Prometheus.configPage.disableMetricLookup}`
|
||||
);
|
||||
await disableMetricLookup.scrollIntoViewIfNeeded();
|
||||
await expect(disableMetricLookup).toBeVisible();
|
||||
|
||||
// prometheus type
|
||||
const prometheusType = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.prometheusType);
|
||||
await prometheusType.scrollIntoViewIfNeeded();
|
||||
const prometheusType = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.prometheusType
|
||||
);
|
||||
await expect(prometheusType).toBeVisible();
|
||||
|
||||
// cache level
|
||||
const cacheLevel = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.cacheLevel);
|
||||
await cacheLevel.scrollIntoViewIfNeeded();
|
||||
const cacheLevel = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.cacheLevel
|
||||
);
|
||||
await expect(cacheLevel).toBeVisible();
|
||||
|
||||
// incremental querying
|
||||
const incrementalQuerying = page.locator(
|
||||
`#${selectors.components.DataSource.Prometheus.configPage.incrementalQuerying}`
|
||||
);
|
||||
await incrementalQuerying.scrollIntoViewIfNeeded();
|
||||
await expect(incrementalQuerying).toBeVisible();
|
||||
|
||||
// disable recording rules
|
||||
const disableRecordingRules = page.locator(
|
||||
`#${selectors.components.DataSource.Prometheus.configPage.disableRecordingRules}`
|
||||
);
|
||||
await disableRecordingRules.scrollIntoViewIfNeeded();
|
||||
await expect(disableRecordingRules).toBeVisible();
|
||||
|
||||
// custom query parameters
|
||||
const customQueryParameters = page.getByTestId(
|
||||
const customQueryParameters = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.customQueryParameters
|
||||
);
|
||||
await customQueryParameters.scrollIntoViewIfNeeded();
|
||||
await expect(customQueryParameters).toBeVisible();
|
||||
|
||||
// http method
|
||||
const httpMethod = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.httpMethod);
|
||||
await httpMethod.scrollIntoViewIfNeeded();
|
||||
const httpMethod = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.httpMethod
|
||||
);
|
||||
await expect(httpMethod).toBeVisible();
|
||||
});
|
||||
|
||||
test('should save the default editor when navigating to explore', async ({ page, selectors }) => {
|
||||
test('should save the default editor when navigating to explore', async ({
|
||||
createDataSourceConfigPage,
|
||||
explorePage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||
const configPage = await createDataSourceConfigPage({
|
||||
type: 'prometheus',
|
||||
name: DATASOURCE_NAME,
|
||||
});
|
||||
|
||||
// Click on default editor
|
||||
const defaultEditor = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.defaultEditor);
|
||||
await defaultEditor.scrollIntoViewIfNeeded();
|
||||
const defaultEditor = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.defaultEditor
|
||||
);
|
||||
await expect(defaultEditor).toBeVisible();
|
||||
await defaultEditor.click();
|
||||
|
||||
@ -111,17 +119,12 @@ test.describe.skip(
|
||||
);
|
||||
await connectionSettings.fill('http://prom-url:9090');
|
||||
|
||||
// Set data source name
|
||||
const nameInput = page.getByTestId(selectors.pages.DataSource.name);
|
||||
await nameInput.clear();
|
||||
await nameInput.fill(DATASOURCE_TYPED_NAME);
|
||||
|
||||
// Save and test
|
||||
const saveAndTestButton = page.getByTestId(selectors.pages.DataSource.saveAndTest);
|
||||
const saveAndTestButton = configPage.getByGrafanaSelector(selectors.pages.DataSource.saveAndTest);
|
||||
await saveAndTestButton.click();
|
||||
|
||||
// Navigate to explore
|
||||
await page.goto('/explore');
|
||||
await explorePage.goto();
|
||||
|
||||
// Select the data source
|
||||
const dataSourcePicker = page.getByTestId(selectors.components.DataSourcePicker.container);
|
||||
@ -129,7 +132,7 @@ test.describe.skip(
|
||||
await dataSourcePicker.click();
|
||||
|
||||
// Type the data source name and press enter
|
||||
await page.keyboard.type(DATASOURCE_TYPED_NAME);
|
||||
await page.keyboard.type(DATASOURCE_NAME);
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// Verify the builder metric select is visible
|
||||
@ -139,10 +142,21 @@ test.describe.skip(
|
||||
await expect(metricSelect).toBeVisible();
|
||||
});
|
||||
|
||||
test('should allow a user to add the version when the Prom type is selected', async ({ page, selectors }) => {
|
||||
test('should allow a user to add the version when the Prom type is selected', async ({
|
||||
createDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||
const configPage = await createDataSourceConfigPage({
|
||||
type: 'prometheus',
|
||||
name: DATASOURCE_NAME,
|
||||
});
|
||||
|
||||
// Click on prometheus type
|
||||
const prometheusType = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.prometheusType);
|
||||
await prometheusType.scrollIntoViewIfNeeded();
|
||||
const prometheusType = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.prometheusType
|
||||
);
|
||||
await expect(prometheusType).toBeVisible();
|
||||
await prometheusType.click();
|
||||
|
||||
@ -150,43 +164,54 @@ test.describe.skip(
|
||||
await selectOption(page, 'Prometheus');
|
||||
|
||||
// Verify prometheus version is visible
|
||||
const prometheusVersion = page.getByTestId(
|
||||
const prometheusVersion = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.prometheusVersion
|
||||
);
|
||||
await prometheusVersion.scrollIntoViewIfNeeded();
|
||||
await expect(prometheusVersion).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have a cache level component', async ({ page, selectors }) => {
|
||||
test('should have a cache level component', async ({ createDataSourceConfigPage, page, selectors }) => {
|
||||
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||
const configPage = await createDataSourceConfigPage({
|
||||
type: 'prometheus',
|
||||
name: DATASOURCE_NAME,
|
||||
});
|
||||
|
||||
// Verify cache level is visible
|
||||
const cacheLevel = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.cacheLevel);
|
||||
await cacheLevel.scrollIntoViewIfNeeded();
|
||||
const cacheLevel = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.cacheLevel
|
||||
);
|
||||
await expect(cacheLevel).toBeVisible();
|
||||
});
|
||||
|
||||
test('should allow a user to select a query overlap window when incremental querying is selected', async ({
|
||||
createDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||
const configPage = await createDataSourceConfigPage({
|
||||
type: 'prometheus',
|
||||
name: DATASOURCE_NAME,
|
||||
});
|
||||
|
||||
// Check the incremental querying checkbox
|
||||
const incrementalQuerying = page.locator(
|
||||
`#${selectors.components.DataSource.Prometheus.configPage.incrementalQuerying}`
|
||||
);
|
||||
await incrementalQuerying.scrollIntoViewIfNeeded();
|
||||
await expect(incrementalQuerying).toBeVisible();
|
||||
await incrementalQuerying.check({ force: true });
|
||||
|
||||
// Verify query overlap window is visible
|
||||
const queryOverlapWindow = page.getByTestId(
|
||||
const queryOverlapWindow = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.queryOverlapWindow
|
||||
);
|
||||
await queryOverlapWindow.scrollIntoViewIfNeeded();
|
||||
await expect(queryOverlapWindow).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
async function selectOption(page, option) {
|
||||
async function selectOption(page: Page, option: string) {
|
||||
const optionElement = page.getByRole('option').filter({ hasText: option });
|
||||
await expect(optionElement).toBeVisible();
|
||||
await optionElement.click();
|
||||
|
@ -1,14 +1,20 @@
|
||||
import { Page } from 'playwright-core';
|
||||
|
||||
import { test, expect, E2ESelectorGroups } from '@grafana/plugin-e2e';
|
||||
import {
|
||||
test,
|
||||
expect,
|
||||
E2ESelectorGroups,
|
||||
CreateDataSourceArgs,
|
||||
DataSourceSettings,
|
||||
DataSourceConfigPage,
|
||||
} from '@grafana/plugin-e2e';
|
||||
|
||||
import { getResources } from '../utils/prometheus-helpers';
|
||||
|
||||
// TODO: fix some tests. Race conditions with other tests in the file cause some to fail.
|
||||
test.describe.skip(
|
||||
test.describe(
|
||||
'Prometheus query editor',
|
||||
{
|
||||
tag: ['@various', '@wip'],
|
||||
tag: ['@various'],
|
||||
},
|
||||
() => {
|
||||
const DATASOURCE_ID = 'Prometheus';
|
||||
@ -18,18 +24,25 @@ test.describe.skip(
|
||||
/**
|
||||
* Create and save a Prometheus data source, navigate to code or builder
|
||||
*/
|
||||
async function navigateToEditor(page: Page, selectors: E2ESelectorGroups, editorType: string, name: string) {
|
||||
// Navigate to add data source page
|
||||
await page.goto('/datasources/new');
|
||||
async function navigateToEditor(
|
||||
page: Page,
|
||||
selectors: E2ESelectorGroups,
|
||||
createDataSource: (args: CreateDataSourceArgs) => Promise<DataSourceSettings>,
|
||||
editorType: EditorType,
|
||||
name: string,
|
||||
gotoDataSourceConfigPage: (uid: string) => Promise<DataSourceConfigPage>
|
||||
) {
|
||||
const { uid } = await createDataSource({
|
||||
type: 'prometheus',
|
||||
name,
|
||||
});
|
||||
|
||||
// Select the Prometheus data source
|
||||
const prometheusPlugin = page.getByRole('button', { name: DATASOURCE_ID });
|
||||
await prometheusPlugin.scrollIntoViewIfNeeded();
|
||||
await expect(prometheusPlugin).toBeVisible();
|
||||
await prometheusPlugin.click();
|
||||
const configPage = await gotoDataSourceConfigPage(uid);
|
||||
|
||||
// Choose default editor
|
||||
const defaultEditor = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.defaultEditor);
|
||||
const defaultEditor = configPage.getByGrafanaSelector(
|
||||
selectors.components.DataSource.Prometheus.configPage.defaultEditor
|
||||
);
|
||||
await defaultEditor.scrollIntoViewIfNeeded();
|
||||
await expect(defaultEditor).toBeVisible();
|
||||
await defaultEditor.click();
|
||||
@ -42,13 +55,10 @@ test.describe.skip(
|
||||
);
|
||||
await connectionSettings.fill('http://prom-url:9090');
|
||||
|
||||
// Name the DS
|
||||
const nameInput = page.getByTestId(selectors.pages.DataSource.name);
|
||||
await nameInput.clear();
|
||||
await nameInput.fill(name);
|
||||
|
||||
const saveAndTestButton = page.getByTestId(selectors.pages.DataSource.saveAndTest);
|
||||
const saveResponse = page.waitForResponse((resp) => resp.url().includes('/api/datasources'));
|
||||
const saveAndTestButton = configPage.getByGrafanaSelector(selectors.pages.DataSource.saveAndTest);
|
||||
await saveAndTestButton.click();
|
||||
await saveResponse;
|
||||
|
||||
// Visit explore
|
||||
await page.goto('/explore');
|
||||
@ -64,24 +74,42 @@ test.describe.skip(
|
||||
await dataSourceOption.click();
|
||||
}
|
||||
|
||||
test('should have a kickstart component', async ({ page, selectors }) => {
|
||||
await navigateToEditor(page, selectors, 'Code', 'prometheus');
|
||||
test('should have a kickstart component', async ({
|
||||
createDataSource,
|
||||
page,
|
||||
selectors,
|
||||
gotoDataSourceConfigPage,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `prometheus_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Code', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
|
||||
const queryPatterns = page.getByTestId(selectors.components.QueryBuilder.queryPatterns);
|
||||
await queryPatterns.scrollIntoViewIfNeeded();
|
||||
await expect(queryPatterns).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have an explain component', async ({ page, selectors }) => {
|
||||
await navigateToEditor(page, selectors, 'Code', 'prometheus');
|
||||
test('should have an explain component', async ({
|
||||
createDataSource,
|
||||
gotoDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `prometheus_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Code', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
|
||||
const explain = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.explain);
|
||||
await explain.scrollIntoViewIfNeeded();
|
||||
await expect(explain).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have an editor toggle component', async ({ page, selectors }) => {
|
||||
await navigateToEditor(page, selectors, 'Code', 'prometheus');
|
||||
test('should have an editor toggle component', async ({
|
||||
createDataSource,
|
||||
gotoDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `prometheus_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Code', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
|
||||
const editorToggle = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.editorToggle);
|
||||
await editorToggle.scrollIntoViewIfNeeded();
|
||||
@ -89,10 +117,13 @@ test.describe.skip(
|
||||
});
|
||||
|
||||
test('should have an options component with legend, format, step, type and exemplars', async ({
|
||||
createDataSource,
|
||||
gotoDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
await navigateToEditor(page, selectors, 'Code', 'prometheus');
|
||||
const DATASOURCE_NAME = `prometheus_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Code', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
|
||||
// Open options
|
||||
const options = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.options);
|
||||
@ -102,36 +133,40 @@ test.describe.skip(
|
||||
|
||||
// Check options
|
||||
const legend = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.legend);
|
||||
await legend.scrollIntoViewIfNeeded();
|
||||
await expect(legend).toBeVisible();
|
||||
|
||||
const format = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.format);
|
||||
await format.scrollIntoViewIfNeeded();
|
||||
await expect(format).toBeVisible();
|
||||
|
||||
const step = page.locator('[data-test-id="prometheus-step"]');
|
||||
await step.scrollIntoViewIfNeeded();
|
||||
const step = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.step);
|
||||
await expect(step).toBeVisible();
|
||||
|
||||
const type = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.type);
|
||||
await type.scrollIntoViewIfNeeded();
|
||||
await expect(type).toBeVisible();
|
||||
|
||||
const exemplars = page.getByTestId('prometheus-exemplars');
|
||||
await exemplars.scrollIntoViewIfNeeded();
|
||||
const exemplars = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.exemplars);
|
||||
await expect(exemplars).toBeVisible();
|
||||
});
|
||||
|
||||
test.describe('Code editor', () => {
|
||||
test('navigates to the code editor with editor type as code', async ({ page, selectors }) => {
|
||||
await navigateToEditor(page, selectors, 'Code', 'prometheusCode');
|
||||
});
|
||||
|
||||
test('navigates to the code editor and opens the metrics browser with metric search, labels, label values, and all components', async ({
|
||||
test('navigates to the code editor with editor type as code', async ({
|
||||
createDataSource,
|
||||
gotoDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
await navigateToEditor(page, selectors, 'Code', 'prometheusCode');
|
||||
const DATASOURCE_NAME = `prometheusCode_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Code', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
});
|
||||
|
||||
test('navigates to the code editor and opens the metrics browser with metric search, labels, label values, and all components', async ({
|
||||
createDataSource,
|
||||
gotoDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `prometheusCode_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Code', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
|
||||
await getResources(page);
|
||||
|
||||
@ -179,8 +214,14 @@ test.describe.skip(
|
||||
await expect(clear).toBeVisible();
|
||||
});
|
||||
|
||||
test('selects a metric in the metrics browser and uses the query', async ({ page, selectors }) => {
|
||||
await navigateToEditor(page, selectors, 'Code', 'prometheusCode');
|
||||
test('selects a metric in the metrics browser and uses the query', async ({
|
||||
createDataSource,
|
||||
gotoDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `prometheusCode_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Code', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
|
||||
await getResources(page);
|
||||
|
||||
@ -217,12 +258,24 @@ test.describe.skip(
|
||||
});
|
||||
|
||||
test.describe('Query builder', () => {
|
||||
test('navigates to the query builder with editor type as code', async ({ page, selectors }) => {
|
||||
await navigateToEditor(page, selectors, 'Builder', 'prometheusBuilder');
|
||||
test('navigates to the query builder with editor type as code', async ({
|
||||
createDataSource,
|
||||
gotoDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `prometheusBuilder_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Builder', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
});
|
||||
|
||||
test('the query builder contains metric select, label filters and operations', async ({ page, selectors }) => {
|
||||
await navigateToEditor(page, selectors, 'Builder', 'prometheusBuilder');
|
||||
test('the query builder contains metric select, label filters and operations', async ({
|
||||
createDataSource,
|
||||
gotoDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `prometheusBuilder_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Builder', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
|
||||
await getResources(page);
|
||||
|
||||
@ -241,8 +294,15 @@ test.describe.skip(
|
||||
await expect(valueSelect).toBeVisible();
|
||||
});
|
||||
|
||||
test('can select a metric and provide a hint', async ({ page, selectors }) => {
|
||||
await navigateToEditor(page, selectors, 'Builder', 'prometheusBuilder');
|
||||
// this throws a maximum update depth error?!
|
||||
test.skip('can select a metric and provide a hint', async ({
|
||||
createDataSource,
|
||||
gotoDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `prometheusBuilder_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Builder', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
|
||||
await getResources(page);
|
||||
|
||||
@ -258,8 +318,14 @@ test.describe.skip(
|
||||
await expect(hints).toContainText('hint: add rate');
|
||||
});
|
||||
|
||||
test('should have the metrics explorer opened via the metric select', async ({ page, selectors }) => {
|
||||
await navigateToEditor(page, selectors, 'Builder', 'prometheusBuilder');
|
||||
test('should have the metrics explorer opened via the metric select', async ({
|
||||
createDataSource,
|
||||
gotoDataSourceConfigPage,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `prometheusBuilder_${Date.now()}`;
|
||||
await navigateToEditor(page, selectors, createDataSource, 'Builder', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||
|
||||
await getResources(page);
|
||||
|
||||
@ -267,9 +333,8 @@ test.describe.skip(
|
||||
selectors.components.DataSource.Prometheus.queryEditor.builder.metricSelect
|
||||
);
|
||||
await expect(metricSelect).toBeVisible();
|
||||
await metricSelect.click();
|
||||
|
||||
await selectOption(page, 'Metrics explorer', selectors);
|
||||
await page.getByLabel('Open metrics explorer').click();
|
||||
|
||||
const metricsExplorer = page.getByTestId(
|
||||
selectors.components.DataSource.Prometheus.queryEditor.builder.metricsExplorer
|
||||
|
@ -1,20 +1,22 @@
|
||||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
import { Page } from 'playwright-core';
|
||||
|
||||
import { test, expect, E2ESelectorGroups } from '@grafana/plugin-e2e';
|
||||
|
||||
import { addDashboard } from '../utils/dashboard-helpers';
|
||||
import { getResources } from '../utils/prometheus-helpers';
|
||||
|
||||
test.describe.skip(
|
||||
test.describe(
|
||||
'Prometheus variable query editor',
|
||||
{
|
||||
tag: ['@various', '@wip'],
|
||||
tag: ['@various'],
|
||||
},
|
||||
() => {
|
||||
const DATASOURCE_NAME = 'prometheusVariableDS';
|
||||
const DATASOURCE_PREFIX = 'prometheusVariableDS';
|
||||
|
||||
/**
|
||||
* Click dashboard settings and then the variables tab
|
||||
*/
|
||||
async function navigateToVariables(page, selectors) {
|
||||
async function navigateToVariables(page: Page, selectors: E2ESelectorGroups) {
|
||||
const editButton = page.getByTestId(selectors.components.NavToolbar.editDashboard.editButton);
|
||||
await expect(editButton).toBeVisible();
|
||||
await editButton.click();
|
||||
@ -30,7 +32,12 @@ test.describe.skip(
|
||||
/**
|
||||
* Begin the process of adding a query type variable for a Prometheus data source
|
||||
*/
|
||||
async function addPrometheusQueryVariable(page, selectors, variableName) {
|
||||
async function addPrometheusQueryVariable(
|
||||
page: Page,
|
||||
selectors: E2ESelectorGroups,
|
||||
datasourceName: string,
|
||||
variableName: string
|
||||
) {
|
||||
const addVariableButton = page.getByTestId(selectors.pages.Dashboard.Settings.Variables.List.addVariableCTAV2);
|
||||
await addVariableButton.click();
|
||||
|
||||
@ -42,7 +49,7 @@ test.describe.skip(
|
||||
await expect(dataSourcePicker).toBeVisible();
|
||||
await dataSourcePicker.click();
|
||||
|
||||
const dataSourceOption = page.getByText(DATASOURCE_NAME);
|
||||
const dataSourceOption = page.getByText(datasourceName);
|
||||
await dataSourceOption.scrollIntoViewIfNeeded();
|
||||
await expect(dataSourceOption).toBeVisible();
|
||||
await dataSourceOption.click();
|
||||
@ -53,10 +60,16 @@ test.describe.skip(
|
||||
/**
|
||||
* Create a Prometheus variable and navigate to the query editor to check that it is available to use.
|
||||
*/
|
||||
async function variableFlowToQueryEditor(page, selectors, variableName, queryType) {
|
||||
async function variableFlowToQueryEditor(
|
||||
page: Page,
|
||||
selectors: E2ESelectorGroups,
|
||||
datasourceName: string,
|
||||
variableName: string,
|
||||
queryType: string
|
||||
) {
|
||||
await addDashboard(page);
|
||||
await navigateToVariables(page, selectors);
|
||||
await addPrometheusQueryVariable(page, selectors, variableName);
|
||||
await addPrometheusQueryVariable(page, selectors, datasourceName, variableName);
|
||||
|
||||
// Select query type
|
||||
const queryTypeSelect = page.getByTestId(
|
||||
@ -88,7 +101,7 @@ test.describe.skip(
|
||||
// Select prom data source from the data source list
|
||||
const dataSourcePickerInput = page.getByTestId(selectors.components.DataSourcePicker.inputV2);
|
||||
await dataSourcePickerInput.click();
|
||||
await dataSourcePickerInput.fill(DATASOURCE_NAME);
|
||||
await dataSourcePickerInput.fill(datasourceName);
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// Confirm the variable exists in the correct input
|
||||
@ -119,19 +132,21 @@ test.describe.skip(
|
||||
}
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page, selectors, createDataSourceConfigPage }) => {
|
||||
await createDataSourceConfigPage({ type: 'prometheus', name: DATASOURCE_NAME });
|
||||
});
|
||||
|
||||
test('should navigate to variable query editor', async ({ page, selectors }) => {
|
||||
await addDashboard(page);
|
||||
await navigateToVariables(page, selectors);
|
||||
});
|
||||
|
||||
test('should select a query type for a Prometheus variable query', async ({ page, selectors }) => {
|
||||
test('should select a query type for a Prometheus variable query', async ({
|
||||
createDataSource,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||
await createDataSource({ type: 'prometheus', name: DATASOURCE_NAME });
|
||||
await addDashboard(page);
|
||||
await navigateToVariables(page, selectors);
|
||||
await addPrometheusQueryVariable(page, selectors, 'labelsVariable');
|
||||
await addPrometheusQueryVariable(page, selectors, DATASOURCE_NAME, 'labelsVariable');
|
||||
|
||||
// Select query type
|
||||
const queryTypeSelect = page.getByTestId(
|
||||
@ -142,29 +157,38 @@ test.describe.skip(
|
||||
});
|
||||
|
||||
test('should create a label names variable that is selectable in the label select in query builder', async ({
|
||||
createDataSource,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
await variableFlowToQueryEditor(page, selectors, 'labelnames', 'Label names');
|
||||
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||
await createDataSource({ type: 'prometheus', name: DATASOURCE_NAME });
|
||||
await variableFlowToQueryEditor(page, selectors, DATASOURCE_NAME, 'labelnames', 'Label names');
|
||||
});
|
||||
|
||||
test('should create a label values variable that is selectable in the label values select in query builder', async ({
|
||||
createDataSource,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
await variableFlowToQueryEditor(page, selectors, 'labelvalues', 'Label values');
|
||||
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||
await createDataSource({ type: 'prometheus', name: DATASOURCE_NAME });
|
||||
await variableFlowToQueryEditor(page, selectors, DATASOURCE_NAME, 'labelvalues', 'Label values');
|
||||
});
|
||||
|
||||
test('should create a metric names variable that is selectable in the metric select in query builder', async ({
|
||||
createDataSource,
|
||||
page,
|
||||
selectors,
|
||||
}) => {
|
||||
await variableFlowToQueryEditor(page, selectors, 'metrics', 'Metrics');
|
||||
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||
await createDataSource({ type: 'prometheus', name: DATASOURCE_NAME });
|
||||
await variableFlowToQueryEditor(page, selectors, DATASOURCE_NAME, 'metrics', 'Metrics');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
async function selectOption(page, option) {
|
||||
async function selectOption(page: Page, option: string) {
|
||||
const optionElement = page.getByRole('option', { name: option });
|
||||
await expect(optionElement).toBeVisible();
|
||||
await optionElement.click();
|
||||
|
@ -1,27 +1,21 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
import longTraceResponse from '../fixtures/long-trace-response.json';
|
||||
|
||||
// this test requires a larger viewport
|
||||
test.use({
|
||||
viewport: { width: 1280, height: 1080 },
|
||||
});
|
||||
|
||||
// TODO for some reason, this test gives "connection refused" errors in CI
|
||||
test.describe.skip(
|
||||
test.describe(
|
||||
'Trace view',
|
||||
{
|
||||
tag: ['@various'],
|
||||
},
|
||||
() => {
|
||||
test('Can lazy load big traces', async ({ page, selectors }) => {
|
||||
// Load the fixture data
|
||||
const fixturePath = join(__dirname, '../fixtures/long-trace-response.json');
|
||||
const longTraceResponse = JSON.parse(readFileSync(fixturePath, 'utf8'));
|
||||
|
||||
// Mock the API response
|
||||
await page.route('*/**/api/traces/trace', async (route) => {
|
||||
await page.route('**/api/ds/query?ds_type=jaeger*', async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
|
@ -1,155 +0,0 @@
|
||||
import { random } from 'lodash';
|
||||
|
||||
import { e2e } from '../utils';
|
||||
|
||||
const DATASOURCE_ID = 'sandbox-test-datasource';
|
||||
let DATASOURCE_CONNECTION_ID = '';
|
||||
const DATASOURCE_TYPED_NAME = 'SandboxDatasourceInstance';
|
||||
|
||||
// Skipping due to flakiness/race conditions with same old arch test e2e/various-suite/frontend-sandbox-datasource.spec.ts
|
||||
describe('Datasource sandbox', () => {
|
||||
before(() => {
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'), true);
|
||||
|
||||
e2e.pages.AddDataSource.visit();
|
||||
e2e.pages.AddDataSource.dataSourcePluginsV2('Sandbox datasource test plugin')
|
||||
.scrollIntoView()
|
||||
.should('be.visible') // prevents flakiness
|
||||
.click();
|
||||
e2e.pages.DataSource.name().clear();
|
||||
e2e.pages.DataSource.name().type(DATASOURCE_TYPED_NAME);
|
||||
e2e.pages.DataSource.saveAndTest().click();
|
||||
cy.url().then((url) => {
|
||||
const split = url.split('/');
|
||||
DATASOURCE_CONNECTION_ID = split[split.length - 1];
|
||||
});
|
||||
});
|
||||
beforeEach(() => {
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'), true);
|
||||
});
|
||||
|
||||
describe('Config Editor', () => {
|
||||
describe('Sandbox disabled', () => {
|
||||
beforeEach(() => {
|
||||
cy.window().then((win) => {
|
||||
win.localStorage.setItem('grafana.featureToggles', 'pluginsFrontendSandbox=0');
|
||||
});
|
||||
});
|
||||
it('Should not render a sandbox wrapper around the datasource config editor', () => {
|
||||
e2e.pages.EditDataSource.visit(DATASOURCE_CONNECTION_ID);
|
||||
cy.wait(300); // wait to prevent false positives because cypress checks too fast
|
||||
cy.get(`div[data-plugin-sandbox="${DATASOURCE_ID}"]`).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sandbox enabled', () => {
|
||||
beforeEach(() => {
|
||||
cy.window().then((win) => {
|
||||
win.localStorage.setItem('grafana.featureToggles', 'pluginsFrontendSandbox=1');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should render a sandbox wrapper around the datasource config editor', () => {
|
||||
e2e.pages.EditDataSource.visit(DATASOURCE_CONNECTION_ID);
|
||||
cy.get(`div[data-plugin-sandbox="${DATASOURCE_ID}"]`).should('exist');
|
||||
});
|
||||
|
||||
it('Should store values in jsonData and secureJsonData correctly', () => {
|
||||
e2e.pages.EditDataSource.visit(DATASOURCE_CONNECTION_ID);
|
||||
|
||||
const valueToStore = 'test' + random(100);
|
||||
|
||||
cy.get('[data-testid="sandbox-config-editor-query-input"]').should('not.be.disabled');
|
||||
cy.get('[data-testid="sandbox-config-editor-query-input"]').type(valueToStore);
|
||||
cy.get('[data-testid="sandbox-config-editor-query-input"]').should('have.value', valueToStore);
|
||||
|
||||
e2e.pages.DataSource.saveAndTest().click();
|
||||
e2e.pages.DataSource.alert().should('exist').contains('Sandbox Success', {});
|
||||
|
||||
// validate the value was stored
|
||||
e2e.pages.EditDataSource.visit(DATASOURCE_CONNECTION_ID);
|
||||
cy.get('[data-testid="sandbox-config-editor-query-input"]').should('not.be.disabled');
|
||||
cy.get('[data-testid="sandbox-config-editor-query-input"]').should('have.value', valueToStore);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Explore Page', () => {
|
||||
describe('Sandbox disabled', () => {
|
||||
beforeEach(() => {
|
||||
cy.window().then((win) => {
|
||||
win.localStorage.setItem('grafana.featureToggles', 'pluginsFrontendSandbox=0');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not wrap the query editor in a sandbox wrapper', () => {
|
||||
e2e.pages.Explore.visit();
|
||||
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
||||
cy.contains(DATASOURCE_TYPED_NAME).scrollIntoView().should('be.visible').click();
|
||||
|
||||
// make sure the datasource was correctly selected and rendered
|
||||
e2e.components.Breadcrumbs.breadcrumb(DATASOURCE_TYPED_NAME).should('be.visible');
|
||||
|
||||
cy.wait(300); // wait to prevent false positives because cypress checks too fast
|
||||
cy.get(`div[data-plugin-sandbox="${DATASOURCE_ID}"]`).should('not.exist');
|
||||
});
|
||||
|
||||
it('Should accept values when typed', () => {
|
||||
e2e.pages.Explore.visit();
|
||||
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
||||
cy.contains(DATASOURCE_TYPED_NAME).scrollIntoView().should('be.visible').click();
|
||||
// make sure the datasource was correctly selected and rendered
|
||||
e2e.components.Breadcrumbs.breadcrumb(DATASOURCE_TYPED_NAME).should('be.visible');
|
||||
|
||||
const valueToType = 'test' + random(100);
|
||||
|
||||
cy.get('[data-testid="sandbox-query-editor-query-input"]').should('not.be.disabled');
|
||||
cy.get('[data-testid="sandbox-query-editor-query-input"]').type(valueToType);
|
||||
cy.get('[data-testid="sandbox-query-editor-query-input"]').should('have.value', valueToType);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sandbox enabled', () => {
|
||||
beforeEach(() => {
|
||||
cy.window().then((win) => {
|
||||
win.localStorage.setItem('grafana.featureToggles', 'pluginsFrontendSandbox=1');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should wrap the query editor in a sandbox wrapper', () => {
|
||||
e2e.pages.Explore.visit();
|
||||
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
||||
cy.contains(DATASOURCE_TYPED_NAME).scrollIntoView().should('be.visible').click();
|
||||
// make sure the datasource was correctly selected and rendered
|
||||
e2e.components.Breadcrumbs.breadcrumb(DATASOURCE_TYPED_NAME).should('be.visible');
|
||||
|
||||
cy.get(`div[data-plugin-sandbox="${DATASOURCE_ID}"]`).should('exist');
|
||||
});
|
||||
|
||||
it('Should accept values when typed', () => {
|
||||
e2e.pages.Explore.visit();
|
||||
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
||||
cy.contains(DATASOURCE_TYPED_NAME).scrollIntoView().should('be.visible').click();
|
||||
// make sure the datasource was correctly selected and rendered
|
||||
e2e.components.Breadcrumbs.breadcrumb(DATASOURCE_TYPED_NAME).should('be.visible');
|
||||
|
||||
const valueToType = 'test' + random(100);
|
||||
|
||||
cy.get('[data-testid="sandbox-query-editor-query-input"]').should('not.be.disabled');
|
||||
cy.get('[data-testid="sandbox-query-editor-query-input"]').type(valueToType);
|
||||
cy.get('[data-testid="sandbox-query-editor-query-input"]').should('have.value', valueToType);
|
||||
|
||||
// typing the query editor should reflect in the url
|
||||
cy.url().should('include', valueToType);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
e2e.flows.revertAllChanges();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.clearCookies();
|
||||
});
|
||||
});
|
@ -1,62 +0,0 @@
|
||||
import { e2e } from '../../utils';
|
||||
|
||||
/**
|
||||
* Create a Prom data source
|
||||
*/
|
||||
export function createPromDS(dataSourceID: string, name: string): void {
|
||||
// login
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'), true);
|
||||
|
||||
// select the prometheus DS
|
||||
e2e.pages.AddDataSource.visit();
|
||||
e2e.pages.AddDataSource.dataSourcePluginsV2(dataSourceID)
|
||||
.scrollIntoView()
|
||||
.should('be.visible') // prevents flakiness
|
||||
.click();
|
||||
|
||||
// add url for DS to save without error
|
||||
e2e.components.DataSource.Prometheus.configPage.connectionSettings().type('http://prom-url:9090');
|
||||
|
||||
// name the DS
|
||||
e2e.pages.DataSource.name().clear();
|
||||
e2e.pages.DataSource.name().type(name);
|
||||
e2e.pages.DataSource.saveAndTest().click();
|
||||
}
|
||||
|
||||
export function getResources() {
|
||||
cy.intercept(/__name__/g, metricResponse);
|
||||
|
||||
cy.intercept(/metadata/g, metadataResponse);
|
||||
|
||||
cy.intercept(/labels/g, labelsResponse);
|
||||
}
|
||||
|
||||
const metricResponse = {
|
||||
status: 'success',
|
||||
data: ['metric1', 'metric2'],
|
||||
};
|
||||
|
||||
const metadataResponse = {
|
||||
status: 'success',
|
||||
data: {
|
||||
metric1: [
|
||||
{
|
||||
type: 'counter',
|
||||
help: 'metric1 help',
|
||||
unit: '',
|
||||
},
|
||||
],
|
||||
metric2: [
|
||||
{
|
||||
type: 'counter',
|
||||
help: 'metric2 help',
|
||||
unit: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const labelsResponse = {
|
||||
status: 'success',
|
||||
data: ['__name__', 'action', 'active', 'backend'],
|
||||
};
|
@ -1,353 +0,0 @@
|
||||
import { e2e } from '../utils';
|
||||
|
||||
describe('Migrate to Cloud (On-prem)', () => {
|
||||
// Here we are mostly testing the UI flow and can do interesting things with the backend responses to see how the UI behaves.
|
||||
describe('with mocked calls to the API backend', () => {
|
||||
afterEach(() => {
|
||||
cy.get('[data-testid="migrate-to-cloud-summary-disconnect-button"]').should('be.visible').click();
|
||||
});
|
||||
|
||||
const SESSION_UID = 'fehq6hqd246iox';
|
||||
const SNAPSHOT_UID1 = 'cehq6vdjqbqbkx';
|
||||
|
||||
const SNAPSHOT_RESULTS = [
|
||||
{ name: 'FolderA', type: 'FOLDER', refId: 'ref-id-folder-a', parentName: 'General' },
|
||||
{ name: 'FolderB', type: 'FOLDER', refId: 'ref-id-folder-b', parentName: 'General' },
|
||||
{ name: 'Prometheus', type: 'DATASOURCE', refId: 'prometheus' },
|
||||
{ name: 'Postgres', type: 'DATASOURCE', refId: 'postgres' },
|
||||
{ name: 'Loki', type: 'DATASOURCE', refId: 'loki' },
|
||||
{ name: 'Alert Rule A', type: 'ALERT_RULE', refId: 'alert-rule-a', parentName: 'FolderA' },
|
||||
{ name: 'Alert Rule B', type: 'ALERT_RULE', refId: 'alert-rule-b', parentName: 'FolderB' },
|
||||
{ name: 'Alert Rule C', type: 'ALERT_RULE', refId: 'alert-rule-c', parentName: 'FolderB' },
|
||||
{ name: 'Alert Rule Group A', type: 'ALERT_RULE_GROUP', refId: 'alert-rule-group-a', parentName: 'FolderA' },
|
||||
{ name: 'Alert Rule Group B', type: 'ALERT_RULE_GROUP', refId: 'alert-rule-group-b', parentName: 'FolderB' },
|
||||
{ name: 'Contact Point A', type: 'CONTACT_POINT', refId: 'contact-point-a' },
|
||||
{ name: 'Contact Point B', type: 'CONTACT_POINT', refId: 'contact-point-b' },
|
||||
{ name: 'Contact Point C', type: 'CONTACT_POINT', refId: 'contact-point-c' },
|
||||
{ name: 'Notification Policy A', type: 'NOTIFICATION_POLICY', refId: 'notification-policy-a' },
|
||||
{ name: 'Notification Template A', type: 'NOTIFICATION_TEMPLATE', refId: 'notification-template-a' },
|
||||
{ name: 'Notification Template B', type: 'NOTIFICATION_TEMPLATE', refId: 'notification-template-b' },
|
||||
{ name: 'Notification Template C', type: 'NOTIFICATION_TEMPLATE', refId: 'notification-template-c' },
|
||||
{ name: 'Plugin A', type: 'PLUGIN', refId: 'plugin-a' },
|
||||
{ name: 'Plugin B', type: 'PLUGIN', refId: 'plugin-b' },
|
||||
{ name: 'Plugin C', type: 'PLUGIN', refId: 'plugin-c' },
|
||||
{ name: 'Mute Timing A', type: 'MUTE_TIMING', refId: 'mute-timing-a' },
|
||||
{ name: 'Mute Timing B', type: 'MUTE_TIMING', refId: 'mute-timing-b' },
|
||||
];
|
||||
|
||||
const MIGRATION_SESSION = {
|
||||
uid: SESSION_UID,
|
||||
slug: 'test-slug',
|
||||
created: '2025-04-02T21:36:08+02:00',
|
||||
updated: '2025-04-02T21:36:08+02:00',
|
||||
};
|
||||
|
||||
const STATS = {
|
||||
types: SNAPSHOT_RESULTS.reduce(
|
||||
(acc, r) => {
|
||||
acc[r.type] = (acc[r.type] || 0) + 1;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>
|
||||
),
|
||||
statuses: {
|
||||
PENDING: SNAPSHOT_RESULTS.length,
|
||||
},
|
||||
total: SNAPSHOT_RESULTS.length,
|
||||
};
|
||||
|
||||
it('creates and uploads a snapshot sucessfully', () => {
|
||||
// Login using the UI.
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
||||
|
||||
// Visit the migrate to cloud onprem page.
|
||||
e2e.pages.MigrateToCloud.visit();
|
||||
|
||||
// Open the connect modal and enter the token.
|
||||
cy.get('[data-testid="migrate-to-cloud-connect-session-modal-button"]').should('be.visible').click();
|
||||
|
||||
cy.get('[data-testid="migrate-to-cloud-connect-session-modal-token-input"]')
|
||||
.should('be.visible')
|
||||
.focus()
|
||||
.type('test');
|
||||
|
||||
cy.intercept('POST', '/api/cloudmigration/migration', {
|
||||
statusCode: 200,
|
||||
body: MIGRATION_SESSION,
|
||||
}).as('createMigrationToken');
|
||||
|
||||
cy.intercept('GET', '/api/cloudmigration/migration', {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
sessions: [MIGRATION_SESSION],
|
||||
},
|
||||
}).as('getMigrationSessionList');
|
||||
|
||||
cy.intercept('GET', `/api/cloudmigration/migration/${SESSION_UID}/snapshots?page=1&limit=1*`, {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
snapshots: [],
|
||||
},
|
||||
}).as('getSnapshotListInitial');
|
||||
|
||||
// Click the connect button to create the token.
|
||||
cy.get('[data-testid="migrate-to-cloud-connect-session-modal-connect-button"]').should('be.visible').click();
|
||||
|
||||
// Wait for the token to be created and the migration session list to be fetched to kickstart the UI state machine.
|
||||
cy.wait(['@createMigrationToken', '@getMigrationSessionList', '@getSnapshotListInitial']);
|
||||
|
||||
// Check the 'Include all' resources checkbox.
|
||||
cy.get('[data-testid="migrate-to-cloud-configure-snapshot-checkbox-resource-include-all"]')
|
||||
.check({ force: true })
|
||||
.should('be.checked');
|
||||
|
||||
// And validate that all resources are indeed checked.
|
||||
for (const resourceType of [
|
||||
'alert_rule',
|
||||
'alert_rule_group',
|
||||
'contact_point',
|
||||
'dashboard',
|
||||
'datasource',
|
||||
'folder',
|
||||
'library_element',
|
||||
'mute_timing',
|
||||
'notification_policy',
|
||||
'notification_template',
|
||||
'plugin',
|
||||
]) {
|
||||
cy.get(
|
||||
`[data-testid="migrate-to-cloud-configure-snapshot-checkbox-resource-${resourceType.toLowerCase()}"]`
|
||||
).should('be.checked');
|
||||
}
|
||||
|
||||
// Remove one of the resources that has dependencies.
|
||||
// Mute Timings are dependencies of Alert Rules, which are dependencies of Alert Rule Groups.
|
||||
cy.get(`[data-testid="migrate-to-cloud-configure-snapshot-checkbox-resource-mute_timing"]`)
|
||||
.uncheck({ force: true })
|
||||
.should('not.be.checked');
|
||||
|
||||
// Validate that those resources are now unchecked.
|
||||
for (const resourceType of ['alert_rule', 'alert_rule_group', 'include-all']) {
|
||||
cy.get(
|
||||
`[data-testid="migrate-to-cloud-configure-snapshot-checkbox-resource-${resourceType.toLowerCase()}"]`
|
||||
).should('not.be.checked');
|
||||
}
|
||||
|
||||
// Check everything again because we can.
|
||||
cy.get('[data-testid="migrate-to-cloud-configure-snapshot-checkbox-resource-include-all"]')
|
||||
.check({ force: true })
|
||||
.should('be.checked');
|
||||
|
||||
// Validate that those resources are now checked again.
|
||||
for (const resourceType of ['alert_rule', 'alert_rule_group', 'mute_timing']) {
|
||||
cy.get(
|
||||
`[data-testid="migrate-to-cloud-configure-snapshot-checkbox-resource-${resourceType.toLowerCase()}"]`
|
||||
).should('be.checked');
|
||||
}
|
||||
|
||||
cy.intercept('POST', `/api/cloudmigration/migration/${SESSION_UID}/snapshot`, {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
uid: SNAPSHOT_UID1,
|
||||
},
|
||||
}).as('createSnapshot');
|
||||
|
||||
cy.intercept('GET', `/api/cloudmigration/migration/${SESSION_UID}/snapshots?page=1&limit=1*`, {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
snapshots: [
|
||||
{
|
||||
uid: SNAPSHOT_UID1,
|
||||
sessionUid: SESSION_UID,
|
||||
status: 'CREATING',
|
||||
created: '2025-04-02T21:40:23+02:00',
|
||||
finished: '0001-01-01T00:00:00Z',
|
||||
},
|
||||
],
|
||||
},
|
||||
}).as('getSnapshotListCreating');
|
||||
|
||||
let getSnapshotCalled = false;
|
||||
cy.intercept(
|
||||
'GET',
|
||||
`/api/cloudmigration/migration/${SESSION_UID}/snapshot/${SNAPSHOT_UID1}?resultPage=1&resultLimit=50*`,
|
||||
(req) => {
|
||||
if (!getSnapshotCalled) {
|
||||
getSnapshotCalled = true;
|
||||
req.reply((res) => {
|
||||
res.send({
|
||||
statusCode: 200,
|
||||
body: {
|
||||
uid: SNAPSHOT_UID1,
|
||||
sessionUid: SESSION_UID,
|
||||
status: 'CREATING',
|
||||
created: '2025-04-02T21:40:23+02:00',
|
||||
finished: '0001-01-01T00:00:00Z',
|
||||
results: [],
|
||||
stats: {
|
||||
types: {},
|
||||
statuses: {},
|
||||
total: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
} else {
|
||||
req.reply((res) => {
|
||||
res.send({
|
||||
statusCode: 200,
|
||||
body: {
|
||||
uid: SNAPSHOT_UID1,
|
||||
sessionUid: SESSION_UID,
|
||||
status: 'PENDING_UPLOAD',
|
||||
created: '2025-04-02T21:40:23+02:00',
|
||||
finished: '0001-01-01T00:00:00Z',
|
||||
results: SNAPSHOT_RESULTS.map((r) => ({ ...r, status: 'PENDING' })),
|
||||
stats: STATS,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
).as('getSnapshot');
|
||||
|
||||
// Build the snapshot.
|
||||
cy.get('[data-testid="migrate-to-cloud-configure-snapshot-build-snapshot-button"]').should('be.visible').click();
|
||||
|
||||
// Wait for the snapshot to be created. Simulate it going from INITIALIZING to PENDING_UPLOAD.
|
||||
cy.wait(['@createSnapshot', '@getSnapshotListCreating', '@getSnapshot']);
|
||||
|
||||
cy.intercept('POST', `/api/cloudmigration/migration/${SESSION_UID}/snapshot/${SNAPSHOT_UID1}/upload`, {
|
||||
statusCode: 200,
|
||||
}).as('uploadSnapshot');
|
||||
|
||||
// Upload the snapshot.
|
||||
cy.get('[data-testid="migrate-to-cloud-summary-upload-snapshot-button"]')
|
||||
.should('be.visible')
|
||||
.wait(2000)
|
||||
.focus()
|
||||
.trigger('click', { force: true, waitForAnimations: true });
|
||||
|
||||
cy.intercept('GET', `/api/cloudmigration/migration/${SESSION_UID}/snapshots?page=1&limit=1*`, {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
snapshots: [
|
||||
{
|
||||
uid: SNAPSHOT_UID1,
|
||||
sessionUid: SESSION_UID,
|
||||
status: 'UPLOADING',
|
||||
created: '2025-04-02T21:40:23+02:00',
|
||||
finished: '0001-01-01T00:00:00Z',
|
||||
},
|
||||
],
|
||||
},
|
||||
}).as('getSnapshotListUploading');
|
||||
|
||||
// Simulate the snapshot being uploaded, the frontend will keep polling until the snapshot is either finished or errored.
|
||||
let getSnapshotUploadingCalls = 0;
|
||||
cy.intercept(
|
||||
'GET',
|
||||
`/api/cloudmigration/migration/${SESSION_UID}/snapshot/${SNAPSHOT_UID1}?resultPage=1&resultLimit=50*`,
|
||||
(req) => {
|
||||
req.reply((res) => {
|
||||
if (getSnapshotUploadingCalls <= 1) {
|
||||
res.send({
|
||||
statusCode: 200,
|
||||
body: {
|
||||
uid: SNAPSHOT_UID1,
|
||||
sessionUid: SESSION_UID,
|
||||
status: getSnapshotUploadingCalls === 1 ? 'PROCESSING' : 'UPLOADING',
|
||||
created: '2025-04-02T21:40:23+02:00',
|
||||
finished: '0001-01-01T00:00:00Z',
|
||||
results: SNAPSHOT_RESULTS.map((r) => ({ ...r, status: 'PENDING' })),
|
||||
stats: STATS,
|
||||
},
|
||||
});
|
||||
getSnapshotUploadingCalls++;
|
||||
} else {
|
||||
res.send({
|
||||
statusCode: 200,
|
||||
body: {
|
||||
uid: SNAPSHOT_UID1,
|
||||
sessionUid: SESSION_UID,
|
||||
status: 'FINISHED',
|
||||
created: '2025-03-27T12:00:00Z',
|
||||
finished: '2025-03-27T12:00:00Z',
|
||||
results: SNAPSHOT_RESULTS.map((r) => ({ ...r, status: 'OK' })),
|
||||
stats: {
|
||||
types: STATS.types,
|
||||
statuses: SNAPSHOT_RESULTS.reduce(
|
||||
(acc, r) => {
|
||||
const status = (r as { status?: string }).status || 'UNKNOWN';
|
||||
acc[status] = (acc[status] || 0) + 1;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>
|
||||
),
|
||||
total: SNAPSHOT_RESULTS.length,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
).as('getSnapshotUploading');
|
||||
|
||||
// Wait for the request to kickstart the upload and then wait until it is finished.
|
||||
cy.wait(['@uploadSnapshot', '@getSnapshotListUploading', '@getSnapshotUploading']);
|
||||
|
||||
// At least some of the items are marked with "Uploaded to cloud" status.
|
||||
cy.contains('Uploaded to cloud').should('be.visible');
|
||||
|
||||
// We can now reconfigure the snapshot.
|
||||
cy.get('[data-testid="migrate-to-cloud-summary-reconfigure-snapshot-button"]').should('be.visible').click();
|
||||
|
||||
// Check the 'Include all' resources checkbox.
|
||||
cy.get('[data-testid="migrate-to-cloud-configure-snapshot-checkbox-resource-include-all"]')
|
||||
.check({ force: true })
|
||||
.should('be.checked');
|
||||
});
|
||||
});
|
||||
|
||||
// Here we are doing a more black box testing of the migration flow, without explicitly mocking the API calls,
|
||||
// but we instead rely on the `[cloud_migration] developer_mode = true` to be set in the `custom.ini` file,
|
||||
// which will make the service use in-memory fake implementations of 3rdparty dependencies, but we'll still
|
||||
// use the real API endpoints, database and business logic.
|
||||
describe('with a fake GMS backend implementation', () => {
|
||||
afterEach(() => {
|
||||
cy.get('[data-testid="migrate-to-cloud-summary-disconnect-button"]').should('be.visible').click();
|
||||
});
|
||||
|
||||
// Manually crafted base64 token for testing, does not contain any sensitive data.
|
||||
const TEST_TOKEN =
|
||||
'eyJUb2tlbiI6ImdsY19kZXZfZXlKdklqb2lNVEl6TkNJc0ltNGlPaUpuY21GbVlXNWhMV05zYjNWa0xXMXBaM0poZEdsdmJuTXRNVEl6TkNJc0ltc2lPaUowWlhOMElpd2liU0k2ZXlKeUlqb2laR1YyTFhWekxXTmxiblJ5WVd3aWZYMEsiLCJJbnN0YW5jZSI6eyJTdGFja0lEIjoxMjM0LCJTbHVnIjoidGVzdC1zbHVnIiwiUmVnaW9uU2x1ZyI6ImRldi11cy1jZW50cmFsIiwiQ2x1c3RlclNsdWciOiJkZXYtdXMtY2VudHJhbC0wIn19Cg==';
|
||||
|
||||
it('creates a snapshot sucessfully', () => {
|
||||
// Login using the UI.
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
||||
|
||||
// Visit the migrate to cloud onprem page.
|
||||
e2e.pages.MigrateToCloud.visit();
|
||||
|
||||
// Open the connect modal and enter the token.
|
||||
cy.get('[data-testid="migrate-to-cloud-connect-session-modal-button"]').should('be.visible').click();
|
||||
|
||||
cy.get('[data-testid="migrate-to-cloud-connect-session-modal-token-input"]')
|
||||
.should('be.visible')
|
||||
.focus()
|
||||
.type(TEST_TOKEN);
|
||||
|
||||
// Click the connect button to create the token.
|
||||
cy.get('[data-testid="migrate-to-cloud-connect-session-modal-connect-button"]').should('be.visible').click();
|
||||
|
||||
// Build the snapshot.
|
||||
cy.get('[data-testid="migrate-to-cloud-configure-snapshot-build-snapshot-button"]').should('be.visible').click();
|
||||
|
||||
// And the rebuild button should be visible.
|
||||
cy.get('[data-testid="migrate-to-cloud-summary-reconfigure-snapshot-button"]').should('be.visible');
|
||||
|
||||
// We don't upload the snapshot yet because we need to create a mock server to validate the uploaded items,
|
||||
// similarly to what the SMTP (tester) server does.
|
||||
});
|
||||
});
|
||||
});
|
@ -1,111 +0,0 @@
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { e2e } from '../utils';
|
||||
|
||||
const DATASOURCE_ID = 'Prometheus';
|
||||
const DATASOURCE_TYPED_NAME = 'PrometheusDatasourceInstance';
|
||||
|
||||
describe('Prometheus config', () => {
|
||||
beforeEach(() => {
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'), true);
|
||||
|
||||
e2e.pages.AddDataSource.visit();
|
||||
e2e.pages.AddDataSource.dataSourcePluginsV2(DATASOURCE_ID)
|
||||
.scrollIntoView()
|
||||
.should('be.visible') // prevents flakiness
|
||||
.click({ force: true });
|
||||
});
|
||||
|
||||
it(`should have the following components:
|
||||
connection settings
|
||||
managed alerts
|
||||
scrape interval
|
||||
query timeout
|
||||
default editor
|
||||
disable metric lookup
|
||||
prometheus type
|
||||
cache level
|
||||
incremental querying
|
||||
disable recording rules
|
||||
custom query parameters
|
||||
http method
|
||||
`, () => {
|
||||
// connection settings
|
||||
e2e.components.DataSource.Prometheus.configPage.connectionSettings().should('be.visible');
|
||||
// managed alerts
|
||||
cy.get(`#${selectors.components.DataSource.Prometheus.configPage.manageAlerts}`).scrollIntoView().should('exist');
|
||||
// scrape interval
|
||||
e2e.components.DataSource.Prometheus.configPage.scrapeInterval().scrollIntoView().should('exist');
|
||||
// query timeout
|
||||
e2e.components.DataSource.Prometheus.configPage.queryTimeout().scrollIntoView().should('exist');
|
||||
// default editor
|
||||
e2e.components.DataSource.Prometheus.configPage.defaultEditor().scrollIntoView().should('exist');
|
||||
// disable metric lookup
|
||||
cy.get(`#${selectors.components.DataSource.Prometheus.configPage.disableMetricLookup}`)
|
||||
.scrollIntoView()
|
||||
.should('exist');
|
||||
// prometheus type
|
||||
e2e.components.DataSource.Prometheus.configPage.prometheusType().scrollIntoView().should('exist');
|
||||
// cache level
|
||||
e2e.components.DataSource.Prometheus.configPage.cacheLevel().scrollIntoView().should('exist');
|
||||
// incremental querying
|
||||
cy.get(`#${selectors.components.DataSource.Prometheus.configPage.incrementalQuerying}`)
|
||||
.scrollIntoView()
|
||||
.should('exist');
|
||||
// disable recording rules
|
||||
cy.get(`#${selectors.components.DataSource.Prometheus.configPage.disableRecordingRules}`)
|
||||
.scrollIntoView()
|
||||
.should('exist');
|
||||
// custom query parameters
|
||||
e2e.components.DataSource.Prometheus.configPage.customQueryParameters().scrollIntoView().should('exist');
|
||||
// http method
|
||||
e2e.components.DataSource.Prometheus.configPage.httpMethod().scrollIntoView().should('exist');
|
||||
});
|
||||
|
||||
it('should save the default editor when navigating to explore', () => {
|
||||
e2e.components.DataSource.Prometheus.configPage.defaultEditor().scrollIntoView().should('exist').click();
|
||||
|
||||
selectOption('Builder');
|
||||
|
||||
e2e.components.DataSource.Prometheus.configPage.connectionSettings().type('http://prom-url:9090');
|
||||
|
||||
e2e.pages.DataSource.name().clear();
|
||||
e2e.pages.DataSource.name().type(DATASOURCE_TYPED_NAME);
|
||||
e2e.pages.DataSource.saveAndTest().click();
|
||||
|
||||
e2e.pages.Explore.visit();
|
||||
|
||||
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
||||
|
||||
e2e.components.DataSourcePicker.container().type(`${DATASOURCE_TYPED_NAME}{enter}`);
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.builder.metricSelect().should('exist');
|
||||
});
|
||||
|
||||
it('should allow a user to add the version when the Prom type is selected', () => {
|
||||
e2e.components.DataSource.Prometheus.configPage.prometheusType().scrollIntoView().should('exist').click();
|
||||
|
||||
selectOption('Prometheus');
|
||||
|
||||
e2e.components.DataSource.Prometheus.configPage.prometheusVersion().scrollIntoView().should('exist');
|
||||
});
|
||||
|
||||
it('should have a cache level component', () => {
|
||||
e2e.components.DataSource.Prometheus.configPage.cacheLevel().scrollIntoView().should('exist');
|
||||
});
|
||||
|
||||
it('should allow a user to select a query overlap window when incremental querying is selected', () => {
|
||||
cy.get(`#${selectors.components.DataSource.Prometheus.configPage.incrementalQuerying}`)
|
||||
.scrollIntoView()
|
||||
.should('exist')
|
||||
.check({ force: true });
|
||||
|
||||
e2e.components.DataSource.Prometheus.configPage.queryOverlapWindow().scrollIntoView().should('exist');
|
||||
});
|
||||
|
||||
// exemplars tested in exemplar.spec
|
||||
});
|
||||
|
||||
export function selectOption(option: string) {
|
||||
e2e.components.Select.option().contains(option).should('be.visible').click();
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
import { e2e } from '../utils';
|
||||
|
||||
import { getResources } from './helpers/prometheus-helpers';
|
||||
|
||||
const DATASOURCE_ID = 'Prometheus';
|
||||
|
||||
type editorType = 'Code' | 'Builder';
|
||||
|
||||
/**
|
||||
* Login, create and save a Prometheus data source, navigate to code or builder
|
||||
*
|
||||
* @param editorType 'Code' or 'Builder'
|
||||
*/
|
||||
function navigateToEditor(editorType: editorType, name: string): void {
|
||||
// login
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'), true);
|
||||
|
||||
// select the prometheus DS
|
||||
e2e.pages.AddDataSource.visit();
|
||||
e2e.pages.AddDataSource.dataSourcePluginsV2(DATASOURCE_ID)
|
||||
.scrollIntoView()
|
||||
.should('be.visible') // prevents flakiness
|
||||
.click();
|
||||
|
||||
// choose default editor
|
||||
e2e.components.DataSource.Prometheus.configPage.defaultEditor().scrollIntoView().should('exist').click();
|
||||
selectOption(editorType);
|
||||
|
||||
// add url for DS to save without error
|
||||
e2e.components.DataSource.Prometheus.configPage.connectionSettings().type('http://prom-url:9090');
|
||||
|
||||
// name the DS
|
||||
e2e.pages.DataSource.name().clear();
|
||||
e2e.pages.DataSource.name().type(name);
|
||||
e2e.pages.DataSource.saveAndTest().click();
|
||||
|
||||
// visit explore
|
||||
e2e.pages.Explore.visit();
|
||||
|
||||
// choose the right DS
|
||||
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
||||
cy.contains(name).scrollIntoView().should('be.visible').click();
|
||||
}
|
||||
// Skipping due to flakiness/race conditions with same old arch test e2e/various-suite/prometheus-editor.spec.ts
|
||||
describe.skip('Prometheus query editor', () => {
|
||||
it('should have a kickstart component', () => {
|
||||
navigateToEditor('Code', 'prometheus');
|
||||
e2e.components.QueryBuilder.queryPatterns().scrollIntoView().should('exist');
|
||||
});
|
||||
|
||||
it('should have an explain component', () => {
|
||||
navigateToEditor('Code', 'prometheus');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.explain().scrollIntoView().should('exist');
|
||||
});
|
||||
|
||||
it('should have an editor toggle component', () => {
|
||||
navigateToEditor('Code', 'prometheus');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.editorToggle().scrollIntoView().should('exist');
|
||||
});
|
||||
|
||||
it('should have an options component with legend, format, step, type and exemplars', () => {
|
||||
navigateToEditor('Code', 'prometheus');
|
||||
// open options
|
||||
e2e.components.DataSource.Prometheus.queryEditor.options().scrollIntoView().should('exist').click();
|
||||
// check options
|
||||
e2e.components.DataSource.Prometheus.queryEditor.legend().scrollIntoView().should('exist');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.format().scrollIntoView().should('exist');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.step().scrollIntoView().should('exist');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.type().scrollIntoView().should('exist');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.exemplars().scrollIntoView().should('exist');
|
||||
});
|
||||
|
||||
describe('Code editor', () => {
|
||||
it('navigates to the code editor with editor type as code', () => {
|
||||
navigateToEditor('Code', 'prometheusCode');
|
||||
});
|
||||
|
||||
it('navigates to the code editor and opens the metrics browser with metric search, labels, label values, and all components', () => {
|
||||
navigateToEditor('Code', 'prometheusCode');
|
||||
|
||||
getResources();
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.queryField().should('exist');
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser
|
||||
.openButton()
|
||||
.contains('Metrics browser')
|
||||
.click();
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.selectMetric().should('exist');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.labelNamesFilter().should('exist');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.labelValuesFilter().should('exist');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.useQuery().should('exist');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.useAsRateQuery().should('exist');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.validateSelector().should('exist');
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.clear().should('exist');
|
||||
});
|
||||
|
||||
it('selects a metric in the metrics browser and uses the query', () => {
|
||||
navigateToEditor('Code', 'prometheusCode');
|
||||
|
||||
getResources();
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser
|
||||
.openButton()
|
||||
.contains('Metrics browser')
|
||||
.click();
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.selectMetric().should('exist').type('met');
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser
|
||||
.metricList()
|
||||
.should('exist')
|
||||
.contains('metric1')
|
||||
.click();
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.useQuery().should('exist').click();
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.code.queryField().should('exist').contains('metric1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Query builder', () => {
|
||||
it('navigates to the query builder with editor type as code', () => {
|
||||
navigateToEditor('Builder', 'prometheusBuilder');
|
||||
});
|
||||
|
||||
it('the query builder contains metric select, label filters and operations', () => {
|
||||
navigateToEditor('Builder', 'prometheusBuilder');
|
||||
|
||||
getResources();
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.builder.metricSelect().should('exist');
|
||||
e2e.components.QueryBuilder.labelSelect().should('exist');
|
||||
e2e.components.QueryBuilder.matchOperatorSelect().should('exist');
|
||||
e2e.components.QueryBuilder.valueSelect().should('exist');
|
||||
});
|
||||
|
||||
it('can select a metric and provide a hint', () => {
|
||||
navigateToEditor('Builder', 'prometheusBuilder');
|
||||
|
||||
getResources();
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.builder.metricSelect().should('exist').click();
|
||||
|
||||
selectOption('metric1');
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.builder.hints().contains('hint: add rate');
|
||||
});
|
||||
|
||||
it('should have the metrics explorer opened via the metric select', () => {
|
||||
navigateToEditor('Builder', 'prometheusBuilder');
|
||||
|
||||
getResources();
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.builder.metricSelect().should('exist').click();
|
||||
|
||||
selectOption('Metrics explorer');
|
||||
|
||||
e2e.components.DataSource.Prometheus.queryEditor.builder.metricsExplorer().should('exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function selectOption(option: string) {
|
||||
e2e.components.Select.option().contains(option).should('be.visible').click();
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
import { e2e } from '../utils';
|
||||
import { addDashboard } from '../utils/flows';
|
||||
|
||||
import { createPromDS, getResources } from './helpers/prometheus-helpers';
|
||||
|
||||
const DATASOURCE_ID = 'Prometheus';
|
||||
|
||||
const DATASOURCE_NAME = 'prometheusVariableDS';
|
||||
|
||||
/**
|
||||
* Click dashboard settings and then the variables tab
|
||||
*/
|
||||
function navigateToVariables() {
|
||||
e2e.components.NavToolbar.editDashboard.editButton().should('be.visible').click();
|
||||
e2e.components.NavToolbar.editDashboard.settingsButton().should('be.visible').click();
|
||||
e2e.components.Tab.title('Variables').click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin the process of adding a query type variable for a Prometheus data source
|
||||
*
|
||||
* @param variableName the name of the variable as a label of the variable dropdown
|
||||
*/
|
||||
function addPrometheusQueryVariable(variableName: string) {
|
||||
e2e.pages.Dashboard.Settings.Variables.List.addVariableCTAV2().click();
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type(variableName);
|
||||
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
||||
cy.contains(DATASOURCE_NAME).scrollIntoView().should('be.visible').click();
|
||||
|
||||
getResources();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Prometheus variable and navigate to the query editor to check that it is available to use.
|
||||
*
|
||||
* @param variableName name the variable
|
||||
* @param queryType query type of 'Label names', 'Label values', 'Metrics', 'Query result', 'Series query' or 'Classic query'. These types should be imported from the Prometheus library eventually but not now because we are in the process of decoupling the DS from core grafana.
|
||||
*/
|
||||
function variableFlowToQueryEditor(variableName: string, queryType: string) {
|
||||
addDashboard();
|
||||
navigateToVariables();
|
||||
addPrometheusQueryVariable(variableName);
|
||||
|
||||
// select query type
|
||||
e2e.components.DataSource.Prometheus.variableQueryEditor.queryType().click();
|
||||
selectOption(queryType);
|
||||
|
||||
// apply the variable
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.applyButton().click();
|
||||
|
||||
// close to return to dashboard
|
||||
e2e.components.NavToolbar.editDashboard.backToDashboardButton().should('be.visible').click();
|
||||
|
||||
// add visualization
|
||||
e2e.pages.AddDashboard.itemButton('Create new panel button').should('be.visible').click();
|
||||
|
||||
// close the data source picker modal
|
||||
cy.get('[aria-label="Close"]').click();
|
||||
|
||||
// select prom data source from the data source list with the useful data-testid
|
||||
e2e.components.DataSourcePicker.inputV2().click({ force: true }).type(`${DATASOURCE_NAME}{enter}`);
|
||||
|
||||
// confirm the variable exists in the correct input
|
||||
// use the variable query type from the library in the future
|
||||
switch (queryType) {
|
||||
case 'Label names':
|
||||
e2e.components.QueryBuilder.labelSelect().should('exist').click({ force: true });
|
||||
selectOption(`${variableName}`);
|
||||
case 'Label values':
|
||||
e2e.components.QueryBuilder.valueSelect().should('exist').click({ force: true });
|
||||
selectOption(`${variableName}`);
|
||||
case 'Metrics':
|
||||
e2e.components.DataSource.Prometheus.queryEditor.builder.metricSelect().should('exist').click({ force: true });
|
||||
selectOption(`${variableName}`);
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
// Skipping due to race conditions with same old arch test e2e/various-suite/prometheus-variable-editor.spec.ts
|
||||
describe.skip('Prometheus variable query editor', () => {
|
||||
beforeEach(() => {
|
||||
createPromDS(DATASOURCE_ID, DATASOURCE_NAME);
|
||||
});
|
||||
|
||||
it('should navigate to variable query editor', () => {
|
||||
addDashboard();
|
||||
navigateToVariables();
|
||||
});
|
||||
|
||||
it('should select a query type for a Prometheus variable query', () => {
|
||||
addDashboard();
|
||||
navigateToVariables();
|
||||
addPrometheusQueryVariable('labelsVariable');
|
||||
|
||||
// select query type
|
||||
e2e.components.DataSource.Prometheus.variableQueryEditor.queryType().click();
|
||||
|
||||
selectOption('Label names');
|
||||
});
|
||||
|
||||
it('should create a label names variable that is selectable in the label select in query builder', () => {
|
||||
addDashboard();
|
||||
navigateToVariables();
|
||||
variableFlowToQueryEditor('labelnames', 'Label names');
|
||||
});
|
||||
|
||||
it('should create a label values variable that is selectable in the label values select in query builder', () => {
|
||||
addDashboard();
|
||||
navigateToVariables();
|
||||
variableFlowToQueryEditor('labelvalues', 'Label values');
|
||||
});
|
||||
|
||||
it('should create a metric names variable that is selectable in the metric select in query builder', () => {
|
||||
addDashboard();
|
||||
navigateToVariables();
|
||||
variableFlowToQueryEditor('metrics', 'Metrics');
|
||||
});
|
||||
});
|
||||
|
||||
function selectOption(option: string) {
|
||||
e2e.components.Select.option().contains(option).should('be.visible').click();
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
import { e2e } from '../utils';
|
||||
|
||||
describe('Trace view', () => {
|
||||
beforeEach(() => {
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
||||
});
|
||||
|
||||
it('Can lazy load big traces', () => {
|
||||
cy.intercept('POST', '**/api/ds/query*', (req) => {
|
||||
if (!req.url.includes('ds_type=jaeger')) {
|
||||
return;
|
||||
}
|
||||
|
||||
req.reply({ fixture: 'long-trace-response-backend.json' });
|
||||
}).as('longTrace');
|
||||
|
||||
e2e.pages.Explore.visit();
|
||||
|
||||
e2e.components.DataSourcePicker.container().should('be.visible').type('gdev-jaeger{enter}');
|
||||
// Wait for the query editor to be set correctly
|
||||
e2e.components.QueryEditorRows.rows().within(() => {
|
||||
cy.contains('gdev-jaeger').should('be.visible');
|
||||
});
|
||||
|
||||
// type this with 0 delay to prevent flaky tests due to cursor position changing between rerenders
|
||||
e2e.components.QueryField.container().should('be.visible').type('trace', {
|
||||
delay: 0,
|
||||
});
|
||||
// Use shift+enter to execute the query as it's more stable than clicking the execute button
|
||||
e2e.components.QueryField.container().type('{shift+enter}');
|
||||
|
||||
cy.wait('@longTrace');
|
||||
|
||||
e2e.components.TraceViewer.spanBar().should('be.visible');
|
||||
|
||||
e2e.components.TraceViewer.spanBar()
|
||||
.its('length')
|
||||
.then((oldLength) => {
|
||||
e2e.pages.Explore.General.scrollView().children().first().scrollTo('center');
|
||||
|
||||
// After scrolling we should load more spans
|
||||
e2e.components.TraceViewer.spanBar().should(($span) => {
|
||||
expect($span.length).to.be.gt(oldLength);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user