mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
E2E: Rewrite mysql tests to playwright (#83424)
* E2E: Rewrite mysql tests to playwright * Fix lint * Add more selectors and address comments * Scope locators when locating text * Don't run it 20 times * Update new-datasource-variable to assert mysql
This commit is contained in:
@@ -3,6 +3,9 @@ import { e2e } from '../utils';
|
|||||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||||
const DASHBOARD_NAME = 'Test variable output';
|
const DASHBOARD_NAME = 'Test variable output';
|
||||||
|
|
||||||
|
const gdev_mysql = 'gdev-mysql';
|
||||||
|
const gdev_mysql_ds_tests = 'gdev-mysql-ds-tests';
|
||||||
|
|
||||||
describe('Variables - Datasource', () => {
|
describe('Variables - Datasource', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
||||||
@@ -20,18 +23,15 @@ describe('Variables - Datasource', () => {
|
|||||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type('VariableUnderTest').blur();
|
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type('VariableUnderTest').blur();
|
||||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type('Variable under test').blur();
|
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type('Variable under test').blur();
|
||||||
|
|
||||||
// If this is failing, but sure to check there are Prometheus datasources named "gdev-prometheus" and "gdev-slow-prometheus"
|
// If this is failing, but sure to check there are MySQL datasources named "gdev-mysql" and "gdev-mysql-ds-tests"
|
||||||
// Or, just update is to match some gdev datasources to test with :)
|
// Or, just update is to match some gdev datasources to test with :)
|
||||||
e2e.pages.Dashboard.Settings.Variables.Edit.DatasourceVariable.datasourceSelect().within(() => {
|
e2e.pages.Dashboard.Settings.Variables.Edit.DatasourceVariable.datasourceSelect().within(() => {
|
||||||
cy.get('input').type('Prometheus{enter}');
|
cy.get('input').type('MySQL{enter}');
|
||||||
});
|
});
|
||||||
|
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('contain.text', gdev_mysql);
|
||||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should(
|
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should(
|
||||||
'contain.text',
|
'contain.text',
|
||||||
'gdev-prometheus'
|
gdev_mysql_ds_tests
|
||||||
);
|
|
||||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should(
|
|
||||||
'contain.text',
|
|
||||||
'gdev-slow-prometheus'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Navigate back to the homepage and change the selected variable value
|
// Navigate back to the homepage and change the selected variable value
|
||||||
@@ -39,11 +39,10 @@ describe('Variables - Datasource', () => {
|
|||||||
e2e.pages.Dashboard.Settings.Actions.close().click();
|
e2e.pages.Dashboard.Settings.Actions.close().click();
|
||||||
e2e.components.RefreshPicker.runButtonV2().click();
|
e2e.components.RefreshPicker.runButtonV2().click();
|
||||||
|
|
||||||
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('gdev-prometheus').click();
|
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(gdev_mysql).click();
|
||||||
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('gdev-slow-prometheus').click();
|
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts(gdev_mysql_ds_tests).click();
|
||||||
|
|
||||||
// Assert it was rendered
|
// Assert it was rendered
|
||||||
cy.get('.markdown-html').should('include.text', 'VariableUnderTest: gdev-slow-prometheus-uid');
|
cy.get('.markdown-html').should('include.text', `VariableUnderTestText: ${gdev_mysql_ds_tests}`);
|
||||||
cy.get('.markdown-html').should('include.text', 'VariableUnderTestText: gdev-slow-prometheus');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
export const normalTableName = 'normalTable';
|
||||||
|
export const tableNameWithSpecialCharacter = 'table-name';
|
||||||
|
export const tablesResponse = {
|
||||||
|
results: {
|
||||||
|
tables: {
|
||||||
|
status: 200,
|
||||||
|
frames: [
|
||||||
|
{
|
||||||
|
schema: {
|
||||||
|
refId: 'tables',
|
||||||
|
meta: {
|
||||||
|
executedQueryString:
|
||||||
|
"SELECT table_name FROM information_schema.tables WHERE table_schema = 'DataMaker' ORDER BY table_name",
|
||||||
|
},
|
||||||
|
fields: [{ name: 'TABLE_NAME', type: 'string', typeInfo: { frame: 'string', nullable: true } }],
|
||||||
|
},
|
||||||
|
data: { values: [[normalTableName, tableNameWithSpecialCharacter]] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fieldsResponse = {
|
||||||
|
results: {
|
||||||
|
fields: {
|
||||||
|
status: 200,
|
||||||
|
frames: [
|
||||||
|
{
|
||||||
|
schema: {
|
||||||
|
refId: 'fields',
|
||||||
|
meta: {
|
||||||
|
executedQueryString:
|
||||||
|
"SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'DataMaker' AND table_name = 'RandomIntsWithTimes' ORDER BY column_name",
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{ name: 'COLUMN_NAME', type: 'string', typeInfo: { frame: 'string', nullable: true } },
|
||||||
|
{ name: 'DATA_TYPE', type: 'string', typeInfo: { frame: 'string', nullable: true } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
values: [
|
||||||
|
['createdAt', 'id', 'time', 'updatedAt', 'bigint'],
|
||||||
|
['datetime', 'int', 'datetime', 'datetime', 'int'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const datasetResponse = {
|
||||||
|
results: {
|
||||||
|
datasets: {
|
||||||
|
status: 200,
|
||||||
|
frames: [
|
||||||
|
{
|
||||||
|
schema: {
|
||||||
|
refId: 'datasets',
|
||||||
|
meta: {
|
||||||
|
executedQueryString:
|
||||||
|
"SELECT DISTINCT TABLE_SCHEMA from information_schema.TABLES where TABLE_TYPE != 'SYSTEM VIEW' ORDER BY TABLE_SCHEMA",
|
||||||
|
},
|
||||||
|
fields: [{ name: 'TABLE_SCHEMA', type: 'string', typeInfo: { frame: 'string', nullable: true } }],
|
||||||
|
},
|
||||||
|
data: { values: [['DataMaker', 'mysql', 'performance_schema', 'sys']] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
import { expect, test } from '@grafana/plugin-e2e';
|
||||||
|
|
||||||
|
import {
|
||||||
|
tablesResponse,
|
||||||
|
fieldsResponse,
|
||||||
|
datasetResponse,
|
||||||
|
normalTableName,
|
||||||
|
tableNameWithSpecialCharacter,
|
||||||
|
} from './mocks/mysql.mocks';
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, selectors, explorePage }) => {
|
||||||
|
await explorePage.datasource.set('gdev-mysql');
|
||||||
|
await context.route(selectors.apis.DataSource.queryPattern, async (route, request) => {
|
||||||
|
switch (request.postDataJSON().queries[0].refId) {
|
||||||
|
case 'tables':
|
||||||
|
return route.fulfill({ json: tablesResponse, status: 200 });
|
||||||
|
case 'fields':
|
||||||
|
return route.fulfill({ json: fieldsResponse, status: 200 });
|
||||||
|
case 'datasets':
|
||||||
|
return route.fulfill({ json: datasetResponse, status: 200 });
|
||||||
|
default:
|
||||||
|
return route.continue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('code editor autocomplete should handle table name escaping/quoting', async ({ explorePage, selectors, page }) => {
|
||||||
|
await page.getByLabel('Code').check();
|
||||||
|
|
||||||
|
const editor = explorePage.getByTestIdOrAriaLabel(selectors.components.CodeEditor.container).getByRole('textbox');
|
||||||
|
await editor.fill('S');
|
||||||
|
await page.getByLabel('SELECT <column> FROM <table>').locator('a').click();
|
||||||
|
await expect(page.getByLabel(tableNameWithSpecialCharacter)).toBeVisible();
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
await expect(editor).toHaveValue(`SELECT FROM grafana.\`${tableNameWithSpecialCharacter}\``);
|
||||||
|
|
||||||
|
for (let i = 0; i < tableNameWithSpecialCharacter.length + 2; i++) {
|
||||||
|
await page.keyboard.press('Backspace');
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.keyboard.press('Control+I');
|
||||||
|
await expect(page.getByLabel(tableNameWithSpecialCharacter)).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('visual query builder should handle time filter macro', async ({ explorePage, page }) => {
|
||||||
|
await explorePage.getByTestIdOrAriaLabel(selectors.components.SQLQueryEditor.headerTableSelector).click();
|
||||||
|
await page.getByText(normalTableName, { exact: true }).click();
|
||||||
|
|
||||||
|
// Open column selector
|
||||||
|
await explorePage.getByTestIdOrAriaLabel(selectors.components.SQLQueryEditor.selectColumn).click();
|
||||||
|
const select = page.getByLabel('Select options menu');
|
||||||
|
await select.locator(page.getByText('createdAt')).click();
|
||||||
|
|
||||||
|
// Toggle where row
|
||||||
|
await page.getByLabel('Filter').click();
|
||||||
|
|
||||||
|
// Click add filter button
|
||||||
|
await page.getByRole('button', { name: 'Add filter' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Add filter' }).click(); // For some reason we need to click twice
|
||||||
|
|
||||||
|
// Open field selector
|
||||||
|
await explorePage.getByTestIdOrAriaLabel(selectors.components.SQLQueryEditor.filterField).click();
|
||||||
|
await select.locator(page.getByText('createdAt')).click();
|
||||||
|
|
||||||
|
// Open operator selector
|
||||||
|
await explorePage.getByTestIdOrAriaLabel(selectors.components.SQLQueryEditor.filterOperator).click();
|
||||||
|
await select.locator(page.getByText('Macros')).click();
|
||||||
|
|
||||||
|
// Open macros value selector
|
||||||
|
await explorePage.getByTestIdOrAriaLabel('Macros value selector').click();
|
||||||
|
await select.locator(page.getByText('timeFilter', { exact: true })).click();
|
||||||
|
|
||||||
|
// Validate that the timeFilter macro was added
|
||||||
|
await expect(
|
||||||
|
explorePage.getByTestIdOrAriaLabel(selectors.components.CodeEditor.container).getByRole('textbox')
|
||||||
|
).toHaveValue(`SELECT\n createdAt\nFROM\n DataMaker.normalTable\nWHERE\n $__timeFilter(createdAt)\nLIMIT\n 50`);
|
||||||
|
|
||||||
|
// Validate that the timeFilter macro was removed when changed to equals operator
|
||||||
|
await explorePage.getByTestIdOrAriaLabel(selectors.components.SQLQueryEditor.filterOperator).click();
|
||||||
|
await select.locator(page.getByText('==')).click();
|
||||||
|
|
||||||
|
await explorePage.getByTestIdOrAriaLabel(selectors.components.DateTimePicker.input).click();
|
||||||
|
await explorePage.getByTestIdOrAriaLabel(selectors.components.DateTimePicker.input).blur();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
explorePage.getByTestIdOrAriaLabel(selectors.components.CodeEditor.container).getByRole('textbox')
|
||||||
|
).not.toHaveValue(`SELECT\n createdAt\nFROM\n DataMaker.normalTable\nWHERE\n createdAt = NULL\nLIMIT\n 50`);
|
||||||
|
});
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"results": {
|
|
||||||
"datasets": {
|
|
||||||
"status": 200,
|
|
||||||
"frames": [
|
|
||||||
{
|
|
||||||
"schema": {
|
|
||||||
"refId": "datasets",
|
|
||||||
"meta": {
|
|
||||||
"executedQueryString": "SELECT DISTINCT TABLE_SCHEMA from information_schema.TABLES where TABLE_TYPE != 'SYSTEM VIEW' ORDER BY TABLE_SCHEMA"
|
|
||||||
},
|
|
||||||
"fields": [
|
|
||||||
{ "name": "TABLE_SCHEMA", "type": "string", "typeInfo": { "frame": "string", "nullable": true } }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"data": { "values": [["DataMaker", "mysql", "performance_schema", "sys"]] }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"results": {
|
|
||||||
"fields": {
|
|
||||||
"status": 200,
|
|
||||||
"frames": [
|
|
||||||
{
|
|
||||||
"schema": {
|
|
||||||
"refId": "fields",
|
|
||||||
"meta": {
|
|
||||||
"executedQueryString": "SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'DataMaker' AND table_name = 'RandomIntsWithTimes' ORDER BY column_name"
|
|
||||||
},
|
|
||||||
"fields": [
|
|
||||||
{ "name": "COLUMN_NAME", "type": "string", "typeInfo": { "frame": "string", "nullable": true } },
|
|
||||||
{ "name": "DATA_TYPE", "type": "string", "typeInfo": { "frame": "string", "nullable": true } }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"data": {
|
|
||||||
"values": [
|
|
||||||
["createdAt", "id", "time", "updatedAt", "bigint"],
|
|
||||||
["datetime", "int", "datetime", "datetime", "int"]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"results": {
|
|
||||||
"tables": {
|
|
||||||
"status": 200,
|
|
||||||
"frames": [
|
|
||||||
{
|
|
||||||
"schema": {
|
|
||||||
"refId": "tables",
|
|
||||||
"meta": {
|
|
||||||
"executedQueryString": "SELECT table_name FROM information_schema.tables WHERE table_schema = 'DataMaker' ORDER BY table_name"
|
|
||||||
},
|
|
||||||
"fields": [{ "name": "TABLE_NAME", "type": "string", "typeInfo": { "frame": "string", "nullable": true } }]
|
|
||||||
},
|
|
||||||
"data": { "values": [["normalTable", "table-name"]] }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
import { e2e } from '../utils';
|
|
||||||
|
|
||||||
import datasetResponse from './fixtures/datasets-response.json';
|
|
||||||
import fieldsResponse from './fixtures/fields-response.json';
|
|
||||||
import tablesResponse from './fixtures/tables-response.json';
|
|
||||||
|
|
||||||
const tableNameWithSpecialCharacter = tablesResponse.results.tables.frames[0].data.values[0][1];
|
|
||||||
const normalTableName = tablesResponse.results.tables.frames[0].data.values[0][0];
|
|
||||||
|
|
||||||
describe('MySQL datasource', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.intercept('POST', '/api/ds/query', (req) => {
|
|
||||||
if (req.body.queries[0].refId === 'datasets') {
|
|
||||||
req.alias = 'datasets';
|
|
||||||
req.reply({
|
|
||||||
body: datasetResponse,
|
|
||||||
});
|
|
||||||
} else if (req.body.queries[0].refId === 'tables') {
|
|
||||||
req.alias = 'tables';
|
|
||||||
req.reply({
|
|
||||||
body: tablesResponse,
|
|
||||||
});
|
|
||||||
} else if (req.body.queries[0].refId === 'fields') {
|
|
||||||
req.alias = 'fields';
|
|
||||||
req.reply({
|
|
||||||
body: fieldsResponse,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
|
||||||
e2e.pages.Explore.visit();
|
|
||||||
|
|
||||||
e2e.components.DataSourcePicker.container().should('be.visible').type('gdev-mysql{enter}');
|
|
||||||
cy.wait('@datasets');
|
|
||||||
});
|
|
||||||
|
|
||||||
it.skip('code editor autocomplete should handle table name escaping/quoting', () => {
|
|
||||||
e2e.components.RadioButton.container().filter(':contains("Code")').click();
|
|
||||||
|
|
||||||
e2e.components.CodeEditor.container().children('[data-testid="Spinner"]').should('not.exist');
|
|
||||||
cy.window().its('monaco').should('exist');
|
|
||||||
|
|
||||||
cy.get('textarea').type('S{downArrow}{enter}');
|
|
||||||
cy.wait('@tables');
|
|
||||||
cy.get('.suggest-widget').contains(tableNameWithSpecialCharacter).should('be.visible');
|
|
||||||
cy.get('textarea').type('{enter}');
|
|
||||||
cy.get('textarea').should('have.value', `SELECT FROM grafana.\`${tableNameWithSpecialCharacter}\``);
|
|
||||||
|
|
||||||
const deleteTimes = new Array(tableNameWithSpecialCharacter.length + 2).fill(
|
|
||||||
'{backspace}',
|
|
||||||
0,
|
|
||||||
tableNameWithSpecialCharacter.length + 2
|
|
||||||
);
|
|
||||||
cy.get('textarea').type(deleteTimes.join(''));
|
|
||||||
|
|
||||||
const commandKey = Cypress.platform === 'darwin' ? '{command}' : '{ctrl}';
|
|
||||||
|
|
||||||
cy.get('textarea').type(`${commandKey}i`);
|
|
||||||
cy.get('.suggest-widget').contains(tableNameWithSpecialCharacter).should('be.visible');
|
|
||||||
cy.get('textarea').type('S{downArrow}{enter}');
|
|
||||||
cy.get('textarea').should('have.value', `SELECT FROM grafana.${normalTableName}`);
|
|
||||||
|
|
||||||
cy.get('textarea').type('.');
|
|
||||||
cy.get('.suggest-widget').contains('No suggestions.').should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('visual query builder', () => {
|
|
||||||
it('should be able to add timeFilter macro', () => {
|
|
||||||
cy.get("[aria-label='Table selector']").should('be.visible').click();
|
|
||||||
selectOption(normalTableName);
|
|
||||||
// Open column selector
|
|
||||||
cy.get("[id^='select-column-0']").should('be.visible').click();
|
|
||||||
selectOption('createdAt');
|
|
||||||
|
|
||||||
// Toggle where row
|
|
||||||
cy.get("label[for^='sql-filter']").last().should('be.visible').click();
|
|
||||||
|
|
||||||
// Click add filter button
|
|
||||||
cy.get('button[title="Add filter"]').should('be.visible').click();
|
|
||||||
cy.get('button[title="Add filter"]').should('be.visible').click(); // For some reason we need to click twice
|
|
||||||
|
|
||||||
// Open field selector
|
|
||||||
cy.get("[aria-label='Field']").should('be.visible').click();
|
|
||||||
selectOption('createdAt');
|
|
||||||
|
|
||||||
// Open operator selector
|
|
||||||
cy.get("[aria-label='Operator']").should('be.visible').click();
|
|
||||||
selectOption('Macros');
|
|
||||||
|
|
||||||
// Open macros value selector
|
|
||||||
cy.get("[aria-label='Macros value selector']").should('be.visible').click();
|
|
||||||
selectOption('timeFilter');
|
|
||||||
|
|
||||||
e2e.components.CodeEditor.container().children('[data-testid="Spinner"]').should('not.exist');
|
|
||||||
cy.window().its('monaco').should('exist');
|
|
||||||
|
|
||||||
// Validate that the timeFilter macro was added
|
|
||||||
e2e.components.CodeEditor.container()
|
|
||||||
.get('textarea')
|
|
||||||
.should(
|
|
||||||
'have.value',
|
|
||||||
`SELECT\n createdAt\nFROM\n DataMaker.normalTable\nWHERE\n $__timeFilter(createdAt)\nLIMIT\n 50`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Validate that the timeFilter macro was removed when changed to equals operator
|
|
||||||
|
|
||||||
// For some reason the input is not visible the second time so we need to force the click
|
|
||||||
cy.get("[aria-label='Operator']").click({ force: true });
|
|
||||||
selectOption('==');
|
|
||||||
|
|
||||||
e2e.components.DateTimePicker.input().should('be.visible').click().blur();
|
|
||||||
|
|
||||||
e2e.components.CodeEditor.container()
|
|
||||||
.get('textarea')
|
|
||||||
.should(
|
|
||||||
'not.have.value',
|
|
||||||
`SELECT\n createdAt\nFROM\n DataMaker.normalTable\nWHERE\n $__timeFilter(createdAt)\nLIMIT\n 50`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function selectOption(option: string) {
|
|
||||||
cy.get("[aria-label='Select option']").contains(option).should('be.visible').click();
|
|
||||||
}
|
|
||||||
@@ -537,4 +537,17 @@ export const Components = {
|
|||||||
Tooltip: {
|
Tooltip: {
|
||||||
container: 'data-testid tooltip',
|
container: 'data-testid tooltip',
|
||||||
},
|
},
|
||||||
|
SQLQueryEditor: {
|
||||||
|
selectColumn: 'data-testid select-column',
|
||||||
|
selectAggregation: 'data-testid select-aggregation',
|
||||||
|
selectAlias: 'data-testid select-alias',
|
||||||
|
filterConjunction: 'data-testid filter-conjunction',
|
||||||
|
filterField: 'data-testid filter-field',
|
||||||
|
filterOperator: 'data-testid filter-operator',
|
||||||
|
headerTableSelector: 'data-testid header-table-selector',
|
||||||
|
headerFilterSwitch: 'data-testid header-filter-switch',
|
||||||
|
headerGroupSwitch: 'data-testid header-group-switch',
|
||||||
|
headerOrderSwitch: 'data-testid header-order-switch',
|
||||||
|
headerPreviewSwitch: 'data-testid header-preview-switch',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useCopyToClipboard } from 'react-use';
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { EditorField, EditorHeader, EditorMode, EditorRow, FlexItem, InlineSelect } from '@grafana/experimental';
|
import { EditorField, EditorHeader, EditorMode, EditorRow, FlexItem, InlineSelect } from '@grafana/experimental';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { Button, InlineSwitch, RadioButtonGroup, Tooltip, Space } from '@grafana/ui';
|
import { Button, InlineSwitch, RadioButtonGroup, Tooltip, Space } from '@grafana/ui';
|
||||||
@@ -137,6 +138,7 @@ export function QueryHeader({
|
|||||||
<InlineSwitch
|
<InlineSwitch
|
||||||
id={`sql-filter-${uuidv4()}}`}
|
id={`sql-filter-${uuidv4()}}`}
|
||||||
label="Filter"
|
label="Filter"
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.headerFilterSwitch}
|
||||||
transparent={true}
|
transparent={true}
|
||||||
showLabel={true}
|
showLabel={true}
|
||||||
value={queryRowFilter.filter}
|
value={queryRowFilter.filter}
|
||||||
@@ -157,6 +159,7 @@ export function QueryHeader({
|
|||||||
<InlineSwitch
|
<InlineSwitch
|
||||||
id={`sql-group-${uuidv4()}}`}
|
id={`sql-group-${uuidv4()}}`}
|
||||||
label="Group"
|
label="Group"
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.headerGroupSwitch}
|
||||||
transparent={true}
|
transparent={true}
|
||||||
showLabel={true}
|
showLabel={true}
|
||||||
value={queryRowFilter.group}
|
value={queryRowFilter.group}
|
||||||
@@ -177,6 +180,7 @@ export function QueryHeader({
|
|||||||
<InlineSwitch
|
<InlineSwitch
|
||||||
id={`sql-order-${uuidv4()}}`}
|
id={`sql-order-${uuidv4()}}`}
|
||||||
label="Order"
|
label="Order"
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.headerOrderSwitch}
|
||||||
transparent={true}
|
transparent={true}
|
||||||
showLabel={true}
|
showLabel={true}
|
||||||
value={queryRowFilter.order}
|
value={queryRowFilter.order}
|
||||||
@@ -197,6 +201,7 @@ export function QueryHeader({
|
|||||||
<InlineSwitch
|
<InlineSwitch
|
||||||
id={`sql-preview-${uuidv4()}}`}
|
id={`sql-preview-${uuidv4()}}`}
|
||||||
label="Preview"
|
label="Preview"
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.headerPreviewSwitch}
|
||||||
transparent={true}
|
transparent={true}
|
||||||
showLabel={true}
|
showLabel={true}
|
||||||
value={queryRowFilter.preview}
|
value={queryRowFilter.preview}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
|
|
||||||
import { SelectableValue, toOption } from '@grafana/data';
|
import { SelectableValue, toOption } from '@grafana/data';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { Select } from '@grafana/ui';
|
import { Select } from '@grafana/ui';
|
||||||
|
|
||||||
import { DB, ResourceSelectorProps } from '../types';
|
import { DB, ResourceSelectorProps } from '../types';
|
||||||
@@ -29,6 +30,7 @@ export const TableSelector = ({ db, dataset, table, className, onChange }: Table
|
|||||||
className={className}
|
className={className}
|
||||||
disabled={state.loading}
|
disabled={state.loading}
|
||||||
aria-label="Table selector"
|
aria-label="Table selector"
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.headerTableSelector}
|
||||||
value={table}
|
value={table}
|
||||||
options={state.value}
|
options={state.value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { isString } from 'lodash';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { dateTime, toOption } from '@grafana/data';
|
import { dateTime, toOption } from '@grafana/data';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { Button, DateTimePicker, Input, Select } from '@grafana/ui';
|
import { Button, DateTimePicker, Input, Select } from '@grafana/ui';
|
||||||
|
|
||||||
const buttonLabels = {
|
const buttonLabels = {
|
||||||
@@ -124,6 +125,7 @@ export const settings: Settings = {
|
|||||||
<Select
|
<Select
|
||||||
id={conjProps?.id}
|
id={conjProps?.id}
|
||||||
aria-label="Conjunction"
|
aria-label="Conjunction"
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.filterConjunction}
|
||||||
menuShouldPortal
|
menuShouldPortal
|
||||||
options={conjProps?.conjunctionOptions ? Object.keys(conjProps?.conjunctionOptions).map(toOption) : undefined}
|
options={conjProps?.conjunctionOptions ? Object.keys(conjProps?.conjunctionOptions).map(toOption) : undefined}
|
||||||
value={conjProps?.selectedConjunction}
|
value={conjProps?.selectedConjunction}
|
||||||
@@ -139,6 +141,7 @@ export const settings: Settings = {
|
|||||||
id={fieldProps?.id}
|
id={fieldProps?.id}
|
||||||
width={25}
|
width={25}
|
||||||
aria-label="Field"
|
aria-label="Field"
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.filterField}
|
||||||
menuShouldPortal
|
menuShouldPortal
|
||||||
options={fieldProps?.items.map((f) => {
|
options={fieldProps?.items.map((f) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -175,6 +178,7 @@ export const settings: Settings = {
|
|||||||
<Select
|
<Select
|
||||||
options={operatorProps?.items.map((op) => ({ label: op.label, value: op.key }))}
|
options={operatorProps?.items.map((op) => ({ label: op.label, value: op.key }))}
|
||||||
aria-label="Operator"
|
aria-label="Operator"
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.filterOperator}
|
||||||
menuShouldPortal
|
menuShouldPortal
|
||||||
value={operatorProps?.selectedKey}
|
value={operatorProps?.selectedKey}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { uniqueId } from 'lodash';
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
|
||||||
import { SelectableValue, toOption } from '@grafana/data';
|
import { SelectableValue, toOption } from '@grafana/data';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { EditorField, Stack } from '@grafana/experimental';
|
import { EditorField, Stack } from '@grafana/experimental';
|
||||||
import { Button, Select, useStyles2 } from '@grafana/ui';
|
import { Button, Select, useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
@@ -115,6 +116,7 @@ export function SelectRow({ sql, format, columns, onSqlChange, functions }: Sele
|
|||||||
<EditorField label="Column" width={25}>
|
<EditorField label="Column" width={25}>
|
||||||
<Select
|
<Select
|
||||||
value={getColumnValue(item)}
|
value={getColumnValue(item)}
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.selectColumn}
|
||||||
options={columnsWithAsterisk}
|
options={columnsWithAsterisk}
|
||||||
inputId={`select-column-${index}-${uniqueId()}`}
|
inputId={`select-column-${index}-${uniqueId()}`}
|
||||||
menuShouldPortal
|
menuShouldPortal
|
||||||
@@ -127,6 +129,7 @@ export function SelectRow({ sql, format, columns, onSqlChange, functions }: Sele
|
|||||||
<Select
|
<Select
|
||||||
value={item.name ? toOption(item.name) : null}
|
value={item.name ? toOption(item.name) : null}
|
||||||
inputId={`select-aggregation-${index}-${uniqueId()}`}
|
inputId={`select-aggregation-${index}-${uniqueId()}`}
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.selectAggregation}
|
||||||
isClearable
|
isClearable
|
||||||
menuShouldPortal
|
menuShouldPortal
|
||||||
allowCustomValue
|
allowCustomValue
|
||||||
@@ -138,6 +141,7 @@ export function SelectRow({ sql, format, columns, onSqlChange, functions }: Sele
|
|||||||
<Select
|
<Select
|
||||||
value={item.alias ? toOption(item.alias) : null}
|
value={item.alias ? toOption(item.alias) : null}
|
||||||
inputId={`select-alias-${index}-${uniqueId()}`}
|
inputId={`select-alias-${index}-${uniqueId()}`}
|
||||||
|
data-testid={selectors.components.SQLQueryEditor.selectAlias}
|
||||||
options={timeSeriesAliasOpts}
|
options={timeSeriesAliasOpts}
|
||||||
onChange={onAliasChange(item, index)}
|
onChange={onAliasChange(item, index)}
|
||||||
isClearable
|
isClearable
|
||||||
|
|||||||
+12
-3
@@ -3,7 +3,7 @@ import path, { dirname } from 'path';
|
|||||||
|
|
||||||
import { PluginOptions } from '@grafana/plugin-e2e';
|
import { PluginOptions } from '@grafana/plugin-e2e';
|
||||||
|
|
||||||
const testDirRoot = 'e2e/plugin-e2e/plugin-e2e-api-tests/';
|
const testDirRoot = 'e2e/plugin-e2e/';
|
||||||
|
|
||||||
export default defineConfig<PluginOptions>({
|
export default defineConfig<PluginOptions>({
|
||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
@@ -44,7 +44,7 @@ export default defineConfig<PluginOptions>({
|
|||||||
// Run all tests in parallel using user with admin role
|
// Run all tests in parallel using user with admin role
|
||||||
{
|
{
|
||||||
name: 'admin',
|
name: 'admin',
|
||||||
testDir: path.join(testDirRoot, '/as-admin-user'),
|
testDir: path.join(testDirRoot, '/plugin-e2e-api-tests/as-admin-user'),
|
||||||
use: {
|
use: {
|
||||||
...devices['Desktop Chrome'],
|
...devices['Desktop Chrome'],
|
||||||
storageState: 'playwright/.auth/admin.json',
|
storageState: 'playwright/.auth/admin.json',
|
||||||
@@ -54,12 +54,21 @@ export default defineConfig<PluginOptions>({
|
|||||||
// Run all tests in parallel using user with viewer role
|
// Run all tests in parallel using user with viewer role
|
||||||
{
|
{
|
||||||
name: 'viewer',
|
name: 'viewer',
|
||||||
testDir: path.join(testDirRoot, '/as-viewer-user'),
|
testDir: path.join(testDirRoot, '/plugin-e2e-api-tests/as-viewer-user'),
|
||||||
use: {
|
use: {
|
||||||
...devices['Desktop Chrome'],
|
...devices['Desktop Chrome'],
|
||||||
storageState: 'playwright/.auth/viewer.json',
|
storageState: 'playwright/.auth/viewer.json',
|
||||||
},
|
},
|
||||||
dependencies: ['createUserAndAuthenticate'],
|
dependencies: ['createUserAndAuthenticate'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'mysql',
|
||||||
|
testDir: path.join(testDirRoot, '/mysql'),
|
||||||
|
use: {
|
||||||
|
...devices['Desktop Chrome'],
|
||||||
|
storageState: 'playwright/.auth/admin.json',
|
||||||
|
},
|
||||||
|
dependencies: ['authenticate'],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user