From fbb5fd71f9ff57069230616d8e90b24ec76186bd Mon Sep 17 00:00:00 2001 From: Sergej-Vlasov <37613182+Sergej-Vlasov@users.noreply.github.com> Date: Fri, 24 May 2024 14:22:11 +0100 Subject: [PATCH] DashboardScenes: Optimise e2e test and add panels suite (#88170) * refactor tests after adding additional data-testids * add panels-suite e2e tests * wip: add missing selectors * finalise panels-suite tests * clean up * adjust workflow for test run * restore workflow --- .github/workflows/run-scenes-e2e.yml | 1 + .../Repeating_a_panel_horizontally.spec.ts | 4 +- .../Repeating_a_panel_vertically.spec.ts | 4 +- .../Repeating_an_empty_row.spec.ts | 6 +- .../load-options-from-url.spec.ts | 160 ++++++------------ .../set-options-from-ui.spec.ts | 126 +++++--------- ...ting-dashboard-links-and-variables.spec.ts | 10 +- e2e/scenes/panels-suite/dashlist.spec.ts | 41 +++++ .../panels-suite/datagrid-data-change.spec.ts | 36 ++++ .../datagrid-editing-features.spec.ts | 159 +++++++++++++++++ .../frontend-sandbox-panel.spec.ts | 128 ++++++++++++++ .../panels-suite/geomap-layer-types.spec.ts | 99 +++++++++++ .../panels-suite/geomap-map-controls.spec.ts | 58 +++++++ ...eomap-spatial-operations-transform.spec.ts | 98 +++++++++++ .../panels-suite/panelEdit_base.spec.ts | 103 +++++++++++ .../panels-suite/panelEdit_queries.spec.ts | 99 +++++++++++ .../panels-suite/panelEdit_transforms.spec.ts | 28 +++ package.json | 2 +- .../PanelDataPane/PanelDataPane.tsx | 3 +- .../PanelDataPane/PanelDataQueriesTab.tsx | 4 +- .../panel-edit/PanelEditorRenderer.tsx | 3 +- yarn.lock | 16 +- 22 files changed, 968 insertions(+), 220 deletions(-) create mode 100644 e2e/scenes/panels-suite/dashlist.spec.ts create mode 100644 e2e/scenes/panels-suite/datagrid-data-change.spec.ts create mode 100644 e2e/scenes/panels-suite/datagrid-editing-features.spec.ts create mode 100644 e2e/scenes/panels-suite/frontend-sandbox-panel.spec.ts create mode 100644 e2e/scenes/panels-suite/geomap-layer-types.spec.ts create mode 100644 e2e/scenes/panels-suite/geomap-map-controls.spec.ts create mode 100644 e2e/scenes/panels-suite/geomap-spatial-operations-transform.spec.ts create mode 100644 e2e/scenes/panels-suite/panelEdit_base.spec.ts create mode 100644 e2e/scenes/panels-suite/panelEdit_queries.spec.ts create mode 100644 e2e/scenes/panels-suite/panelEdit_transforms.spec.ts diff --git a/.github/workflows/run-scenes-e2e.yml b/.github/workflows/run-scenes-e2e.yml index ff2e70841a2..53425680492 100644 --- a/.github/workflows/run-scenes-e2e.yml +++ b/.github/workflows/run-scenes-e2e.yml @@ -3,6 +3,7 @@ name: Run dashboard scenes e2e on: schedule: - cron: "0 8 * * 1-5" # every day at 08:00UTC on weekdays + # push # uncomment for test run during PR env: ARCH: linux-amd64 diff --git a/e2e/scenes/dashboard-suite/Repeating_a_panel_horizontally.spec.ts b/e2e/scenes/dashboard-suite/Repeating_a_panel_horizontally.spec.ts index 2f2adef207a..9b3ec4806e0 100644 --- a/e2e/scenes/dashboard-suite/Repeating_a_panel_horizontally.spec.ts +++ b/e2e/scenes/dashboard-suite/Repeating_a_panel_horizontally.spec.ts @@ -43,8 +43,8 @@ describe('Repeating a panel horizontally', () => { cy.get('input').click(); }); - e2e.components.Select.option().contains('1').click(); - e2e.components.Select.option().contains('3').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('1').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('3').click(); // blur the dropdown cy.get('body').click(); diff --git a/e2e/scenes/dashboard-suite/Repeating_a_panel_vertically.spec.ts b/e2e/scenes/dashboard-suite/Repeating_a_panel_vertically.spec.ts index 325d648bda9..fbf0f5bf9d2 100644 --- a/e2e/scenes/dashboard-suite/Repeating_a_panel_vertically.spec.ts +++ b/e2e/scenes/dashboard-suite/Repeating_a_panel_vertically.spec.ts @@ -44,8 +44,8 @@ describe('Repeating a panel vertically', () => { cy.get('input').click(); }); - e2e.components.Select.option().contains('1').click(); - e2e.components.Select.option().contains('3').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('1').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('3').click(); // blur the dropdown cy.get('body').click(); diff --git a/e2e/scenes/dashboard-suite/Repeating_an_empty_row.spec.ts b/e2e/scenes/dashboard-suite/Repeating_an_empty_row.spec.ts index 9048c5149b0..2bf52250bad 100644 --- a/e2e/scenes/dashboard-suite/Repeating_an_empty_row.spec.ts +++ b/e2e/scenes/dashboard-suite/Repeating_an_empty_row.spec.ts @@ -1,7 +1,7 @@ import { e2e } from '../utils'; const PAGE_UNDER_TEST = 'dtpl2Ctnk/repeating-an-empty-row'; -describe.skip('Repeating empty rows', () => { +describe('Repeating empty rows', () => { beforeEach(() => { e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); }); @@ -38,8 +38,8 @@ describe.skip('Repeating empty rows', () => { cy.get('input').click(); }); - e2e.components.Select.option().contains('1').click(); - e2e.components.Select.option().contains('3').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('1').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('3').click(); // blur the dropdown cy.get('body').click(); diff --git a/e2e/scenes/dashboard-suite/load-options-from-url.spec.ts b/e2e/scenes/dashboard-suite/load-options-from-url.spec.ts index 677428a8be7..04740c74e24 100644 --- a/e2e/scenes/dashboard-suite/load-options-from-url.spec.ts +++ b/e2e/scenes/dashboard-suite/load-options-from-url.spec.ts @@ -18,72 +18,46 @@ describe('Variables - Load options from Url', () => { e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A') .should('be.visible') - .children() - .children() - .first() - .click(); + .within(() => { + cy.get('input').click(); + }); e2e.components.Select.option().parent().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'A') - .next() - .should('have.text', 'B') - .next() - .should('have.text', 'C') - .next() - .should('have.text', 'D'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('C').should('be.visible'); cy.get('body').click(0, 0); e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('AA') .should('be.visible') - .children() - .children() - .first() - .click(); + .within(() => { + cy.get('input').click(); + }); e2e.components.Select.option().parent().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'AA') - .next() - .should('have.text', 'AB') - .next() - .should('have.text', 'AC') - .next() - .should('have.text', 'AD'); + + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AA').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AB').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AC').should('be.visible'); cy.get('body').click(0, 0); e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('$__all') .should('be.visible') - .children() - .children() - .first() - .click(); + .within(() => { + cy.get('input').click(); + }); e2e.components.Select.option().parent().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'AAA') - .next() - .should('have.text', 'AAB') - .next() - .should('have.text', 'AAC') - .next() - .should('have.text', 'AAD'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAA').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAB').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAC').should('be.visible'); }); it('options set in url should load correct options', () => { @@ -97,73 +71,46 @@ describe('Variables - Load options from Url', () => { e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('B') .should('be.visible') - .children() - .children() - .first() - .click(); + .within(() => { + cy.get('input').click(); + }); e2e.components.Select.option().parent().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'A') - .next() - .should('have.text', 'B') - .next() - .should('have.text', 'C') - .next() - .should('have.text', 'D'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('C').should('be.visible'); cy.get('body').click(0, 0); e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('BB') .should('be.visible') - .children() - .children() - .first() - .click(); + .within(() => { + cy.get('input').click(); + }); e2e.components.Select.option().parent().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'BA') - .next() - .should('have.text', 'BB') - .next() - .should('have.text', 'BC') - .next() - .should('have.text', 'BD'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BA').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BB').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BC').should('be.visible'); cy.get('body').click(0, 0); e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('BBB') .should('be.visible') - .children() - .children() - .first() - .click(); + .within(() => { + cy.get('input').click(); + }); e2e.components.Select.option().parent().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'BBA') - .next() - .should('have.text', 'BBB') - .next() - .should('have.text', 'BBC') - .next() - .should('have.text', 'BBD'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBA').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBB').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBC').should('be.visible'); }); it('options set in url that do not exist should load correct options', () => { @@ -188,23 +135,16 @@ describe('Variables - Load options from Url', () => { e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('X') .should('be.visible') - .children() - .children() - .first() - .click(); + .within(() => { + cy.get('input').click(); + }); e2e.components.Select.option().parent().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'A') - .next() - .should('have.text', 'B') - .next() - .should('have.text', 'C'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('C').should('be.visible'); cy.get('body').click(); diff --git a/e2e/scenes/dashboard-suite/set-options-from-ui.spec.ts b/e2e/scenes/dashboard-suite/set-options-from-ui.spec.ts index 4b5f809a930..7903488314a 100644 --- a/e2e/scenes/dashboard-suite/set-options-from-ui.spec.ts +++ b/e2e/scenes/dashboard-suite/set-options-from-ui.spec.ts @@ -12,8 +12,13 @@ describe('Variables - Set options from ui', () => { it('clicking a value that is not part of dependents options should change these to All', () => { e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&var-datacenter=A&var-server=AA&var-pod=AAA` }); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A').should('be.visible').click().click(); - e2e.components.Select.option().contains('B').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A') + .should('be.visible') + .within(() => { + cy.get('input').click(); + }); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible').click(); cy.get('body').click(); e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('B').scrollIntoView().should('be.visible'); @@ -27,21 +32,16 @@ describe('Variables - Set options from ui', () => { }); e2e.components.Select.option().parent().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'BA') - .next() - .should('have.text', 'BB') - .next() - .should('have.text', 'BC'); + + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BA').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BB').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BC').should('be.visible'); cy.get('body').click(); e2e.pages.Dashboard.SubMenu.submenuItemLabels('pod') - .next() + .parent() .within(() => { cy.get('input').click(); }); @@ -49,22 +49,13 @@ describe('Variables - Set options from ui', () => { // length is 11 because of virtualized select options e2e.components.Select.option().parent().should('have.length', 11); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'BAA') - .next() - .should('have.text', 'BAB') - .next() - .should('have.text', 'BAC') - .next() - .should('have.text', 'BAD') - .next() - .should('have.text', 'BAE') - .next() - .should('have.text', 'BAF'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAA').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAB').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAC').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAD').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAE').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAF').should('be.visible'); }); it('adding a value that is not part of dependents options should add the new values dependant options', () => { @@ -80,7 +71,7 @@ describe('Variables - Set options from ui', () => { .within(() => { cy.get('input').click(); }); - e2e.components.Select.option().contains('B').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible').click(); cy.get('body').click(); cy.wait('@query'); @@ -97,18 +88,11 @@ describe('Variables - Set options from ui', () => { e2e.components.Select.option().should('have.length', 11); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'AA') - .next() - .should('have.text', 'AB') - .next() - .should('have.text', 'AC') - .next() - .should('have.text', 'AD'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AA').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AB').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AC').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AD').should('be.visible'); cy.get('body').click(); @@ -120,16 +104,10 @@ describe('Variables - Set options from ui', () => { e2e.components.Select.option().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'AAA') - .next() - .should('have.text', 'AAB') - .next() - .should('have.text', 'AAC'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAA').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAB').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAC').should('be.visible'); }); it('removing a value that is part of dependents options should remove the new values dependant options', () => { @@ -143,23 +121,18 @@ describe('Variables - Set options from ui', () => { e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A,B') .should('be.visible') - .children() - .first() - .click(); + .within(() => { + cy.get('input').click(); + }); + + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible').click(); cy.get('body').click(); cy.wait('@query'); cy.get(`[aria-label="${selectors.components.LoadingIndicator.icon}"]`).should('not.exist'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('B') - .scrollIntoView() - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - cy.get('body').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('B').should('be.visible'); cy.get(`[aria-label="${selectors.components.LoadingIndicator.icon}"]`).should('not.exist'); @@ -171,16 +144,10 @@ describe('Variables - Set options from ui', () => { e2e.components.Select.option().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'BA') - .next() - .should('have.text', 'BB') - .next() - .should('have.text', 'BC'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BA').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BB').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BC').should('be.visible'); cy.get('body').click(0, 0); @@ -191,15 +158,8 @@ describe('Variables - Set options from ui', () => { }); e2e.components.Select.option().should('have.length', 9); - e2e.components.Select.option() - .first() - .should('have.text', 'All') - .parent() - .next() - .should('have.text', 'BBA') - .next() - .should('have.text', 'BBB') - .next() - .should('have.text', 'BBC'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBA').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBB').should('be.visible'); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBC').should('be.visible'); }); }); diff --git a/e2e/scenes/dashboard-suite/templating-dashboard-links-and-variables.spec.ts b/e2e/scenes/dashboard-suite/templating-dashboard-links-and-variables.spec.ts index 87c832d45da..829303aab41 100644 --- a/e2e/scenes/dashboard-suite/templating-dashboard-links-and-variables.spec.ts +++ b/e2e/scenes/dashboard-suite/templating-dashboard-links-and-variables.spec.ts @@ -34,24 +34,20 @@ describe('Templating', () => { e2e.components.DashboardLinks.dropDown().should('be.visible').click().wait('@tagsTemplatingSearch'); - // verify all links, should have All value verifyLinks('var-custom=p1&var-custom=p2&var-custom=p3'); cy.get('body').click(); - e2e.pages.Dashboard.SubMenu.submenuItemLabels('custom') - .next() - .should('have.text', 'All') - .parent() + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('$__all') + .should('be.visible') .within(() => { cy.get('input').click(); }); - e2e.components.Select.option().contains('p2').click(); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('p2').should('be.visible').click(); cy.get('body').click(); - e2e.components.NavToolbar.container().click(); e2e.components.DashboardLinks.dropDown() .scrollIntoView() .should('be.visible') diff --git a/e2e/scenes/panels-suite/dashlist.spec.ts b/e2e/scenes/panels-suite/dashlist.spec.ts new file mode 100644 index 00000000000..7ad25207910 --- /dev/null +++ b/e2e/scenes/panels-suite/dashlist.spec.ts @@ -0,0 +1,41 @@ +import { e2e } from '../../utils'; +const PAGE_UNDER_TEST = 'a6801696-cc53-4196-b1f9-2403e3909185/panel-tests-dashlist-variables'; + +describe('DashList panel', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + }); + + // this is to prevent the fix for https://github.com/grafana/grafana/issues/76800 from regressing + it('should pass current variable values correctly when `Include current template variable values` is set', () => { + e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST }); + + // check the initial value of the urls contain the variable value correctly + e2e.components.Panels.Panel.title('Include time range and variables enabled') + .should('be.visible') + .within(() => { + cy.get('a').each(($el) => { + cy.wrap($el).should('have.attr', 'href').and('contain', 'var-server=A'); + }); + }); + + // update variable to b + e2e.pages.Dashboard.SubMenu.submenuItemLabels('server') + .parent() + .within(() => { + cy.get('input').click(); + }); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').click(); + // blur the dropdown + cy.get('body').click(); + + // check the urls are updated with the new variable value + e2e.components.Panels.Panel.title('Include time range and variables enabled') + .should('be.visible') + .within(() => { + cy.get('a').each(($el) => { + cy.wrap($el).should('have.attr', 'href').and('contain', 'var-server=B'); + }); + }); + }); +}); diff --git a/e2e/scenes/panels-suite/datagrid-data-change.spec.ts b/e2e/scenes/panels-suite/datagrid-data-change.spec.ts new file mode 100644 index 00000000000..e7950030659 --- /dev/null +++ b/e2e/scenes/panels-suite/datagrid-data-change.spec.ts @@ -0,0 +1,36 @@ +import { e2e } from '../../utils'; + +const DASHBOARD_ID = 'c01bf42b-b783-4447-a304-8554cee1843b'; +const DATAGRID_SELECT_SERIES = 'Datagrid Select series'; + +//TODO enable this test when panel goes live +describe.skip('Datagrid data changes', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + }); + + it('Tests changing data in the grid', () => { + e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } }); + + // Check that the data is series A + e2e.components.PanelEditor.OptionsPane.fieldLabel(DATAGRID_SELECT_SERIES).should('be.visible'); + cy.get('[data-testid="glide-cell-2-0"]').should('have.text', '1'); + cy.get('[data-testid="glide-cell-2-1"]').should('have.text', '20'); + cy.get('[data-testid="glide-cell-2-2"]').should('have.text', '90'); + + // Change the series to B + e2e.components.PanelEditor.OptionsPane.fieldLabel(DATAGRID_SELECT_SERIES).find('input').type('B {enter}'); + cy.get('[data-testid="glide-cell-2-3"]').should('have.text', '30'); + cy.get('[data-testid="glide-cell-2-4"]').should('have.text', '40'); + cy.get('[data-testid="glide-cell-2-5"]').should('have.text', '50'); + + // Edit datagrid which triggers a snapshot query + cy.get('.dvn-scroller').click(200, 100); + cy.get('[data-testid="glide-cell-2-1"]').should('have.attr', 'aria-selected', 'true'); + cy.get('body').type('12{enter}', { delay: 500 }); + + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); + + cy.get('[data-testid="query-editor-row"]').contains('Snapshot'); + }); +}); diff --git a/e2e/scenes/panels-suite/datagrid-editing-features.spec.ts b/e2e/scenes/panels-suite/datagrid-editing-features.spec.ts new file mode 100644 index 00000000000..71e69b21022 --- /dev/null +++ b/e2e/scenes/panels-suite/datagrid-editing-features.spec.ts @@ -0,0 +1,159 @@ +import { e2e } from '../../utils'; + +const DASHBOARD_ID = 'c01bf42b-b783-4447-a304-8554cee1843b'; +const DATAGRID_CANVAS = 'data-grid-canvas'; + +//TODO enable this test when panel goes live +describe.skip('Datagrid data changes', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + }); + + it('Tests changing data in the grid', () => { + e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } }); + + // Edit datagrid which triggers a snapshot query + cy.get('.dvn-scroller').click(200, 100); + cy.get('[data-testid="glide-cell-2-1"]').should('have.attr', 'aria-selected', 'true'); + cy.get('body').type('123{enter}', { delay: 500 }); + + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); + + // Delete a cell + cy.get('.dvn-scroller').click(200, 200); + cy.get('body').type('{del}'); + cy.get('[data-testid="glide-cell-2-4"]').should('have.text', 0); + + // Delete a selection + cy.get('.dvn-scroller').click(50, 100, { shiftKey: true }); + cy.get('body').type('{del}'); + cy.get('[data-testid="glide-cell-2-3"]').should('have.text', 0); + cy.get('[data-testid="glide-cell-2-2"]').should('have.text', 0); + cy.get('[data-testid="glide-cell-2-1"]').should('have.text', 0); + cy.get('[data-testid="glide-cell-2-0"]').should('have.text', 1); + cy.get('[data-testid="glide-cell-1-3"]').should('have.text', ''); + cy.get('[data-testid="glide-cell-1-2"]').should('have.text', ''); + cy.get('[data-testid="glide-cell-1-1"]').should('have.text', ''); + cy.get('[data-testid="glide-cell-1-0"]').should('not.have.text', ''); + + // Clear column through context menu + cy.get('.dvn-scroller').rightclick(200, 100); + cy.get('[aria-label="Context menu"]').click(100, 120); // click clear column + cy.get('[data-testid="glide-cell-2-0"]').should('have.text', 0); + cy.get('[data-testid="glide-cell-2-4"]').should('have.text', 0); + + // Clear row through context menu + cy.get('.dvn-scroller').click(200, 220); + cy.get('body').type('1123{enter}', { delay: 500 }); + cy.get('.dvn-scroller').rightclick(200, 220); + cy.get('[aria-label="Context menu"]').click(100, 100); // click clear row + cy.get('[data-testid="glide-cell-1-4"]').should('have.text', ''); + cy.get('[data-testid="glide-cell-2-4"]').should('have.text', 0); + + // get the data back + cy.reload(); + + // Clear row through row selector + cy.get('.dvn-scroller').click(20, 190, { waitForAnimations: true }); + cy.get('.dvn-scroller').click(20, 90, { shiftKey: true, waitForAnimations: true }); // with shift to select all rows between clicks + cy.get('body').type('{del}'); + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); + cy.get('[data-testid="glide-cell-1-4"]').should('have.text', ''); + cy.get('[data-testid="glide-cell-1-3"]').should('have.text', ''); + cy.get('[data-testid="glide-cell-1-2"]').should('have.text', ''); + cy.get('[data-testid="glide-cell-1-1"]').should('have.text', ''); + cy.get('[data-testid="glide-cell-2-4"]').should('have.text', 0); + cy.get('[data-testid="glide-cell-2-3"]').should('have.text', 0); + cy.get('[data-testid="glide-cell-2-2"]').should('have.text', 0); + cy.get('[data-testid="glide-cell-2-1"]').should('have.text', 0); + cy.wait(1000); + cy.reload(); + cy.get('.dvn-scroller').click(20, 190, { waitForAnimations: true }); + cy.get('.dvn-scroller').click(20, 90, { commandKey: true, waitForAnimations: true }); // with cmd to select only clicked rows + cy.get('body').type('{del}'); + + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); + + cy.get('[data-testid="glide-cell-1-1"]').should('have.text', ''); + cy.get('[data-testid="glide-cell-2-1"]').should('have.text', 0); + cy.get('[data-testid="glide-cell-2-4"]').should('have.text', 0); + cy.get('[data-testid="glide-cell-1-4"]').should('have.text', ''); + + // Remove all data + cy.get('.dvn-scroller').rightclick(100, 100); + cy.get('body').click(150, 420); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] th`).should('have.length', 0); + + cy.reload(); + + // Delete column through header dropdown menu + cy.get('.dvn-scroller').click(250, 15); // click header dropdown + cy.get('body').click(450, 420); // click delete column + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] th`).should('have.length', 1); + + // Delete row through context menu + cy.get('.dvn-scroller').rightclick(100, 100); + cy.get('[aria-label="Context menu"]').click(10, 10); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] tbody tr`).should('have.length', 6); // there are 5 data rows + 1 for the add new row btns + + // Delete rows through row selector + cy.get('.dvn-scroller').click(20, 190, { waitForAnimations: true }); + cy.get('.dvn-scroller').click(20, 90, { shiftKey: true, waitForAnimations: true }); // with shift to select all rows between clicks + cy.get('.dvn-scroller').rightclick(100, 100); + cy.get('[aria-label="Context menu"]').click(10, 10); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] tbody tr`).should('have.length', 2); // there are 1 data rows + 1 for the add new row btns + cy.reload(); + cy.get('.dvn-scroller').click(20, 190, { waitForAnimations: true }); + cy.get('.dvn-scroller').click(20, 90, { commandKey: true, waitForAnimations: true }); // with shift to select all rows between clicks + cy.get('.dvn-scroller').rightclick(40, 90); + cy.get('[aria-label="Context menu"]').click(10, 10); + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] tbody tr`).should('have.length', 5); // there are 5 data rows + 1 for the add new row btns + + // Delete column through context menu + cy.get('.dvn-scroller').rightclick(100, 100); + cy.get('[aria-label="Context menu"]').click(10, 50); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] th`).should('have.length', 1); + + cy.reload(); + cy.wait(3000); + + // Add a new column + cy.get('body').click(350, 200).type('New Column{enter}'); + cy.get('[data-testid="data-testid Confirm Modal Danger Button"]').click(); + cy.get('body') + .click(350, 230) + .type('Value 1{enter}') + .type('Value 2{enter}') + .type('Value 3{enter}') + .type('Value 4{enter}') + .type('Value 5{enter}') + .type('Value 6{enter}'); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] th`).should('have.length', 3); + + // Rename a column + cy.get('.dvn-scroller').click(250, 15); // click header dropdown + cy.get('body').click(450, 380).type('{selectall}{backspace}Renamed column{enter}'); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] th`).contains('Renamed column'); + + // Change column field type + cy.get('.dvn-scroller').click(310, 15); + cy.get('[aria-label="Context menu"]').click(50, 50); + cy.get('.dvn-scroller').click(200, 100); + cy.get('body').type('Str Value{enter}'); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] tr`).contains('Str Value'); + + // Select all rows through row selection + cy.get('.dvn-scroller').click(10, 10, { waitForAnimations: true }); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] [aria-selected="true"]`).should('have.length', 6); + + // Add a new row + cy.get('.dvn-scroller').click(200, 250); + cy.get('body').type('123'); + cy.get('.dvn-scroller').click(50, 250); + cy.get('body').type('Val{enter}'); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] tbody tr`).contains('Val'); + cy.get(`[data-testid="${DATAGRID_CANVAS}"] tbody tr`).should('have.length', 8); + }); +}); diff --git a/e2e/scenes/panels-suite/frontend-sandbox-panel.spec.ts b/e2e/scenes/panels-suite/frontend-sandbox-panel.spec.ts new file mode 100644 index 00000000000..1541be609d9 --- /dev/null +++ b/e2e/scenes/panels-suite/frontend-sandbox-panel.spec.ts @@ -0,0 +1,128 @@ +import panelSandboxDashboard from '../../dashboards/PanelSandboxDashboard.json'; +import { e2e } from '../../utils'; + +const DASHBOARD_ID = 'c46b2460-16b7-42a5-82d1-b07fbf431950'; + +describe('Panel sandbox', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'), true); + return e2e.flows.importDashboard(panelSandboxDashboard, 1000, true); + }); + + describe('Sandbox disabled', () => { + beforeEach(() => { + cy.window().then((win) => { + win.localStorage.setItem('grafana.featureToggles', 'pluginsFrontendSandbox=0'); + }); + cy.reload(); + }); + + it('Add iframes to body', () => { + // this button adds iframes to the body + cy.get('[data-testid="button-create-iframes"]').click(); + + const iframeIds = [ + 'createElementIframe', + 'innerHTMLIframe', + 'appendIframe', + 'prependIframe', + 'afterIframe', + 'beforeIframe', + 'outerHTMLIframe', + 'parseFromStringIframe', + 'insertBeforeIframe', + 'replaceChildIframe', + ]; + iframeIds.forEach((id) => { + cy.get(`#${id}`).should('exist'); + }); + }); + + it('Reaches out of panel div', () => { + // this button reaches out of the panel div and modifies the element dataset + cy.get('[data-testid="button-reach-out"]').click(); + + cy.get('[data-sandbox-test="true"]').should('exist'); + }); + + it('Reaches out of the panel editor', () => { + e2e.flows.openDashboard({ + uid: DASHBOARD_ID, + queryParams: { + editPanel: 1, + }, + }); + + cy.get('[data-testid="panel-editor-custom-editor-input"]').should('not.be.disabled'); + cy.get('[data-testid="panel-editor-custom-editor-input"]').type('x', { force: true }); + cy.get('[data-sandbox-test="panel-editor"]').should('exist'); + }); + }); + + describe('Sandbox enabled', () => { + beforeEach(() => { + cy.window().then((win) => { + win.localStorage.setItem('grafana.featureToggles', 'pluginsFrontendSandbox=1'); + }); + cy.reload(); + }); + + it('Does not add iframes to body', () => { + // this button adds 3 iframes to the body + cy.get('[data-testid="button-create-iframes"]').click(); + cy.wait(100); // small delay to prevent false positives from too fast tests + + const iframeIds = [ + 'createElementIframe', + 'innerHTMLIframe', + 'appendIframe', + 'prependIframe', + 'afterIframe', + 'beforeIframe', + 'outerHTMLIframe', + 'parseFromStringIframe', + 'insertBeforeIframe', + 'replaceChildIframe', + ]; + iframeIds.forEach((id) => { + cy.get(`#${id}`).should('not.exist'); + }); + }); + + it('Does not reaches out of panel div', () => { + // this button reaches out of the panel div and modifies the element dataset + cy.get('[data-testid="button-reach-out"]').click(); + cy.wait(100); // small delay to prevent false positives from too fast tests + cy.get('[data-sandbox-test="true"]').should('not.exist'); + }); + + it('Does not Reaches out of the panel editor', () => { + e2e.flows.openDashboard({ + uid: DASHBOARD_ID, + queryParams: { + editPanel: 1, + }, + }); + + cy.get('[data-testid="panel-editor-custom-editor-input"]').should('not.be.disabled'); + cy.get('[data-testid="panel-editor-custom-editor-input"]').type('x', { force: true }); + cy.wait(100); // small delay to prevent false positives from too fast tests + cy.get('[data-sandbox-test="panel-editor"]').should('not.exist'); + }); + + it('Can access specific window global variables', () => { + cy.get('[data-testid="button-test-globals"]').click(); + cy.get('[data-sandbox-global="Prism"]').should('be.visible'); + cy.get('[data-sandbox-global="jQuery"]').should('be.visible'); + cy.get('[data-sandbox-global="location"]').should('be.visible'); + }); + }); + + afterEach(() => { + e2e.flows.revertAllChanges(); + }); + + after(() => { + return cy.clearCookies(); + }); +}); diff --git a/e2e/scenes/panels-suite/geomap-layer-types.spec.ts b/e2e/scenes/panels-suite/geomap-layer-types.spec.ts new file mode 100644 index 00000000000..2e60e88f3f5 --- /dev/null +++ b/e2e/scenes/panels-suite/geomap-layer-types.spec.ts @@ -0,0 +1,99 @@ +import { e2e } from '../../utils'; + +const DASHBOARD_ID = 'P2jR04WVk'; + +const MAP_LAYERS_TYPE = 'Map layers Layer type'; +const MAP_LAYERS_DATA = 'Map layers Data'; +const MAP_LAYERS_GEOJSON = 'Map layers GeoJSON URL'; + +describe('Geomap layer types', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + }); + + it('Tests changing the layer type', () => { + e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } }); + cy.get('[data-testid="layer-drag-drop-list"]').should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).should('be.visible'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('markers'); + + // Heatmap + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).type('Heatmap{enter}'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('heatmap'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA).should('be.visible'); + // e2e.components.PanelEditor.General.content().should('be.visible'); + + // GeoJSON + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).type('GeoJSON{enter}'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('geojson'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA).should('not.exist'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_GEOJSON).should('be.visible'); + // e2e.components.PanelEditor.General.content().should('be.visible'); + + // Open Street Map + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).type('Open Street Map{enter}'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('osm-standard'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA).should('not.exist'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_GEOJSON).should('not.exist'); + // e2e.components.PanelEditor.General.content().should('be.visible'); + + // CARTO basemap + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).type('CARTO basemap{enter}'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('carto'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers Show labels').should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers Theme').should('be.visible'); + // e2e.components.PanelEditor.General.content().should('be.visible'); + + // ArcGIS MapServer + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).type('ArcGIS MapServer{enter}'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('esri-xyz'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers Server instance').should('be.visible'); + // e2e.components.PanelEditor.General.content().should('be.visible'); + + // XYZ Tile layer + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).type('XYZ Tile layer{enter}'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('xyz'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers URL template').should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers Attribution').should('be.visible'); + // e2e.components.PanelEditor.General.content().should('be.visible'); + }); + + it.skip('Tests changing the layer type (alpha)', () => { + e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } }); + cy.get('[data-testid="layer-drag-drop-list"]').should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).should('be.visible'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('markers'); + + // Icon at last point (Alpha) + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).type('Icon at last point{enter}'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('last-point-tracker'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA).should('be.visible'); + e2e.components.PanelEditor.General.content().should('be.visible'); + + // Dynamic GeoJSON (Alpha) + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).type('Dynamic GeoJSON{enter}'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('dynamic-geojson'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA).should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_GEOJSON).should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers ID Field').should('be.visible'); + e2e.components.PanelEditor.General.content().should('be.visible'); + + // Night / Day (Alpha) + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).type('Night / Day{enter}'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('dayNight'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA).should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers Show').should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers Night region color').should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers Display sun').should('be.visible'); + e2e.components.PanelEditor.General.content().should('be.visible'); + + // Route (Alpha) + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_TYPE).type('Route{enter}'); + cy.get('[data-testid="layer-drag-drop-list"]').contains('route'); + e2e.components.PanelEditor.OptionsPane.fieldLabel(MAP_LAYERS_DATA).should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers Location').should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers Style').should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Map layers Line width').should('be.visible'); + e2e.components.PanelEditor.General.content().should('be.visible'); + }); +}); diff --git a/e2e/scenes/panels-suite/geomap-map-controls.spec.ts b/e2e/scenes/panels-suite/geomap-map-controls.spec.ts new file mode 100644 index 00000000000..21ba2f73c8b --- /dev/null +++ b/e2e/scenes/panels-suite/geomap-map-controls.spec.ts @@ -0,0 +1,58 @@ +import { e2e } from '../../utils'; +const DASHBOARD_ID = 'P2jR04WVk'; + +describe('Geomap layer controls options', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + }); + + it('Tests map controls options', () => { + e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } }); + e2e.components.OptionsGroup.group('Map controls').scrollIntoView().should('exist'); + + // Show zoom + e2e.components.PanelEditor.showZoomField() + .should('exist') + .within(() => { + cy.get('input[type="checkbox"]').check({ force: true }); + }); + + cy.contains('+'); + cy.get('.ol-zoom').should('exist'); + + // Show attribution + e2e.components.PanelEditor.showAttributionField() + .should('exist') + .within(() => { + cy.get('input[type="checkbox"]').check({ force: true }); + }); + + cy.get('.ol-attribution').should('exist'); + + // Show scale + e2e.components.PanelEditor.showScaleField() + .should('exist') + .within(() => { + cy.get('input[type="checkbox"]').check({ force: true }); + }); + + cy.get('.ol-scale-line').should('exist'); + + // Show measure tool + e2e.components.PanelEditor.showMeasureField() + .should('exist') + .within(() => { + cy.get('input[type="checkbox"]').check({ force: true }); + }); + + e2e.components.PanelEditor.measureButton().should('exist'); + + // Show debug + e2e.components.PanelEditor.showDebugField() + .should('exist') + .within(() => { + cy.get('input[type="checkbox"]').check({ force: true }); + }); + e2e.components.DebugOverlay.wrapper().should('exist'); + }); +}); diff --git a/e2e/scenes/panels-suite/geomap-spatial-operations-transform.spec.ts b/e2e/scenes/panels-suite/geomap-spatial-operations-transform.spec.ts new file mode 100644 index 00000000000..66fb1f102e0 --- /dev/null +++ b/e2e/scenes/panels-suite/geomap-spatial-operations-transform.spec.ts @@ -0,0 +1,98 @@ +import { e2e } from '../../utils'; + +const DASHBOARD_ID = 'P2jR04WVk'; + +describe('Geomap spatial operations', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + }); + + it('Tests location auto option', () => { + e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } }); + e2e.components.Tab.title('Transformations').should('be.visible').click(); + e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click(); + + e2e.components.TransformTab.newTransform('Spatial operations').scrollIntoView().should('be.visible').click(); + e2e.components.Transforms.SpatialOperations.actionLabel().type('Prepare spatial field{enter}'); + e2e.components.Transforms.SpatialOperations.locationLabel().should('be.visible'); + e2e.components.Transforms.SpatialOperations.locationLabel().type('Auto{enter}'); + + e2e.components.PanelEditor.toggleTableView().click({ force: true }); + e2e.components.Panels.Visualization.Table.header() + .should('be.visible') + .within(() => { + cy.contains('Point').should('be.visible'); + }); + }); + + it('Tests location coords option', () => { + e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } }); + e2e.components.Tab.title('Transformations').should('be.visible').click(); + e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click(); + + e2e.components.TransformTab.newTransform('Spatial operations').scrollIntoView().should('be.visible').click(); + e2e.components.Transforms.SpatialOperations.actionLabel().type('Prepare spatial field{enter}'); + e2e.components.Transforms.SpatialOperations.locationLabel().should('be.visible'); + e2e.components.Transforms.SpatialOperations.locationLabel().type('Coords{enter}'); + + e2e.components.Transforms.SpatialOperations.location.coords.latitudeFieldLabel().should('be.visible'); + e2e.components.Transforms.SpatialOperations.location.coords.latitudeFieldLabel().type('Lat{enter}'); + + e2e.components.Transforms.SpatialOperations.location.coords.longitudeFieldLabel().should('be.visible'); + e2e.components.Transforms.SpatialOperations.location.coords.longitudeFieldLabel().type('Lng{enter}'); + + e2e.components.PanelEditor.toggleTableView().click({ force: true }); + e2e.components.Panels.Visualization.Table.header() + .should('be.visible') + .within(() => { + cy.contains('Point').should('be.visible'); + }); + }); + + it('Tests geoshash field column appears in table view', () => { + e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } }); + e2e.components.Tab.title('Transformations').should('be.visible').click(); + e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click(); + + e2e.components.TransformTab.newTransform('Spatial operations').scrollIntoView().should('be.visible').click(); + e2e.components.Transforms.SpatialOperations.actionLabel().type('Prepare spatial field{enter}'); + e2e.components.Transforms.SpatialOperations.locationLabel().should('be.visible'); + e2e.components.Transforms.SpatialOperations.locationLabel().type('Geohash{enter}'); + + e2e.components.Transforms.SpatialOperations.location.geohash + .geohashFieldLabel() + .should('be.visible') + .type('State{enter}'); + + e2e.components.PanelEditor.toggleTableView().click({ force: true }); + e2e.components.Panels.Visualization.Table.header() + .should('be.visible') + .within(() => { + cy.contains('State 1').should('be.visible'); + }); + }); + + it('Tests location lookup option', () => { + e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } }); + e2e.components.Tab.title('Transformations').should('be.visible').click(); + e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click(); + + e2e.components.TransformTab.newTransform('Spatial operations').scrollIntoView().should('be.visible').click(); + e2e.components.Transforms.SpatialOperations.actionLabel().type('Prepare spatial field{enter}'); + e2e.components.Transforms.SpatialOperations.locationLabel().should('be.visible'); + e2e.components.Transforms.SpatialOperations.locationLabel().type('Lookup{enter}'); + + e2e.components.Transforms.SpatialOperations.location.lookup.lookupFieldLabel().should('be.visible'); + e2e.components.Transforms.SpatialOperations.location.lookup.lookupFieldLabel().type('State{enter}'); + + e2e.components.Transforms.SpatialOperations.location.lookup.gazetteerFieldLabel().should('be.visible'); + e2e.components.Transforms.SpatialOperations.location.lookup.gazetteerFieldLabel().type('USA States{enter}'); + + e2e.components.PanelEditor.toggleTableView().click({ force: true }); + e2e.components.Panels.Visualization.Table.header() + .should('be.visible') + .within(() => { + cy.contains('Geometry').should('be.visible'); + }); + }); +}); diff --git a/e2e/scenes/panels-suite/panelEdit_base.spec.ts b/e2e/scenes/panels-suite/panelEdit_base.spec.ts new file mode 100644 index 00000000000..e8d0e301aff --- /dev/null +++ b/e2e/scenes/panels-suite/panelEdit_base.spec.ts @@ -0,0 +1,103 @@ +import { selectors } from '@grafana/e2e-selectors'; + +import { e2e } from '../../utils'; + +const PANEL_UNDER_TEST = 'Lines 500 data points'; + +describe('Panel edit tests', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + }); + + it('Tests various Panel edit scenarios', () => { + cy.intercept({ + pathname: '/api/ds/query', + }).as('query'); + e2e.flows.openDashboard({ uid: 'TkZXxlNG3' }); + cy.wait('@query'); + + e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Edit, PANEL_UNDER_TEST); + + // // New panel editor opens when navigating from Panel menu + e2e.components.PanelEditor.General.content().should('be.visible'); + + // Queries tab is rendered and open by default + e2e.components.PanelEditor.DataPane.content() + .scrollIntoView() + .should('be.visible') + .within(() => { + e2e.components.Tab.title('Queries').should('be.visible'); + // data should be the active tab + e2e.components.Tab.active().within((li: JQuery) => { + expect(li.text()).equals('Queries1'); // there's already a query so therefore Query + 1 + }); + // cy.get('[data-testid]="query-editor-rows"').should('be.visible'); + cy.get(`[data-testid="${selectors.components.QueryTab.content}"]`).should('be.visible'); + e2e.components.TransformTab.content().should('not.exist'); + e2e.components.AlertTab.content().should('not.exist'); + e2e.components.PanelAlertTabContent.content().should('not.exist'); + + // Bottom pane tabs + // Can change to Transform tab + e2e.components.Tab.title('Transformations').should('be.visible').click(); + e2e.components.Tab.active().within((li: JQuery) => { + expect(li.text()).equals('Transformations0'); // there's no transform so therefore Transform + 0 + }); + e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible'); + cy.get(`[data-testid="${selectors.components.QueryTab.content}"]`).should('not.exist'); + e2e.components.AlertTab.content().should('not.exist'); + e2e.components.PanelAlertTabContent.content().should('not.exist'); + + // Can change to Alerts tab (graph panel is the default vis so the alerts tab should be rendered) + e2e.components.Tab.title('Alert').scrollIntoView().should('be.visible').click(); + e2e.components.Tab.active().should('have.text', 'Alert0'); // there's no alert so therefore Alert + 0 + + // Needs to be disabled until Grafana EE turns unified alerting on by default + // e2e.components.AlertTab.content().should('not.exist'); + + cy.get(`[data-testid="${selectors.components.QueryTab.content}"]`).should('not.exist'); + e2e.components.TransformTab.content().should('not.exist'); + + // Needs to be disabled until Grafana EE turns unified alerting on by default + // e2e.components.PanelAlertTabContent.content().should('exist'); + // e2e.components.PanelAlertTabContent.content().should('be.visible'); + + e2e.components.Tab.title('Queries').should('be.visible').click(); + }); + + // Check that Time series is chosen + e2e.components.PanelEditor.toggleVizPicker().click(); + e2e.components.PluginVisualization.item('Time series').should('be.visible'); + e2e.components.PluginVisualization.current().should((e) => expect(e).to.contain('Time series')); + + // Check that table view works + e2e.components.Panels.Panel.loadingBar().should('not.exist'); + e2e.components.PanelEditor.toggleTableView().click({ force: true }); + e2e.components.Panels.Visualization.Table.header() + .should('be.visible') + .within(() => { + cy.contains('A-series').should('be.visible'); + }); + + // Change to Text panel + e2e.components.PluginVisualization.item('Text').scrollIntoView().should('be.visible').click(); + e2e.components.PanelEditor.toggleVizPicker().should((e) => expect(e).to.contain('Text')); + + // Data pane should not be rendered + e2e.components.PanelEditor.DataPane.content().should('not.exist'); + + // Change to Table panel + e2e.components.PanelEditor.toggleVizPicker().click(); + e2e.components.PluginVisualization.item('Table').scrollIntoView().should('be.visible').click(); + e2e.components.PanelEditor.toggleVizPicker().should((e) => expect(e).to.contain('Table')); + + // Data pane should be rendered + e2e.components.PanelEditor.DataPane.content().should('be.visible'); + + // Field & Overrides tabs (need to switch to React based vis, i.e. Table) + e2e.components.PanelEditor.toggleTableView().click({ force: true }).click({ force: true }); + + e2e.components.PanelEditor.OptionsPane.fieldLabel('Table Show table header').should('be.visible'); + e2e.components.PanelEditor.OptionsPane.fieldLabel('Table Column width').should('be.visible'); + }); +}); diff --git a/e2e/scenes/panels-suite/panelEdit_queries.spec.ts b/e2e/scenes/panels-suite/panelEdit_queries.spec.ts new file mode 100644 index 00000000000..a06b05a1668 --- /dev/null +++ b/e2e/scenes/panels-suite/panelEdit_queries.spec.ts @@ -0,0 +1,99 @@ +import { e2e } from '../../utils'; + +const flakyTimeout = 10000; + +describe('Panel edit tests - queries', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + }); + + it('Tests various Panel edit queries scenarios', () => { + e2e.flows.openDashboard({ uid: '5SdHCadmz', queryParams: { editPanel: 3 } }); + + // New panel editor opens when navigating from Panel menu + e2e.components.PanelEditor.General.content().should('be.visible'); + + // Queries tab is rendered and open by default + e2e.components.PanelEditor.DataPane.content().should('be.visible'); + + // We expect row with refId A to exist and be visible + e2e.components.QueryEditorRows.rows().within((rows) => { + expect(rows.length).equals(1); + }); + + // Add query button should be visible and clicking on it should create a new row + e2e.components.QueryTab.addQuery().scrollIntoView().should('be.visible').click(); + + // We expect row with refId A and B to exist and be visible + e2e.components.QueryEditorRows.rows({ timeout: flakyTimeout }).should('have.length', 2); + + // Remove refId A + e2e.components.QueryEditorRow.actionButton('Remove query').eq(0).scrollIntoView(); + e2e.components.QueryEditorRow.actionButton('Remove query').eq(0).should('be.visible').click(); + + // We expect row with refId B to exist and be visible + e2e.components.QueryEditorRows.rows({ timeout: flakyTimeout }).should('have.length', 1); + + // Duplicate refId B + e2e.components.QueryEditorRow.actionButton('Duplicate query').eq(0).should('be.visible').click(); + + // We expect row with refId Band and A to exist and be visible + e2e.components.QueryEditorRows.rows().should('have.length', 2); + + // Change to CSV Metric Values scenario for A + e2e.components.DataSource.TestData.QueryTab.scenarioSelectContainer() + .first() + .should('be.visible') + .within(() => { + cy.get('input[id*="test-data-scenario-select-"]').eq(0).should('be.visible').click(); + }); + + cy.contains('CSV Metric Values').scrollIntoView().should('be.visible').eq(0).click(); + + // Disable / enable row + expectInspectorResultAndClose((keys) => { + const length = keys.length; + const resultIds = new Set([ + keys[length - 2].innerText, // last 2 + keys[length - 1].innerText, // last 2 + ]); + + expect(resultIds.has('A:')).equals(true); + expect(resultIds.has('B:')).equals(true); + }); + + // Hide response for row with refId A + e2e.components.QueryEditorRow.actionButton('Hide response').eq(1).should('be.visible').click(); + + expectInspectorResultAndClose((keys) => { + const length = keys.length; + expect(keys[length - 1].innerText).equals('B:'); + }); + + // Show response for row with refId A + e2e.components.QueryEditorRow.actionButton('Hide response').eq(1).should('be.visible').click(); + + expectInspectorResultAndClose((keys) => { + const length = keys.length; + const resultIds = new Set([ + keys[length - 2].innerText, // last 2 + keys[length - 1].innerText, // last 2 + ]); + + expect(resultIds.has('A:')).equals(true); + expect(resultIds.has('B:')).equals(true); + }); + }); +}); + +const expectInspectorResultAndClose = (expectCallBack: (keys: JQuery) => void) => { + e2e.components.QueryTab.queryInspectorButton().should('be.visible').click(); + + e2e.components.PanelInspector.Query.refreshButton().should('be.visible').click(); + + e2e.components.PanelInspector.Query.jsonObjectKeys({ timeout: flakyTimeout }) + .should('be.visible') + .should((keys) => expectCallBack(keys)); + + e2e.components.Drawer.General.close().should('be.visible').click(); +}; diff --git a/e2e/scenes/panels-suite/panelEdit_transforms.spec.ts b/e2e/scenes/panels-suite/panelEdit_transforms.spec.ts new file mode 100644 index 00000000000..ef14d6e55a5 --- /dev/null +++ b/e2e/scenes/panels-suite/panelEdit_transforms.spec.ts @@ -0,0 +1,28 @@ +import { e2e } from '../../utils'; + +describe('Panel edit tests - transformations', () => { + beforeEach(() => { + e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); + }); + + it('Tests transformations editor', () => { + e2e.flows.openDashboard({ uid: 'TkZXxlNG3', queryParams: { editPanel: 47 } }); + + e2e.components.Tab.title('Transformations').should('be.visible').click(); + e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click(); + e2e.components.TransformTab.newTransform('Reduce').scrollIntoView().should('be.visible').click(); + e2e.components.Transforms.Reduce.calculationsLabel().scrollIntoView().should('be.visible'); + e2e.components.Transforms.Reduce.modeLabel().should('be.visible'); + }); + + it('Tests case where transformations can be disabled and not clear out panel data', () => { + e2e.flows.openDashboard({ uid: 'TkZXxlNG3', queryParams: { editPanel: 47 } }); + + e2e.components.Tab.title('Transformations').should('be.visible').click(); + e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click(); + e2e.components.TransformTab.newTransform('Reduce').scrollIntoView().should('be.visible').click(); + e2e.components.Transforms.disableTransformationButton().should('be.visible').click(); + + e2e.components.Panels.Panel.PanelDataErrorMessage().should('not.exist'); + }); +}); diff --git a/package.json b/package.json index d973e060463..d81c473269b 100644 --- a/package.json +++ b/package.json @@ -258,7 +258,7 @@ "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", "@grafana/saga-icons": "workspace:*", - "@grafana/scenes": "^4.21.0", + "@grafana/scenes": "^4.23.1", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx index 66a5c9816f5..24a2428eb34 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { Unsubscribable } from 'rxjs'; import { GrafanaTheme2 } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; import { SceneComponentProps, SceneObjectBase, @@ -144,7 +145,7 @@ function PanelDataPaneRendered({ model }: SceneComponentProps) { const currentTab = tabs.find((t) => t.tabId === tab); return ( -
+
{tabs.map((t, index) => { return ( diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx index 04bc7c4295c..2d484422eea 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx @@ -189,7 +189,7 @@ export function PanelDataQueriesTabRendered({ model }: SceneComponentProps +
- +
); } diff --git a/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx b/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx index c850073a94f..0f7757f1b42 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx @@ -2,6 +2,7 @@ import { css, cx } from '@emotion/css'; import React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; import { SceneComponentProps } from '@grafana/scenes'; import { Button, ToolbarButton, useStyles2 } from '@grafana/ui'; @@ -32,7 +33,7 @@ export function PanelEditorRenderer({ model }: SceneComponentProps) return ( <> -
+
diff --git a/yarn.lock b/yarn.lock index 918b1291b1a..7b982840a48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3047,13 +3047,13 @@ __metadata: linkType: soft "@grafana/e2e-selectors@npm:^10.4.1": - version: 10.4.2 - resolution: "@grafana/e2e-selectors@npm:10.4.2" + version: 10.4.3 + resolution: "@grafana/e2e-selectors@npm:10.4.3" dependencies: "@grafana/tsconfig": "npm:^1.2.0-rc1" tslib: "npm:2.6.2" typescript: "npm:5.3.3" - checksum: 10/a8eb622ff0137eb759cead6d7cc81c1fc9e00a6666655cf3043ea8ba23aada8e1148810471ac92911b412854028a527fd75be7a9519e4fa7826a111c555b5ffb + checksum: 10/c8799f2f4440e22f3a39f41e8ec465fe35ae2c459142e52486ce88dd176cef8212905151b03d9c48e1708c5a0f342d74054bfecb84e64dc9bf0f459c36c0464b languageName: node linkType: hard @@ -3501,9 +3501,9 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes@npm:^4.21.0": - version: 4.21.1 - resolution: "@grafana/scenes@npm:4.21.1" +"@grafana/scenes@npm:^4.23.1": + version: 4.23.1 + resolution: "@grafana/scenes@npm:4.23.1" dependencies: "@grafana/e2e-selectors": "npm:^10.4.1" react-grid-layout: "npm:1.3.4" @@ -3517,7 +3517,7 @@ __metadata: "@grafana/ui": ^10.4.1 react: ^18.0.0 react-dom: ^18.0.0 - checksum: 10/f0aa7d71f1d1d5d93f53314bc87da5e45e788b0020ab19c9f4e482025dff9e9d6424af1bf7e9e0cb3fcb7bdddddfa03567e481ea2645a1852369e27a002cde59 + checksum: 10/7d0ad670cf5b93b8d82e09091fa3bf8f257cd07fcd1a2205f510f417be2b64132fbe889b566e5212ecb5d096c7eaf78e762a961b123143c20b68567a28935e41 languageName: node linkType: hard @@ -16736,7 +16736,7 @@ __metadata: "@grafana/prometheus": "workspace:*" "@grafana/runtime": "workspace:*" "@grafana/saga-icons": "workspace:*" - "@grafana/scenes": "npm:^4.21.0" + "@grafana/scenes": "npm:^4.23.1" "@grafana/schema": "workspace:*" "@grafana/sql": "workspace:*" "@grafana/tsconfig": "npm:^1.3.0-rc1"