mirror of https://github.com/grafana/grafana.git
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:
parent
454380431d
commit
f4833ee2d8
|
@ -208,8 +208,6 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- suite: various-suite
|
|
||||||
path: e2e/various-suite
|
|
||||||
- suite: various-suite (old arch)
|
- suite: various-suite (old arch)
|
||||||
path: e2e/old-arch/various-suite
|
path: e2e/old-arch/various-suite
|
||||||
flags: --flags="--env dashboardScene=false"
|
flags: --flags="--env dashboardScene=false"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,28 +1,15 @@
|
||||||
import { random } from 'lodash';
|
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';
|
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
|
test.describe(
|
||||||
// TODO: fix and remove skip
|
|
||||||
test.describe.skip(
|
|
||||||
'Datasource sandbox',
|
'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('Config Editor', () => {
|
||||||
test.describe('Sandbox disabled', () => {
|
test.describe('Sandbox disabled', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
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.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}"]`);
|
const sandboxDiv = page.locator(`div[data-plugin-sandbox="${DATASOURCE_ID}"]`);
|
||||||
await expect(sandboxDiv).toBeHidden();
|
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}"]`);
|
const sandboxDiv = page.locator(`div[data-plugin-sandbox="${DATASOURCE_ID}"]`);
|
||||||
await expect(sandboxDiv).toBeVisible();
|
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}`);
|
await page.goto(`/connections/datasources/edit/${DATASOURCE_CONNECTION_ID}`);
|
||||||
|
|
||||||
const valueToStore = 'test' + random(100);
|
const valueToStore = 'test' + random(100);
|
||||||
|
@ -62,10 +80,10 @@ test.describe.skip(
|
||||||
await queryInput.fill(valueToStore);
|
await queryInput.fill(valueToStore);
|
||||||
await expect(queryInput).toHaveValue(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();
|
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).toBeVisible();
|
||||||
await expect(alert).toContainText('Sandbox Success');
|
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');
|
await page.goto('/explore');
|
||||||
|
|
||||||
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
||||||
|
@ -103,12 +134,19 @@ test.describe.skip(
|
||||||
);
|
);
|
||||||
await expect(breadcrumb).toBeVisible();
|
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}"]`);
|
const sandboxDiv = page.locator(`div[data-plugin-sandbox="${DATASOURCE_ID}"]`);
|
||||||
await expect(sandboxDiv).toBeHidden();
|
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');
|
await page.goto('/explore');
|
||||||
|
|
||||||
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
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');
|
await page.goto('/explore');
|
||||||
|
|
||||||
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
||||||
|
@ -164,7 +215,15 @@ test.describe.skip(
|
||||||
await expect(sandboxDiv).toBeVisible();
|
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');
|
await page.goto('/explore');
|
||||||
|
|
||||||
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
const dataSourcePicker = dashboardPage.getByGrafanaSelector(selectors.components.DataSourcePicker.container);
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { test, expect } from '@grafana/plugin-e2e';
|
||||||
test.describe(
|
test.describe(
|
||||||
'Migrate to Cloud (On-prem)',
|
'Migrate to Cloud (On-prem)',
|
||||||
{
|
{
|
||||||
tag: ['@various', '@wip'],
|
tag: ['@various'],
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
test.describe('with mocked calls to the API backend', () => {
|
test.describe('with mocked calls to the API backend', () => {
|
||||||
|
@ -59,8 +59,7 @@ test.describe(
|
||||||
total: SNAPSHOT_RESULTS.length,
|
total: SNAPSHOT_RESULTS.length,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: fix the test. It makes it most of the way through. Probably a network mock issue.
|
test('creates and uploads a snapshot successfully', async ({ page }) => {
|
||||||
test.skip('creates and uploads a snapshot successfully', async ({ page }) => {
|
|
||||||
// Visit the migrate to cloud onprem page
|
// Visit the migrate to cloud onprem page
|
||||||
await page.goto('/admin/migrate-to-cloud');
|
await page.goto('/admin/migrate-to-cloud');
|
||||||
|
|
||||||
|
@ -75,7 +74,7 @@ test.describe(
|
||||||
await tokenInput.fill('test');
|
await tokenInput.fill('test');
|
||||||
|
|
||||||
// Mock API responses
|
// Mock API responses
|
||||||
await page.route(/api\/cloudmigration\/migration/, async (route) => {
|
await page.route(/api\/cloudmigration\/migration$/, async (route) => {
|
||||||
if (route.request().method() === 'POST') {
|
if (route.request().method() === 'POST') {
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
status: 200,
|
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({
|
await route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
|
@ -141,14 +140,14 @@ test.describe(
|
||||||
'migrate-to-cloud-configure-snapshot-checkbox-resource-mute_timing'
|
'migrate-to-cloud-configure-snapshot-checkbox-resource-mute_timing'
|
||||||
);
|
);
|
||||||
await muteTimingCheckbox.uncheck({ force: true });
|
await muteTimingCheckbox.uncheck({ force: true });
|
||||||
await expect(muteTimingCheckbox).not.toBeChecked();
|
await expect(muteTimingCheckbox).toBeChecked({ checked: false });
|
||||||
|
|
||||||
// Validate that those resources are now unchecked
|
// Validate that those resources are now unchecked
|
||||||
for (const resourceType of ['alert_rule', 'alert_rule_group', 'include-all']) {
|
for (const resourceType of ['alert_rule', 'alert_rule_group', 'include-all']) {
|
||||||
const checkbox = page.getByTestId(
|
const checkbox = page.getByTestId(
|
||||||
`migrate-to-cloud-configure-snapshot-checkbox-resource-${resourceType.toLowerCase()}`
|
`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
|
// Check everything again because we can
|
||||||
|
@ -164,7 +163,7 @@ test.describe(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock snapshot creation
|
// 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({
|
await route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: 'application/json',
|
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({
|
await route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
|
@ -194,7 +193,7 @@ test.describe(
|
||||||
|
|
||||||
let getSnapshotCalled = false;
|
let getSnapshotCalled = false;
|
||||||
await page.route(
|
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) => {
|
async (route) => {
|
||||||
if (!getSnapshotCalled) {
|
if (!getSnapshotCalled) {
|
||||||
getSnapshotCalled = true;
|
getSnapshotCalled = true;
|
||||||
|
@ -238,9 +237,12 @@ test.describe(
|
||||||
await expect(buildSnapshotButton).toBeVisible();
|
await expect(buildSnapshotButton).toBeVisible();
|
||||||
await buildSnapshotButton.click();
|
await buildSnapshotButton.click();
|
||||||
|
|
||||||
|
const uploadButton = page.getByTestId('migrate-to-cloud-summary-upload-snapshot-button');
|
||||||
|
await expect(uploadButton).toBeVisible();
|
||||||
|
|
||||||
// Mock upload
|
// Mock upload
|
||||||
const uploadSnapshot = await page.route(
|
await page.route(
|
||||||
/api\/cloudmigration\/migration\/${SESSION_UID}\/snapshot\/${SNAPSHOT_UID1}\/upload/,
|
`api/cloudmigration/migration/${SESSION_UID}/snapshot/${SNAPSHOT_UID1}/upload`,
|
||||||
async (route) => {
|
async (route) => {
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
status: 200,
|
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
|
// Mock uploading status
|
||||||
const getSnapshotListUploading = await page.route(
|
await page.route(`api/cloudmigration/migration/${SESSION_UID}/snapshots?page=1&limit=1*`, async (route) => {
|
||||||
/api\/cloudmigration\/migration\/${SESSION_UID}\/snapshots\?page=1&limit=1/,
|
|
||||||
async (route) => {
|
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
|
@ -276,13 +269,12 @@ test.describe(
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// Simulate the snapshot being uploaded
|
// Simulate the snapshot being uploaded
|
||||||
let getSnapshotUploadingCalls = 0;
|
let getSnapshotUploadingCalls = 0;
|
||||||
const getSnapshotUploading = await page.route(
|
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) => {
|
async (route) => {
|
||||||
if (getSnapshotUploadingCalls <= 1) {
|
if (getSnapshotUploadingCalls <= 1) {
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
|
@ -312,14 +304,11 @@ test.describe(
|
||||||
results: SNAPSHOT_RESULTS.map((r) => ({ ...r, status: 'OK' })),
|
results: SNAPSHOT_RESULTS.map((r) => ({ ...r, status: 'OK' })),
|
||||||
stats: {
|
stats: {
|
||||||
types: STATS.types,
|
types: STATS.types,
|
||||||
statuses: SNAPSHOT_RESULTS.reduce(
|
statuses: SNAPSHOT_RESULTS.reduce<Record<string, number>>((acc, r) => {
|
||||||
(acc, r) => {
|
|
||||||
const status = (r as { status?: string }).status || 'UNKNOWN';
|
const status = (r as { status?: string }).status || 'UNKNOWN';
|
||||||
acc[status] = (acc[status] || 0) + 1;
|
acc[status] = (acc[status] || 0) + 1;
|
||||||
return acc;
|
return acc;
|
||||||
},
|
}, {}),
|
||||||
{} as Record<string, number>
|
|
||||||
),
|
|
||||||
total: SNAPSHOT_RESULTS.length,
|
total: SNAPSHOT_RESULTS.length,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
@ -328,12 +317,12 @@ test.describe(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await uploadSnapshot;
|
// Upload the snapshot
|
||||||
await getSnapshotListUploading;
|
await uploadButton.focus();
|
||||||
await getSnapshotUploading;
|
await uploadButton.click({ force: true });
|
||||||
|
|
||||||
// At least some of the items are marked with "Uploaded to cloud" status
|
// 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
|
// We can now reconfigure the snapshot
|
||||||
const reconfigureButton = page.getByTestId('migrate-to-cloud-summary-reconfigure-snapshot-button');
|
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';
|
import { test, expect } from '@grafana/plugin-e2e';
|
||||||
|
|
||||||
// Todo: Fix datasource creation
|
test.describe(
|
||||||
test.describe.skip(
|
|
||||||
'Prometheus config',
|
'Prometheus config',
|
||||||
{
|
{
|
||||||
tag: ['@various'],
|
tag: ['@various'],
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
const DATASOURCE_ID = 'Prometheus';
|
const DATASOURCE_PREFIX = 'PrometheusConfig';
|
||||||
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();
|
|
||||||
});
|
|
||||||
|
|
||||||
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 ({
|
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,
|
page,
|
||||||
selectors,
|
selectors,
|
||||||
|
createDataSourceConfigPage,
|
||||||
}) => {
|
}) => {
|
||||||
|
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||||
|
const configPage = await createDataSourceConfigPage({
|
||||||
|
type: 'prometheus',
|
||||||
|
name: DATASOURCE_NAME,
|
||||||
|
});
|
||||||
// connection settings
|
// connection settings
|
||||||
const connectionSettings = page.getByLabel(
|
const connectionSettings = page.getByLabel(
|
||||||
selectors.components.DataSource.Prometheus.configPage.connectionSettings
|
selectors.components.DataSource.Prometheus.configPage.connectionSettings
|
||||||
|
@ -33,72 +28,85 @@ test.describe.skip(
|
||||||
|
|
||||||
// managed alerts
|
// managed alerts
|
||||||
const manageAlerts = page.locator(`#${selectors.components.DataSource.Prometheus.configPage.manageAlerts}`);
|
const manageAlerts = page.locator(`#${selectors.components.DataSource.Prometheus.configPage.manageAlerts}`);
|
||||||
await manageAlerts.scrollIntoViewIfNeeded();
|
|
||||||
await expect(manageAlerts).toBeVisible();
|
await expect(manageAlerts).toBeVisible();
|
||||||
|
|
||||||
// scrape interval
|
// scrape interval
|
||||||
const scrapeInterval = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.scrapeInterval);
|
const scrapeInterval = configPage.getByGrafanaSelector(
|
||||||
await scrapeInterval.scrollIntoViewIfNeeded();
|
selectors.components.DataSource.Prometheus.configPage.scrapeInterval
|
||||||
|
);
|
||||||
await expect(scrapeInterval).toBeVisible();
|
await expect(scrapeInterval).toBeVisible();
|
||||||
|
|
||||||
// query timeout
|
// query timeout
|
||||||
const queryTimeout = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.queryTimeout);
|
const queryTimeout = configPage.getByGrafanaSelector(
|
||||||
await queryTimeout.scrollIntoViewIfNeeded();
|
selectors.components.DataSource.Prometheus.configPage.queryTimeout
|
||||||
|
);
|
||||||
await expect(queryTimeout).toBeVisible();
|
await expect(queryTimeout).toBeVisible();
|
||||||
|
|
||||||
// default editor
|
// default editor
|
||||||
const defaultEditor = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.defaultEditor);
|
const defaultEditor = configPage.getByGrafanaSelector(
|
||||||
await defaultEditor.scrollIntoViewIfNeeded();
|
selectors.components.DataSource.Prometheus.configPage.defaultEditor
|
||||||
|
);
|
||||||
await expect(defaultEditor).toBeVisible();
|
await expect(defaultEditor).toBeVisible();
|
||||||
|
|
||||||
// disable metric lookup
|
// disable metric lookup
|
||||||
const disableMetricLookup = page.locator(
|
const disableMetricLookup = page.locator(
|
||||||
`#${selectors.components.DataSource.Prometheus.configPage.disableMetricLookup}`
|
`#${selectors.components.DataSource.Prometheus.configPage.disableMetricLookup}`
|
||||||
);
|
);
|
||||||
await disableMetricLookup.scrollIntoViewIfNeeded();
|
|
||||||
await expect(disableMetricLookup).toBeVisible();
|
await expect(disableMetricLookup).toBeVisible();
|
||||||
|
|
||||||
// prometheus type
|
// prometheus type
|
||||||
const prometheusType = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.prometheusType);
|
const prometheusType = configPage.getByGrafanaSelector(
|
||||||
await prometheusType.scrollIntoViewIfNeeded();
|
selectors.components.DataSource.Prometheus.configPage.prometheusType
|
||||||
|
);
|
||||||
await expect(prometheusType).toBeVisible();
|
await expect(prometheusType).toBeVisible();
|
||||||
|
|
||||||
// cache level
|
// cache level
|
||||||
const cacheLevel = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.cacheLevel);
|
const cacheLevel = configPage.getByGrafanaSelector(
|
||||||
await cacheLevel.scrollIntoViewIfNeeded();
|
selectors.components.DataSource.Prometheus.configPage.cacheLevel
|
||||||
|
);
|
||||||
await expect(cacheLevel).toBeVisible();
|
await expect(cacheLevel).toBeVisible();
|
||||||
|
|
||||||
// incremental querying
|
// incremental querying
|
||||||
const incrementalQuerying = page.locator(
|
const incrementalQuerying = page.locator(
|
||||||
`#${selectors.components.DataSource.Prometheus.configPage.incrementalQuerying}`
|
`#${selectors.components.DataSource.Prometheus.configPage.incrementalQuerying}`
|
||||||
);
|
);
|
||||||
await incrementalQuerying.scrollIntoViewIfNeeded();
|
|
||||||
await expect(incrementalQuerying).toBeVisible();
|
await expect(incrementalQuerying).toBeVisible();
|
||||||
|
|
||||||
// disable recording rules
|
// disable recording rules
|
||||||
const disableRecordingRules = page.locator(
|
const disableRecordingRules = page.locator(
|
||||||
`#${selectors.components.DataSource.Prometheus.configPage.disableRecordingRules}`
|
`#${selectors.components.DataSource.Prometheus.configPage.disableRecordingRules}`
|
||||||
);
|
);
|
||||||
await disableRecordingRules.scrollIntoViewIfNeeded();
|
|
||||||
await expect(disableRecordingRules).toBeVisible();
|
await expect(disableRecordingRules).toBeVisible();
|
||||||
|
|
||||||
// custom query parameters
|
// custom query parameters
|
||||||
const customQueryParameters = page.getByTestId(
|
const customQueryParameters = configPage.getByGrafanaSelector(
|
||||||
selectors.components.DataSource.Prometheus.configPage.customQueryParameters
|
selectors.components.DataSource.Prometheus.configPage.customQueryParameters
|
||||||
);
|
);
|
||||||
await customQueryParameters.scrollIntoViewIfNeeded();
|
|
||||||
await expect(customQueryParameters).toBeVisible();
|
await expect(customQueryParameters).toBeVisible();
|
||||||
|
|
||||||
// http method
|
// http method
|
||||||
const httpMethod = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.httpMethod);
|
const httpMethod = configPage.getByGrafanaSelector(
|
||||||
await httpMethod.scrollIntoViewIfNeeded();
|
selectors.components.DataSource.Prometheus.configPage.httpMethod
|
||||||
|
);
|
||||||
await expect(httpMethod).toBeVisible();
|
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
|
// Click on default editor
|
||||||
const defaultEditor = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.defaultEditor);
|
const defaultEditor = configPage.getByGrafanaSelector(
|
||||||
await defaultEditor.scrollIntoViewIfNeeded();
|
selectors.components.DataSource.Prometheus.configPage.defaultEditor
|
||||||
|
);
|
||||||
await expect(defaultEditor).toBeVisible();
|
await expect(defaultEditor).toBeVisible();
|
||||||
await defaultEditor.click();
|
await defaultEditor.click();
|
||||||
|
|
||||||
|
@ -111,17 +119,12 @@ test.describe.skip(
|
||||||
);
|
);
|
||||||
await connectionSettings.fill('http://prom-url:9090');
|
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
|
// Save and test
|
||||||
const saveAndTestButton = page.getByTestId(selectors.pages.DataSource.saveAndTest);
|
const saveAndTestButton = configPage.getByGrafanaSelector(selectors.pages.DataSource.saveAndTest);
|
||||||
await saveAndTestButton.click();
|
await saveAndTestButton.click();
|
||||||
|
|
||||||
// Navigate to explore
|
// Navigate to explore
|
||||||
await page.goto('/explore');
|
await explorePage.goto();
|
||||||
|
|
||||||
// Select the data source
|
// Select the data source
|
||||||
const dataSourcePicker = page.getByTestId(selectors.components.DataSourcePicker.container);
|
const dataSourcePicker = page.getByTestId(selectors.components.DataSourcePicker.container);
|
||||||
|
@ -129,7 +132,7 @@ test.describe.skip(
|
||||||
await dataSourcePicker.click();
|
await dataSourcePicker.click();
|
||||||
|
|
||||||
// Type the data source name and press enter
|
// 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');
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
// Verify the builder metric select is visible
|
// Verify the builder metric select is visible
|
||||||
|
@ -139,10 +142,21 @@ test.describe.skip(
|
||||||
await expect(metricSelect).toBeVisible();
|
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
|
// Click on prometheus type
|
||||||
const prometheusType = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.prometheusType);
|
const prometheusType = configPage.getByGrafanaSelector(
|
||||||
await prometheusType.scrollIntoViewIfNeeded();
|
selectors.components.DataSource.Prometheus.configPage.prometheusType
|
||||||
|
);
|
||||||
await expect(prometheusType).toBeVisible();
|
await expect(prometheusType).toBeVisible();
|
||||||
await prometheusType.click();
|
await prometheusType.click();
|
||||||
|
|
||||||
|
@ -150,43 +164,54 @@ test.describe.skip(
|
||||||
await selectOption(page, 'Prometheus');
|
await selectOption(page, 'Prometheus');
|
||||||
|
|
||||||
// Verify prometheus version is visible
|
// Verify prometheus version is visible
|
||||||
const prometheusVersion = page.getByTestId(
|
const prometheusVersion = configPage.getByGrafanaSelector(
|
||||||
selectors.components.DataSource.Prometheus.configPage.prometheusVersion
|
selectors.components.DataSource.Prometheus.configPage.prometheusVersion
|
||||||
);
|
);
|
||||||
await prometheusVersion.scrollIntoViewIfNeeded();
|
|
||||||
await expect(prometheusVersion).toBeVisible();
|
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
|
// Verify cache level is visible
|
||||||
const cacheLevel = page.getByTestId(selectors.components.DataSource.Prometheus.configPage.cacheLevel);
|
const cacheLevel = configPage.getByGrafanaSelector(
|
||||||
await cacheLevel.scrollIntoViewIfNeeded();
|
selectors.components.DataSource.Prometheus.configPage.cacheLevel
|
||||||
|
);
|
||||||
await expect(cacheLevel).toBeVisible();
|
await expect(cacheLevel).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should allow a user to select a query overlap window when incremental querying is selected', async ({
|
test('should allow a user to select a query overlap window when incremental querying is selected', async ({
|
||||||
|
createDataSourceConfigPage,
|
||||||
page,
|
page,
|
||||||
selectors,
|
selectors,
|
||||||
}) => {
|
}) => {
|
||||||
|
const DATASOURCE_NAME = `${DATASOURCE_PREFIX}_${Date.now()}`;
|
||||||
|
const configPage = await createDataSourceConfigPage({
|
||||||
|
type: 'prometheus',
|
||||||
|
name: DATASOURCE_NAME,
|
||||||
|
});
|
||||||
|
|
||||||
// Check the incremental querying checkbox
|
// Check the incremental querying checkbox
|
||||||
const incrementalQuerying = page.locator(
|
const incrementalQuerying = page.locator(
|
||||||
`#${selectors.components.DataSource.Prometheus.configPage.incrementalQuerying}`
|
`#${selectors.components.DataSource.Prometheus.configPage.incrementalQuerying}`
|
||||||
);
|
);
|
||||||
await incrementalQuerying.scrollIntoViewIfNeeded();
|
|
||||||
await expect(incrementalQuerying).toBeVisible();
|
await expect(incrementalQuerying).toBeVisible();
|
||||||
await incrementalQuerying.check({ force: true });
|
await incrementalQuerying.check({ force: true });
|
||||||
|
|
||||||
// Verify query overlap window is visible
|
// Verify query overlap window is visible
|
||||||
const queryOverlapWindow = page.getByTestId(
|
const queryOverlapWindow = configPage.getByGrafanaSelector(
|
||||||
selectors.components.DataSource.Prometheus.configPage.queryOverlapWindow
|
selectors.components.DataSource.Prometheus.configPage.queryOverlapWindow
|
||||||
);
|
);
|
||||||
await queryOverlapWindow.scrollIntoViewIfNeeded();
|
|
||||||
await expect(queryOverlapWindow).toBeVisible();
|
await expect(queryOverlapWindow).toBeVisible();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
async function selectOption(page, option) {
|
async function selectOption(page: Page, option: string) {
|
||||||
const optionElement = page.getByRole('option').filter({ hasText: option });
|
const optionElement = page.getByRole('option').filter({ hasText: option });
|
||||||
await expect(optionElement).toBeVisible();
|
await expect(optionElement).toBeVisible();
|
||||||
await optionElement.click();
|
await optionElement.click();
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
import { Page } from 'playwright-core';
|
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';
|
import { getResources } from '../utils/prometheus-helpers';
|
||||||
|
|
||||||
// TODO: fix some tests. Race conditions with other tests in the file cause some to fail.
|
test.describe(
|
||||||
test.describe.skip(
|
|
||||||
'Prometheus query editor',
|
'Prometheus query editor',
|
||||||
{
|
{
|
||||||
tag: ['@various', '@wip'],
|
tag: ['@various'],
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
const DATASOURCE_ID = 'Prometheus';
|
const DATASOURCE_ID = 'Prometheus';
|
||||||
|
@ -18,18 +24,25 @@ test.describe.skip(
|
||||||
/**
|
/**
|
||||||
* Create and save a Prometheus data source, navigate to code or builder
|
* Create and save a Prometheus data source, navigate to code or builder
|
||||||
*/
|
*/
|
||||||
async function navigateToEditor(page: Page, selectors: E2ESelectorGroups, editorType: string, name: string) {
|
async function navigateToEditor(
|
||||||
// Navigate to add data source page
|
page: Page,
|
||||||
await page.goto('/datasources/new');
|
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 configPage = await gotoDataSourceConfigPage(uid);
|
||||||
const prometheusPlugin = page.getByRole('button', { name: DATASOURCE_ID });
|
|
||||||
await prometheusPlugin.scrollIntoViewIfNeeded();
|
|
||||||
await expect(prometheusPlugin).toBeVisible();
|
|
||||||
await prometheusPlugin.click();
|
|
||||||
|
|
||||||
// Choose default editor
|
// 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 defaultEditor.scrollIntoViewIfNeeded();
|
||||||
await expect(defaultEditor).toBeVisible();
|
await expect(defaultEditor).toBeVisible();
|
||||||
await defaultEditor.click();
|
await defaultEditor.click();
|
||||||
|
@ -42,13 +55,10 @@ test.describe.skip(
|
||||||
);
|
);
|
||||||
await connectionSettings.fill('http://prom-url:9090');
|
await connectionSettings.fill('http://prom-url:9090');
|
||||||
|
|
||||||
// Name the DS
|
const saveResponse = page.waitForResponse((resp) => resp.url().includes('/api/datasources'));
|
||||||
const nameInput = page.getByTestId(selectors.pages.DataSource.name);
|
const saveAndTestButton = configPage.getByGrafanaSelector(selectors.pages.DataSource.saveAndTest);
|
||||||
await nameInput.clear();
|
|
||||||
await nameInput.fill(name);
|
|
||||||
|
|
||||||
const saveAndTestButton = page.getByTestId(selectors.pages.DataSource.saveAndTest);
|
|
||||||
await saveAndTestButton.click();
|
await saveAndTestButton.click();
|
||||||
|
await saveResponse;
|
||||||
|
|
||||||
// Visit explore
|
// Visit explore
|
||||||
await page.goto('/explore');
|
await page.goto('/explore');
|
||||||
|
@ -64,24 +74,42 @@ test.describe.skip(
|
||||||
await dataSourceOption.click();
|
await dataSourceOption.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
test('should have a kickstart component', async ({ page, selectors }) => {
|
test('should have a kickstart component', async ({
|
||||||
await navigateToEditor(page, selectors, 'Code', 'prometheus');
|
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);
|
const queryPatterns = page.getByTestId(selectors.components.QueryBuilder.queryPatterns);
|
||||||
await queryPatterns.scrollIntoViewIfNeeded();
|
await queryPatterns.scrollIntoViewIfNeeded();
|
||||||
await expect(queryPatterns).toBeVisible();
|
await expect(queryPatterns).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should have an explain component', async ({ page, selectors }) => {
|
test('should have an explain component', async ({
|
||||||
await navigateToEditor(page, selectors, 'Code', 'prometheus');
|
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);
|
const explain = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.explain);
|
||||||
await explain.scrollIntoViewIfNeeded();
|
await explain.scrollIntoViewIfNeeded();
|
||||||
await expect(explain).toBeVisible();
|
await expect(explain).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should have an editor toggle component', async ({ page, selectors }) => {
|
test('should have an editor toggle component', async ({
|
||||||
await navigateToEditor(page, selectors, 'Code', 'prometheus');
|
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);
|
const editorToggle = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.editorToggle);
|
||||||
await editorToggle.scrollIntoViewIfNeeded();
|
await editorToggle.scrollIntoViewIfNeeded();
|
||||||
|
@ -89,10 +117,13 @@ test.describe.skip(
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should have an options component with legend, format, step, type and exemplars', async ({
|
test('should have an options component with legend, format, step, type and exemplars', async ({
|
||||||
|
createDataSource,
|
||||||
|
gotoDataSourceConfigPage,
|
||||||
page,
|
page,
|
||||||
selectors,
|
selectors,
|
||||||
}) => {
|
}) => {
|
||||||
await navigateToEditor(page, selectors, 'Code', 'prometheus');
|
const DATASOURCE_NAME = `prometheus_${Date.now()}`;
|
||||||
|
await navigateToEditor(page, selectors, createDataSource, 'Code', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||||
|
|
||||||
// Open options
|
// Open options
|
||||||
const options = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.options);
|
const options = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.options);
|
||||||
|
@ -102,36 +133,40 @@ test.describe.skip(
|
||||||
|
|
||||||
// Check options
|
// Check options
|
||||||
const legend = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.legend);
|
const legend = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.legend);
|
||||||
await legend.scrollIntoViewIfNeeded();
|
|
||||||
await expect(legend).toBeVisible();
|
await expect(legend).toBeVisible();
|
||||||
|
|
||||||
const format = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.format);
|
const format = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.format);
|
||||||
await format.scrollIntoViewIfNeeded();
|
|
||||||
await expect(format).toBeVisible();
|
await expect(format).toBeVisible();
|
||||||
|
|
||||||
const step = page.locator('[data-test-id="prometheus-step"]');
|
const step = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.step);
|
||||||
await step.scrollIntoViewIfNeeded();
|
|
||||||
await expect(step).toBeVisible();
|
await expect(step).toBeVisible();
|
||||||
|
|
||||||
const type = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.type);
|
const type = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.type);
|
||||||
await type.scrollIntoViewIfNeeded();
|
|
||||||
await expect(type).toBeVisible();
|
await expect(type).toBeVisible();
|
||||||
|
|
||||||
const exemplars = page.getByTestId('prometheus-exemplars');
|
const exemplars = page.getByTestId(selectors.components.DataSource.Prometheus.queryEditor.exemplars);
|
||||||
await exemplars.scrollIntoViewIfNeeded();
|
|
||||||
await expect(exemplars).toBeVisible();
|
await expect(exemplars).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Code editor', () => {
|
test.describe('Code editor', () => {
|
||||||
test('navigates to the code editor with editor type as code', async ({ page, selectors }) => {
|
test('navigates to the code editor with editor type as code', async ({
|
||||||
await navigateToEditor(page, selectors, 'Code', 'prometheusCode');
|
createDataSource,
|
||||||
});
|
gotoDataSourceConfigPage,
|
||||||
|
|
||||||
test('navigates to the code editor and opens the metrics browser with metric search, labels, label values, and all components', async ({
|
|
||||||
page,
|
page,
|
||||||
selectors,
|
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);
|
await getResources(page);
|
||||||
|
|
||||||
|
@ -179,8 +214,14 @@ test.describe.skip(
|
||||||
await expect(clear).toBeVisible();
|
await expect(clear).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('selects a metric in the metrics browser and uses the query', async ({ page, selectors }) => {
|
test('selects a metric in the metrics browser and uses the query', async ({
|
||||||
await navigateToEditor(page, selectors, 'Code', 'prometheusCode');
|
createDataSource,
|
||||||
|
gotoDataSourceConfigPage,
|
||||||
|
page,
|
||||||
|
selectors,
|
||||||
|
}) => {
|
||||||
|
const DATASOURCE_NAME = `prometheusCode_${Date.now()}`;
|
||||||
|
await navigateToEditor(page, selectors, createDataSource, 'Code', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||||
|
|
||||||
await getResources(page);
|
await getResources(page);
|
||||||
|
|
||||||
|
@ -217,12 +258,24 @@ test.describe.skip(
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Query builder', () => {
|
test.describe('Query builder', () => {
|
||||||
test('navigates to the query builder with editor type as code', async ({ page, selectors }) => {
|
test('navigates to the query builder with editor type as code', async ({
|
||||||
await navigateToEditor(page, selectors, 'Builder', 'prometheusBuilder');
|
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 }) => {
|
test('the query builder contains metric select, label filters and operations', async ({
|
||||||
await navigateToEditor(page, selectors, 'Builder', 'prometheusBuilder');
|
createDataSource,
|
||||||
|
gotoDataSourceConfigPage,
|
||||||
|
page,
|
||||||
|
selectors,
|
||||||
|
}) => {
|
||||||
|
const DATASOURCE_NAME = `prometheusBuilder_${Date.now()}`;
|
||||||
|
await navigateToEditor(page, selectors, createDataSource, 'Builder', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||||
|
|
||||||
await getResources(page);
|
await getResources(page);
|
||||||
|
|
||||||
|
@ -241,8 +294,15 @@ test.describe.skip(
|
||||||
await expect(valueSelect).toBeVisible();
|
await expect(valueSelect).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can select a metric and provide a hint', async ({ page, selectors }) => {
|
// this throws a maximum update depth error?!
|
||||||
await navigateToEditor(page, selectors, 'Builder', 'prometheusBuilder');
|
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);
|
await getResources(page);
|
||||||
|
|
||||||
|
@ -258,8 +318,14 @@ test.describe.skip(
|
||||||
await expect(hints).toContainText('hint: add rate');
|
await expect(hints).toContainText('hint: add rate');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should have the metrics explorer opened via the metric select', async ({ page, selectors }) => {
|
test('should have the metrics explorer opened via the metric select', async ({
|
||||||
await navigateToEditor(page, selectors, 'Builder', 'prometheusBuilder');
|
createDataSource,
|
||||||
|
gotoDataSourceConfigPage,
|
||||||
|
page,
|
||||||
|
selectors,
|
||||||
|
}) => {
|
||||||
|
const DATASOURCE_NAME = `prometheusBuilder_${Date.now()}`;
|
||||||
|
await navigateToEditor(page, selectors, createDataSource, 'Builder', DATASOURCE_NAME, gotoDataSourceConfigPage);
|
||||||
|
|
||||||
await getResources(page);
|
await getResources(page);
|
||||||
|
|
||||||
|
@ -267,9 +333,8 @@ test.describe.skip(
|
||||||
selectors.components.DataSource.Prometheus.queryEditor.builder.metricSelect
|
selectors.components.DataSource.Prometheus.queryEditor.builder.metricSelect
|
||||||
);
|
);
|
||||||
await expect(metricSelect).toBeVisible();
|
await expect(metricSelect).toBeVisible();
|
||||||
await metricSelect.click();
|
|
||||||
|
|
||||||
await selectOption(page, 'Metrics explorer', selectors);
|
await page.getByLabel('Open metrics explorer').click();
|
||||||
|
|
||||||
const metricsExplorer = page.getByTestId(
|
const metricsExplorer = page.getByTestId(
|
||||||
selectors.components.DataSource.Prometheus.queryEditor.builder.metricsExplorer
|
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 { addDashboard } from '../utils/dashboard-helpers';
|
||||||
import { getResources } from '../utils/prometheus-helpers';
|
import { getResources } from '../utils/prometheus-helpers';
|
||||||
|
|
||||||
test.describe.skip(
|
test.describe(
|
||||||
'Prometheus variable query editor',
|
'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
|
* 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);
|
const editButton = page.getByTestId(selectors.components.NavToolbar.editDashboard.editButton);
|
||||||
await expect(editButton).toBeVisible();
|
await expect(editButton).toBeVisible();
|
||||||
await editButton.click();
|
await editButton.click();
|
||||||
|
@ -30,7 +32,12 @@ test.describe.skip(
|
||||||
/**
|
/**
|
||||||
* Begin the process of adding a query type variable for a Prometheus data source
|
* 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);
|
const addVariableButton = page.getByTestId(selectors.pages.Dashboard.Settings.Variables.List.addVariableCTAV2);
|
||||||
await addVariableButton.click();
|
await addVariableButton.click();
|
||||||
|
|
||||||
|
@ -42,7 +49,7 @@ test.describe.skip(
|
||||||
await expect(dataSourcePicker).toBeVisible();
|
await expect(dataSourcePicker).toBeVisible();
|
||||||
await dataSourcePicker.click();
|
await dataSourcePicker.click();
|
||||||
|
|
||||||
const dataSourceOption = page.getByText(DATASOURCE_NAME);
|
const dataSourceOption = page.getByText(datasourceName);
|
||||||
await dataSourceOption.scrollIntoViewIfNeeded();
|
await dataSourceOption.scrollIntoViewIfNeeded();
|
||||||
await expect(dataSourceOption).toBeVisible();
|
await expect(dataSourceOption).toBeVisible();
|
||||||
await dataSourceOption.click();
|
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.
|
* 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 addDashboard(page);
|
||||||
await navigateToVariables(page, selectors);
|
await navigateToVariables(page, selectors);
|
||||||
await addPrometheusQueryVariable(page, selectors, variableName);
|
await addPrometheusQueryVariable(page, selectors, datasourceName, variableName);
|
||||||
|
|
||||||
// Select query type
|
// Select query type
|
||||||
const queryTypeSelect = page.getByTestId(
|
const queryTypeSelect = page.getByTestId(
|
||||||
|
@ -88,7 +101,7 @@ test.describe.skip(
|
||||||
// Select prom data source from the data source list
|
// Select prom data source from the data source list
|
||||||
const dataSourcePickerInput = page.getByTestId(selectors.components.DataSourcePicker.inputV2);
|
const dataSourcePickerInput = page.getByTestId(selectors.components.DataSourcePicker.inputV2);
|
||||||
await dataSourcePickerInput.click();
|
await dataSourcePickerInput.click();
|
||||||
await dataSourcePickerInput.fill(DATASOURCE_NAME);
|
await dataSourcePickerInput.fill(datasourceName);
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
// Confirm the variable exists in the correct input
|
// 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 }) => {
|
test('should navigate to variable query editor', async ({ page, selectors }) => {
|
||||||
await addDashboard(page);
|
await addDashboard(page);
|
||||||
await navigateToVariables(page, selectors);
|
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 addDashboard(page);
|
||||||
await navigateToVariables(page, selectors);
|
await navigateToVariables(page, selectors);
|
||||||
await addPrometheusQueryVariable(page, selectors, 'labelsVariable');
|
await addPrometheusQueryVariable(page, selectors, DATASOURCE_NAME, 'labelsVariable');
|
||||||
|
|
||||||
// Select query type
|
// Select query type
|
||||||
const queryTypeSelect = page.getByTestId(
|
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 ({
|
test('should create a label names variable that is selectable in the label select in query builder', async ({
|
||||||
|
createDataSource,
|
||||||
page,
|
page,
|
||||||
selectors,
|
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 ({
|
test('should create a label values variable that is selectable in the label values select in query builder', async ({
|
||||||
|
createDataSource,
|
||||||
page,
|
page,
|
||||||
selectors,
|
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 ({
|
test('should create a metric names variable that is selectable in the metric select in query builder', async ({
|
||||||
|
createDataSource,
|
||||||
page,
|
page,
|
||||||
selectors,
|
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 });
|
const optionElement = page.getByRole('option', { name: option });
|
||||||
await expect(optionElement).toBeVisible();
|
await expect(optionElement).toBeVisible();
|
||||||
await optionElement.click();
|
await optionElement.click();
|
||||||
|
|
|
@ -1,27 +1,21 @@
|
||||||
import { readFileSync } from 'fs';
|
|
||||||
import { join } from 'path';
|
|
||||||
|
|
||||||
import { test, expect } from '@grafana/plugin-e2e';
|
import { test, expect } from '@grafana/plugin-e2e';
|
||||||
|
|
||||||
|
import longTraceResponse from '../fixtures/long-trace-response.json';
|
||||||
|
|
||||||
// this test requires a larger viewport
|
// this test requires a larger viewport
|
||||||
test.use({
|
test.use({
|
||||||
viewport: { width: 1280, height: 1080 },
|
viewport: { width: 1280, height: 1080 },
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO for some reason, this test gives "connection refused" errors in CI
|
test.describe(
|
||||||
test.describe.skip(
|
|
||||||
'Trace view',
|
'Trace view',
|
||||||
{
|
{
|
||||||
tag: ['@various'],
|
tag: ['@various'],
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
test('Can lazy load big traces', async ({ page, selectors }) => {
|
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
|
// 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({
|
await route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: 'application/json',
|
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue