import {Page} from '@playwright/test'; import { AddFieldQA, DialogConfirmQA, SectionDatasetQA, WizardVisualizationId, } from '../../../src/shared'; import {getParentByQARole, slct, waitForCondition} from '../../utils'; import {waitForSuccessfulResponse} from '../BasePage'; export enum PlaceholderName { X = 'placeholder-x', Y = 'placeholder-y', Y2 = 'placeholder-y2', FlatTableColumns = 'placeholder-flat-table-columns', Geopoint = 'placeholder-geopoint', Geopoligon = 'placeholder-geopolygon', Measures = 'placeholder-measures', Colors = 'placeholder-colors', Labels = 'placeholder-labels', Tooltips = 'placeholder-tooltips', Filters = 'placeholder-filters', DashboardFilters = 'placeholder-dashboard-filters', DashboardParameters = 'placeholder-dashboard-parameters', LayerFilters = 'placeholder-layer-filters', Size = 'placeholder-size', Shapes = 'placeholder-shapes', Points = 'placeholder-points', Dimensions = 'placeholder-dimensions', PivotTableColumns = 'placeholder-pivot-table-columns', Rows = 'placeholder-rows', Polyline = 'placeholder-polyline', Grouping = 'placeholder-grouping', Heatmap = 'placeholder-heatmap', Sort = 'placeholder-sort', Segments = 'placeholder-segments', } export enum GeopointType { Geopoints = 'geopoint', Geopoligons = 'geopolygon', GeopointsHeatMap = 'heatmap', Polyline = 'polyline', } export default class SectionVisualization { private page: Page; private layersSelectSelector = slct('geolayers-select'); private layerPopupItemSelector = '.geolayers-select__popup-item'; private geotypeControlSelector = slct('geotype-select'); constructor(page: Page) { this.page = page; } getPlaceholderItems(placeholder: PlaceholderName) { return this.page.$$(`${slct(placeholder)} .data-qa-placeholder-item`); } async removeFieldByClick(placeholder: PlaceholderName, fieldTitle: string) { await this.page.hover(`${slct(placeholder)} ${slct(fieldTitle)}`); return this.page.click(`${slct(placeholder)} ${slct(fieldTitle)} .cross-icon`); } async removeFieldByDragAndDrop(placeholder: PlaceholderName, fieldName: string) { const source = `${slct(placeholder)} ${slct('dnd-container')} >> text=${fieldName}`; const target = `${slct(SectionDatasetQA.DatasetContainer)} ${slct('dnd-container')}`; await this.page.dragAndDrop(source, target); } async dragAndDropFieldBetweenPlaceholders({ from, to, fieldName, }: { from: PlaceholderName; to: PlaceholderName; fieldName: string; }) { const dndContainerSelector = slct('dnd-container'); const sourceLocator = `${slct(from)} ${dndContainerSelector} >> text=${fieldName}`; const targetLocator = `${slct(to)} ${dndContainerSelector}`; await this.page.dragAndDrop(sourceLocator, targetLocator); } async addFieldByDragAndDrop(placeholder: PlaceholderName, fieldName: string) { await this.page.waitForSelector(slct(SectionDatasetQA.ItemTitle)); const source = slct(SectionDatasetQA.ItemTitle, fieldName); const target = `${slct(placeholder)} ${slct('dnd-container')}`; await this.page.dragAndDrop(source, target); } async replaceFieldByDragAndDrop( placeholder: PlaceholderName, oldField: string, newField: string, ) { const sourceSelector = slct(SectionDatasetQA.ItemTitle, newField); const targetSelector = `${slct(placeholder)} ${slct('dnd-container')} >> text=${oldField}`; await this.page.dragAndDrop(sourceSelector, targetSelector); } async addFieldByClick( placeholder: PlaceholderName, field: string, options?: {waitForApiRun?: boolean}, ) { const promise: Promise = options?.waitForApiRun ? waitForSuccessfulResponse('/api/run', this.page) : Promise.resolve(); await this.page.hover(slct(placeholder)); await this.page.click(`${slct(placeholder)} ${slct(AddFieldQA.AddFieldButton)}`, { force: true, }); const selectList = this.page.locator(slct('select-list')); await selectList.getByText(new RegExp(`^${field}$`, 'i')).click(); await promise; } async waitForPlaceholderFields(placeholder: PlaceholderName, fields: string[]) { let itemTexts: string[] = []; try { await waitForCondition(async () => { const items = await this.page.$$(`${slct(placeholder)} .data-qa-placeholder-item`); itemTexts = await Promise.all(items.map((item) => item.innerText())); return itemTexts.join().includes(fields.join()); }); } catch { throw new Error(`Expected: ${fields.join()}, got: ${itemTexts.join()}`); } } async addLayer() { await this.page.click(slct('add-layer')); } async toggleLayerList() { await this.page.click(this.layersSelectSelector); } async switchLayer(layerQa: string) { await this.toggleLayerList(); await this.page.click(slct(layerQa)); } async selectCombinedChartLayerVisualization( visualization: | WizardVisualizationId.Line | WizardVisualizationId.Column | WizardVisualizationId.Area, ) { await this.page.click(`.combined-chart-layer-type-switcher [value="${visualization}"]`); } async renameGeoLayer(oldGeoLayerQa: string, newName: string) { const oldLayerName = await this.page.waitForSelector(slct(oldGeoLayerQa)); const oldLayerItem = (await getParentByQARole(oldLayerName, 'geolayer-select-item'))!; await oldLayerItem.hover(); const oldLayerActions = (await oldLayerItem.$('[data-qa="geolayer-item-actions"]'))!; await oldLayerActions.click(); await this.page.click(slct('geolayer-select-option-menu-rename')); await this.page.fill(`${slct('layer-name-input')} input`, newName); await this.page.click(slct('apply-layer-settings-button')); } async removeGeoLayer(name: string) { const oldLayerItem = this.page.locator(slct('geolayer-select-item'), { has: this.page.locator(slct(name)), }); await oldLayerItem.hover(); const oldLayerActions = oldLayerItem.locator(slct('geolayer-item-actions')); await oldLayerActions.click(); await this.page.locator(slct('geolayer-select-option-menu-remove')).click(); await this.page.locator(slct(DialogConfirmQA.ApplyButton)).click(); } async setGeotype(type: GeopointType) { await this.page.click(this.geotypeControlSelector); await this.page.click(`[data-qa="${type}"]`); } async waitForLayers(layers: string[]) { const locatorAll = this.page .locator(this.layerPopupItemSelector) .locator('.geolayers-select__popup-item-label'); await this.expectLayersSelectItemsCount(layers.length); await Promise.all( layers.map((layer, i) => expect(locatorAll.nth(i)).toHaveAttribute('data-qa', layer)), ); } async getAddFieldItemsList(placeholderName: PlaceholderName) { let itemsList: string[] = []; await waitForCondition(async () => { const placeholderSelector = slct(placeholderName); await this.page.hover(placeholderSelector); await this.page.click(`${placeholderSelector} ${slct(AddFieldQA.AddFieldButton)}`); const itemNodes = await this.page.$$(`${slct('select-list')} .add-field__option-text`); itemsList = await Promise.all(itemNodes.map((node) => node.innerText())); return itemsList.length; }); return itemsList; } async getPlaceholderAddItemTooltipValue(placeholderName: PlaceholderName) { const placeholderSelector = slct(placeholderName); await this.page.hover(placeholderSelector); await this.page.hover(`${placeholderSelector} .add-field`); return await this.page.locator('.g-popover [data-qa]').getAttribute('data-qa'); } async getPlaceholderItemsInnerText(placeholderName: PlaceholderName) { const items = await this.getPlaceholderItems(placeholderName); const titles = await Promise.all(items.map((item) => item.$('.item-title span'))); const titleTexts = await Promise.all(titles.map((title) => title?.innerText())); return titleTexts.filter(Boolean) as string[]; } async clickOnSortIcon() { await this.page.click('.sort-icon'); } async setLayerOpacity(opacityValue: string) { this.page.fill('.visualization-layers-control__range input', opacityValue); } async expectLayersSelectItemsCount(count: number) { await expect(this.page.locator(this.layerPopupItemSelector)).toHaveCount(count); } async expectLayersSelectItemsTexts(texts: string[]) { await expect(this.page.locator(this.layerPopupItemSelector)).toHaveText(texts); } } export const VISUALIZATION_PLACEHOLDERS = { line: [ 'placeholder-x', 'placeholder-y', 'placeholder-y2', 'placeholder-colors', 'placeholder-shapes', 'placeholder-sort', 'placeholder-labels', 'placeholder-segments', 'placeholder-filters', ], area: [ 'placeholder-x', 'placeholder-y', 'placeholder-colors', 'placeholder-sort', 'placeholder-labels', 'placeholder-segments', 'placeholder-filters', ], area100p: [ 'placeholder-x', 'placeholder-y', 'placeholder-colors', 'placeholder-sort', 'placeholder-labels', 'placeholder-segments', 'placeholder-filters', ], column: [ 'placeholder-x', 'placeholder-y', 'placeholder-colors', 'placeholder-sort', 'placeholder-labels', 'placeholder-segments', 'placeholder-filters', ], column100p: [ 'placeholder-x', 'placeholder-y', 'placeholder-colors', 'placeholder-sort', 'placeholder-labels', 'placeholder-segments', 'placeholder-filters', ], bar: [ 'placeholder-y', 'placeholder-x', 'placeholder-colors', 'placeholder-sort', 'placeholder-labels', 'placeholder-filters', ], bar100p: [ 'placeholder-y', 'placeholder-x', 'placeholder-colors', 'placeholder-sort', 'placeholder-labels', 'placeholder-filters', ], pie: [ 'placeholder-dimensions', 'placeholder-colors', 'placeholder-measures', 'placeholder-sort', 'placeholder-labels', 'placeholder-filters', ], donut: [ 'placeholder-dimensions', 'placeholder-colors', 'placeholder-measures', 'placeholder-sort', 'placeholder-labels', 'placeholder-filters', ], metric: ['placeholder-measures', 'placeholder-metric-colors', 'placeholder-filters'], scatter: [ 'placeholder-x', 'placeholder-y', 'placeholder-points', 'placeholder-size', 'placeholder-colors', 'placeholder-shapes', 'placeholder-sort', 'placeholder-filters', ], treemap: [ 'placeholder-dimensions', 'placeholder-measures', 'placeholder-colors', 'placeholder-filters', ], flatTable: [ 'placeholder-flat-table-columns', 'placeholder-colors', 'placeholder-sort', 'placeholder-filters', ], pivotTable: [ 'placeholder-pivot-table-columns', 'placeholder-rows', 'placeholder-measures', 'placeholder-colors', 'placeholder-sort', 'placeholder-filters', ], geopoint: [ 'placeholder-geopoint', 'placeholder-size', 'placeholder-colors', 'placeholder-labels', 'placeholder-tooltips', 'placeholder-layer-filters', 'placeholder-filters', ], geopolygon: [ 'placeholder-geopolygon', 'placeholder-colors', 'placeholder-tooltips', 'placeholder-layer-filters', 'placeholder-filters', ], heatmap: [ 'placeholder-heatmap', 'placeholder-colors', 'placeholder-layer-filters', 'placeholder-filters', ], polyline: [ 'placeholder-polyline', 'placeholder-measures', 'placeholder-grouping', 'placeholder-colors', 'placeholder-sort', 'placeholder-layer-filters', 'placeholder-filters', ], };