import {Page, Response, expect} from '@playwright/test'; import {clickSelectOption, openTestPage, slct} from '../../../utils'; import datalensTest from '../../../utils/playwright/globalTestDefinition'; import {TestParametrizationConfig} from '../../../types/config'; import {getUrlStateParam} from '../../../suites/dash/helpers'; import {CommonUrls} from '../../../page-objects/constants/common-urls'; import {ChartkitMenuDialogsQA, ControlQA, ControlType, WizardType} from '../../../../src/shared'; import DashboardPage from '../../../page-objects/dashboard/DashboardPage'; const SELECTORS = { TAB_SELECTOR_TEXT: '.yc-select-control__tokens-text', EMPTY_SELECTOR_VAL: '.yc-select-control__tokens-text_empty', }; const PARAMS = { TAB_1_SELECT_VALUE_1: 'Abilene', TAB_1_SELECT_VALUE_2: 'Aberdeen', TAB_2_SELECT_VALUE_1: 'Allen', TAB_4_SELECT_VALUE_1: 'Akron', EMPTY_SELECTOR_VAL_TEXT: '—', EMPTY_SELECTOR_VAL_FULL_TEXT: 'No selected values', TAB_1_NAME: 'Tab 1', TAB_2_NAME: 'Tab 2', TAB_4_NAME: 'Tab 4', }; const TIMEOUT = 4000; const getStateFromResponse = async (response: Promise) => { const receivedResponse = await response; const jsonResponse = await receivedResponse.json(); return jsonResponse.hash; }; const selectOptionAndGetState = async ({ page, selector, value, }: { page: Page; selector: string; value: string; }) => { const stateChangeResponse = page.waitForResponse(CommonUrls.CreateDashState); await Promise.all([page.waitForNavigation(), clickSelectOption(page, selector, value)]); const stateFromRequest = await getStateFromResponse(stateChangeResponse); const stateParam = getUrlStateParam(page); expect(stateParam).toEqual(stateFromRequest); return stateFromRequest; }; const waitBackGetUrlStateParam = async (page: Page): Promise => { await Promise.all([page.waitForNavigation(), page.goBack()]); await page.waitForTimeout(TIMEOUT); return getUrlStateParam(page); }; const waitForWidgetsRunAndGetChartResponse = async ( page: Page, action?: () => Promise, ): Promise => { let chartResponseData; const chartResponse = page.waitForResponse(async (response: Response) => { if (response.url().includes(CommonUrls.ApiRun)) { expect(response.status()).toEqual(200); const jsonResponse = await response.json(); if ( jsonResponse.type === WizardType.GraphWizardNode || jsonResponse.type === WizardType.GravityChartsWizardNode ) { chartResponseData = response; return true; } } return false; }); const controlResponse = page.waitForResponse(async (response: Response) => { if (response.url().includes(CommonUrls.ApiRun)) { expect(response.status()).toEqual(200); const jsonResponse = await response.json(); if (jsonResponse.type === ControlType.Dash) { return true; } } return false; }); await action?.(); await Promise.all([chartResponse, controlResponse]); return chartResponseData; }; const checkChartWithValuesAndGetResponse = async ({ page, rightValues, wrongValues, action, }: { page: Page; rightValues: string[]; wrongValues?: string[]; action?: () => Promise; }) => { // Wait for load of chart and selector const chartResponseData = await waitForWidgetsRunAndGetChartResponse(page, action); const graph = page .locator(`${slct(ChartkitMenuDialogsQA.chartWidget)} svg:first-child`) .first(); await expect(graph).toBeVisible(); // Check that the chart has been drawn with the applied values of the selector parameters based on the dataset for (const rightValue of rightValues) { const chartWidgetWithRightValue = page .locator(`${slct(ChartkitMenuDialogsQA.chartWidget)} >> text=${rightValue}`) .first(); await expect(chartWidgetWithRightValue).toBeAttached(); } if (wrongValues) { for (const wrongValue of wrongValues) { const chartWidgetWithRightValue = page .locator(`${slct(ChartkitMenuDialogsQA.chartWidget)} >> text=${wrongValue}`) .first(); await expect(chartWidgetWithRightValue).toBeHidden(); } } return chartResponseData; }; datalensTest.describe('Dashboards - States with tabs', () => { datalensTest.beforeEach( async ({page, config}: {page: Page; config: TestParametrizationConfig}) => { // waiting for api/run for selector and chart await waitForWidgetsRunAndGetChartResponse(page, async () => { await openTestPage(page, config.dash.urls.DashboardWithTabsAndSelectors); }); }, ); datalensTest( 'Checking that the state parameter was added when the selector was changed and reset when switching to another tab', async ({page}: {page: Page}) => { const dashboardPage = new DashboardPage({page}); // Check that the state parameter has been updated await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_1_SELECT_VALUE_1, }); // Click on another tab await dashboardPage.changeTab({tabName: PARAMS.TAB_2_NAME}); // Waiting for rendering on the second tab await page.waitForSelector(`${slct(ControlQA.controlSelect)}`); // Check that the state parameter has been reset expect(getUrlStateParam(page)).toEqual(null); }, ); datalensTest( 'Checking that the state changes correctly and resets when switching between tabs using the browser\'s "Back" button', async ({page}: {page: Page}) => { const dashboardPage = new DashboardPage({page}); // Check that the state parameter has been updated const firstTabState = await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_1_SELECT_VALUE_1, }); // Click on another tab await dashboardPage.changeTab({tabName: PARAMS.TAB_2_NAME}); // Waiting for rendering on the second tab await page.waitForSelector(slct(ControlQA.controlSelect)); // Check that the state parameter has been reset expect(getUrlStateParam(page)).toEqual(null); // Click on the fourth tab and wait for the page url to change const urlParam = await dashboardPage.changeTabAndGetState({ tabName: PARAMS.TAB_4_NAME, timeout: TIMEOUT, }); // Check that the state parameter has been reset expect(urlParam).toEqual(null); // Select the value of the external selector on the fourth tab and // waiting for the deferred request to create a state and add it to the url to pass const fourthTabState = await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_4_SELECT_VALUE_1, }); // Check that the state parameter was updated when the external selector was changed expect(fourthTabState).not.toEqual(firstTabState); // Click on the native browser button "Back" and wait for the reset of the state await page.goBack(); expect(getUrlStateParam(page)).toEqual(null); // Click on the native browser button "Back" and wait for the transition to the second tab that did not have a state await page.goBack(); await page.waitForSelector(slct(ControlQA.controlSelect)); expect(getUrlStateParam(page)).toEqual(null); // Click on the native browser button "Back" and wait for the transition to the first tab that has a state await waitForWidgetsRunAndGetChartResponse(page, async () => { await page.goBack(); }); expect(getUrlStateParam(page)).toEqual(firstTabState); // Click on the native browser button "Forward" and wait for the transition to the second tab that did not have a state await page.goForward(); await page.waitForSelector(slct(ControlQA.controlSelect)); expect(getUrlStateParam(page)).toEqual(null); // Double-click on the native browser button "Forward" and wait for the transition to the fourth tab with the restored state await page.goForward(); await page.goForward(); await page.waitForSelector(slct(ControlQA.controlSelect, PARAMS.TAB_4_SELECT_VALUE_1)); expect(getUrlStateParam(page)).toEqual(fourthTabState); }, ); datalensTest( 'Checking whether the state parameter is saved when changing different types of selectors and when switching to another tab and back', async ({page}: {page: Page}) => { const dashboardPage = new DashboardPage({page}); // Check that the state parameter has been updated const firstTabState = await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_1_SELECT_VALUE_1, }); // Select another selector value on the first tab and // waiting for the deferred request to create a state and add it to the url to pass const firstTabStateAfterUpdate = await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_1_SELECT_VALUE_2, }); // Check that the state parameter has changed after a new selection in the selector based on the dataset expect(firstTabStateAfterUpdate).not.toEqual(firstTabState); // Click on the second tab and wait for the update in the url let urlParam = await dashboardPage.changeTabAndGetState({ tabName: PARAMS.TAB_2_NAME, timeout: TIMEOUT, }); // Check that the state parameter was reset when switching to the second tab expect(urlParam).toEqual(null); // Click on the first tab and wait for the page url to change await checkChartWithValuesAndGetResponse({ page, rightValues: [PARAMS.TAB_1_SELECT_VALUE_2], wrongValues: [PARAMS.TAB_1_SELECT_VALUE_1], action: async () => { const changedUrl = await dashboardPage.changeTabAndGetState({ tabName: PARAMS.TAB_1_NAME, timeout: TIMEOUT, }); // Check that the state parameter has been preserved after returning to the previous tab expect(changedUrl).toEqual(firstTabStateAfterUpdate); }, }); // Click on the fourth tab and wait for the page url to change urlParam = await dashboardPage.changeTabAndGetState({ tabName: PARAMS.TAB_4_NAME, timeout: TIMEOUT, }); // Check that the state parameter has been reset expect(urlParam).toEqual(null); // Select the value of the external selector on the fourth tab and // waiting for the deferred request to create a state and add it to the url to pass const fourthTabState = await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_4_SELECT_VALUE_1, }); // Check that the state parameter was updated when the external selector was changed expect(fourthTabState).not.toEqual(firstTabStateAfterUpdate); // Click on the second tab and wait for the update in the url urlParam = await dashboardPage.changeTabAndGetState({ tabName: PARAMS.TAB_2_NAME, timeout: TIMEOUT, }); // Check that the state parameter has been reset expect(urlParam).toEqual(null); // Select the value of the manual selector on the second tab and // waiting for the deferred request to create a state and add it to the url to pass const secondTabState = await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_2_SELECT_VALUE_1, }); // Check that the state parameter has been updated after selecting the value of the manual selector expect(secondTabState).not.toEqual(fourthTabState); // Click on the fourth tab and wait for the page url to change urlParam = await dashboardPage.changeTabAndGetState({ tabName: PARAMS.TAB_4_NAME, timeout: TIMEOUT, }); // Check that the state parameter has been restored expect(urlParam).toEqual(fourthTabState); // Click on the second tab and wait for the page url to change urlParam = await dashboardPage.changeTabAndGetState({ tabName: PARAMS.TAB_2_NAME, timeout: TIMEOUT, }); // Check that the state parameter has been restored expect(urlParam).toEqual(secondTabState); }, ); datalensTest( 'Checking backward transitions in the browser and consistency of the state parameter when changing different types of selectors and when switching to another tab', async ({page}: {page: Page}) => { const dashboardPage = new DashboardPage({page}); // Check that the state parameter has been updated const firstTabState = await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_1_SELECT_VALUE_1, }); // Select another selector value on the first tab and // waiting for the deferred request to create a state and add it to the url to pass const firstTabStateUpdated = await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_1_SELECT_VALUE_2, }); expect(firstTabStateUpdated).not.toEqual(firstTabState); await checkChartWithValuesAndGetResponse({ page, rightValues: [PARAMS.TAB_1_SELECT_VALUE_1], wrongValues: [PARAMS.TAB_1_SELECT_VALUE_2], action: async () => { // We return to the browser by clicking the back button const urlParam = await waitBackGetUrlStateParam(page); // Check that when switching back, the state parameter returned to the state of the first selected expect(urlParam).toEqual(firstTabState); }, }); // Click on the second tab and wait for the update in the url await dashboardPage.changeTabAndGetState({ tabName: PARAMS.TAB_2_NAME, timeout: TIMEOUT, }); // Select the value of the manual selector on the second tab and // waiting for the deferred request to create a state and add it to the url to pass const secondTabState = await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_2_SELECT_VALUE_1, }); // Check that the state parameter has been updated after selecting the value of the manual selector expect(secondTabState).not.toEqual(firstTabState); // Click on the fourth tab and wait for the page url to change let urlParam = await dashboardPage.changeTabAndGetState({ tabName: PARAMS.TAB_4_NAME, timeout: TIMEOUT, }); // Check that the state parameter has been reset expect(urlParam).toEqual(null); // Select the value of the external selector on the fourth tab and // waiting for the deferred request to create a state and add it to the url to pass const fourthTabState = await selectOptionAndGetState({ page, selector: ControlQA.controlSelect, value: PARAMS.TAB_4_SELECT_VALUE_1, }); // Check that the state parameter was updated when the external selector was changed expect(fourthTabState).not.toEqual(secondTabState); // We return to the browser by clicking the back button urlParam = await waitBackGetUrlStateParam(page); // Check that when switching back, the state parameter returned to the state of the not selected selector on the third tab expect(urlParam).toEqual(null); let selector = await page.$( `${slct(ControlQA.controlSelect)} >> ${SELECTORS.TAB_SELECTOR_TEXT}`, ); let selectorText = await selector?.innerText(); // EMPTY_SELECTOR_VAL_FULL_TEXT for common selectors // EMPTY_SELECTOR_VAL_TEXT for editor selectors expect(selectorText).toMatch( new RegExp( `${PARAMS.EMPTY_SELECTOR_VAL_FULL_TEXT}|${PARAMS.EMPTY_SELECTOR_VAL_TEXT}`, ), ); // We return to the browser by clicking the back button urlParam = await waitBackGetUrlStateParam(page); // Check that when switching back, the state parameter returned to the state of the selector on the second tab expect(urlParam).toEqual(secondTabState); selector = await page.$( `${slct(ControlQA.controlSelect)} >> ${SELECTORS.TAB_SELECTOR_TEXT}`, ); selectorText = await selector?.innerText(); // Check that when switching back, the selector value has returned to the state on the second tab expect(selectorText).toEqual(PARAMS.TAB_2_SELECT_VALUE_1); // We return to the browser by clicking the back button urlParam = await waitBackGetUrlStateParam(page); // Check that when switching back, the state parameter returned to the state of the not selected selector on the second tab expect(urlParam).toEqual(null); selector = await page.$( `${slct(ControlQA.controlSelect)} >> ${SELECTORS.TAB_SELECTOR_TEXT}`, ); selectorText = await selector?.innerText(); // Check that when switching back, the selector value has returned to the state on the second tab const defaultValue = await page.$(`${SELECTORS.EMPTY_SELECTOR_VAL}`); const defaultValueText = await defaultValue?.innerText(); expect(selectorText).toEqual(defaultValueText); await checkChartWithValuesAndGetResponse({ page, rightValues: [PARAMS.TAB_1_SELECT_VALUE_1], wrongValues: [PARAMS.TAB_1_SELECT_VALUE_2], action: async () => { // We return to the browser by clicking the back button const urlAfterBack = await waitBackGetUrlStateParam(page); // Check that when switching back, the state parameter returned to the state of the first selected expect(urlAfterBack).toEqual(firstTabState); }, }); const chartResponse = await checkChartWithValuesAndGetResponse({ page, rightValues: [PARAMS.TAB_1_SELECT_VALUE_2], action: async () => { // We return to the browser by clicking the back button const urlAfterBack = await waitBackGetUrlStateParam(page); // Check that when switching back, the selector value has returned to the unselected state on the first tab expect(urlAfterBack).toEqual(null); }, }); // сhart must have one empty param if (chartResponse) { const requestDataParams = chartResponse.request().postDataJSON().params; expect(Object.values(requestDataParams)[0]).toBeFalsy(); } }, ); });