mirror of https://github.com/grafana/grafana
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 mysqlpull/78289/head^2
parent
351425ab3d
commit
a4fe7f39ea
@ -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(); |
||||
} |
||||
Loading…
Reference in new issue