import {expect} from '@playwright/test'; import datalensTest from '../../utils/playwright/globalTestDefinition'; import {openTestPage, slct} from '../../utils'; import { DatasetFieldsTabQa, DatasetEditorTableSettingsItems, EditHistoryQA, DatasetFieldContextMenuItemsQA, FieldEditorQa, DatasetFieldTabBatchPanelQa, DialogConfirmQA, DATASET_TAB, DialogParameterQA, ActionPanelQA, DialogRelatedEntitiesQA, } from '../../../src/shared'; import DatasetPage from '../../page-objects/dataset/DatasetPage'; import FieldEditor from '../../page-objects/wizard/FieldEditor'; import {VALIDATE_DATASET_URL} from '../../page-objects/dataset/constants'; import {RobotChartsDatasetUrls} from '../../utils/constants'; import {getValidatePromise} from '../../page-objects/dataset/utils'; import {ActionPanelEntryContextMenuQa} from '../../../src/shared/constants/qa/action-panel'; datalensTest.describe('Dataset history', () => { const url = RobotChartsDatasetUrls.DatasetWithCsvConnection; let datasetPage: DatasetPage; datalensTest.beforeEach(async ({page}) => { datasetPage = new DatasetPage({page}); const validatePromise = page.waitForResponse( (response) => response.url().includes('/validateDataset') && response.ok(), ); await openTestPage(page, url); await page.waitForSelector(slct(DatasetFieldsTabQa.FieldNameColumnInput)); // Wait for initial dataset validation to complete, ensuring the edit history is initialized await validatePromise; }); datalensTest('Undo button is disabled when no changes were made', async ({page}) => { const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeVisible(); await expect(undoBtn).toBeDisabled(); }); datalensTest('Redo button is disabled when no changes were made', async ({page}) => { const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeVisible(); await expect(redoBtn).toBeDisabled(); }); datalensTest('Undo and redo reverts a field rename', async ({page}) => { const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeDisabled(); const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput(); const {newValue: changedValue, originalValue} = (await datasetPage.renameFirstField()) ?? {}; await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const undoValue = await fieldInput.inputValue(); expect(undoValue).toBe(originalValue); const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const redoValue = await fieldInput.inputValue(); expect(redoValue).toBe(changedValue); }); datalensTest('Redo is disabled after undo and making a new change', async ({page}) => { await datasetPage.renameFirstField(); // Undo const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await datasetPage.renameFirstField({value: 'new_value'}); await expect(redoBtn).toBeDisabled(); }); datalensTest('Undo reverts field visibility toggle', async ({page}) => { // Disable "Show hidden" so we cant observe the hidden field const settingsBtn = page.locator(slct(DatasetFieldsTabQa.TableSettingsBtn)); await settingsBtn.click(); const showHiddenMenuItem = await page.waitForSelector( slct(DatasetEditorTableSettingsItems.ShowHidden), ); await showHiddenMenuItem.click(); const rowsCountBefore = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); // Hide first field const hiddenBtn = page.locator(slct(DatasetFieldsTabQa.FieldVisibleColumnIcon)).first(); await hiddenBtn.click(); const rowsCountAfterHide = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterHide).toBe(rowsCountBefore - 1); // Undo the hide const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const rowsCountAfterUndo = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterUndo).toBe(rowsCountBefore); }); datalensTest('Redo restores field visibility toggle', async ({page}) => { // Get the visibility state of the first field before hiding const firstFieldVisibleIcon = page .locator(slct(DatasetFieldsTabQa.FieldVisibleColumnIcon)) .first(); const visibleClassBefore = await firstFieldVisibleIcon.getAttribute('title'); // Hide first field await firstFieldVisibleIcon.click(); const visibleClassAfterHide = await firstFieldVisibleIcon.getAttribute('title'); expect(visibleClassAfterHide).not.toBe(visibleClassBefore); // Undo the hide const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); // Redo the hide const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const visibleClassAfterRedo = await firstFieldVisibleIcon.getAttribute('title'); expect(visibleClassAfterRedo).toBe(visibleClassAfterHide); }); datalensTest('Undo and Redo restores field duplication', async ({page}) => { const rowsCountBefore = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput(); await fieldInput.hover(); // Duplicate field via context menu const contextMenuBtn = page.locator(slct(DatasetFieldsTabQa.FieldContextMenuBtn)).first(); await contextMenuBtn.click(); const duplicateItem = page.locator(slct(DatasetFieldContextMenuItemsQA.DUPLICATE)); const duplicatePromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL); await duplicateItem.click(); await duplicatePromise; const rowsCountAfterDuplicate = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterDuplicate).toBe(rowsCountBefore + 1); // Undo the duplication const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const rowsCountAfterUndo = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterUndo).toBe(rowsCountBefore); // Redo the duplication const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const rowsCountAfterRedo = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterRedo).toBe(rowsCountBefore + 1); }); datalensTest('Undo and Redo restores field removal', async ({page}) => { const rowsCountBefore = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput(); await fieldInput.hover(); // Remove field via context menu const contextMenuBtn = page.locator(slct(DatasetFieldsTabQa.FieldContextMenuBtn)).first(); await contextMenuBtn.click(); const removeItem = page.locator(slct(DatasetFieldContextMenuItemsQA.REMOVE)); const removePromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL); await removeItem.click(); await removePromise; const rowsCountAfterRemove = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterRemove).toBe(rowsCountBefore - 1); // Undo the removal const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const rowsCountAfterUndo = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterUndo).toBe(rowsCountBefore); // Redo the removal const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const rowsCountAfterRedo = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterRedo).toBe(rowsCountBefore - 1); }); datalensTest('Undo reverts field description change', async ({page}) => { const descriptionInput = page .locator(slct(DatasetFieldsTabQa.FieldDescriptionColumnInput)) .first() .locator('input'); const originalValue = await descriptionInput.inputValue(); await descriptionInput.fill('test description for history'); await page.keyboard.press('Enter'); const updatedValue = await descriptionInput.inputValue(); expect(updatedValue).toBe('test description for history'); // Undo the description change const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const restoredValue = await descriptionInput.inputValue(); expect(restoredValue).toBe(originalValue); }); datalensTest('Redo restores field description change', async ({page}) => { const descriptionInput = page .locator(slct(DatasetFieldsTabQa.FieldDescriptionColumnInput)) .first() .locator('input'); const originalValue = await descriptionInput.inputValue(); const newDescription = 'test description for redo'; await descriptionInput.fill(newDescription); await page.keyboard.press('Enter'); // Undo const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const afterUndo = await descriptionInput.inputValue(); expect(afterUndo).toBe(originalValue); // Redo const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const afterRedo = await descriptionInput.inputValue(); expect(afterRedo).toBe(newDescription); }); datalensTest('Undo reverts field type change', async ({page}) => { const typeSelect = page.locator(slct(DatasetFieldsTabQa.FieldTypeColumnBtn)); const { targetOption, selectBtn: typeSelectBtn, originalSelectText: originalTypeText, } = await datasetPage.datasetFieldsTable.changeFieldSelect(typeSelect); if (!targetOption || !typeSelectBtn) { return; } const validatePromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL); await targetOption.click(); await validatePromise; const changedTypeText = await typeSelectBtn.textContent(); expect(changedTypeText).not.toBe(originalTypeText); // Undo the type change const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const restoredTypeText = await typeSelectBtn.textContent(); expect(restoredTypeText).toBe(originalTypeText); }); datalensTest('Redo restores field type change', async ({page}) => { const typeSelect = page.locator(slct(DatasetFieldsTabQa.FieldTypeColumnBtn)); const { targetOption, targetOptionText, originalSelectText: originalTypeText, selectBtn: typeSelectBtn, } = await datasetPage.datasetFieldsTable.changeFieldSelect(typeSelect); if (!targetOption || !typeSelectBtn) { return; } const validatePromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL); await targetOption.click(); await validatePromise; // Undo const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const afterUndo = await typeSelectBtn.textContent(); expect(afterUndo).toBe(originalTypeText); // Redo const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const afterRedo = await typeSelectBtn.textContent(); expect(afterRedo).toBe(targetOptionText); }); datalensTest('Undo reverts field aggregation change', async ({page}) => { const aggregationSelectBtns = page.locator( slct(DatasetFieldsTabQa.FieldAggregationColumnBtn), ); const { targetOption, selectBtn: aggregationSelectBtn, originalSelectText: originalAggregationText, } = await datasetPage.datasetFieldsTable.changeFieldSelect(aggregationSelectBtns); if (!targetOption || !aggregationSelectBtn) { return; } const validatePromise = getValidatePromise(page); await targetOption.click(); await validatePromise; const changedAggregationText = await aggregationSelectBtn.textContent(); expect(changedAggregationText).not.toBe(originalAggregationText); // Undo const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const restoredAggregationText = await aggregationSelectBtn.textContent(); expect(restoredAggregationText).toBe(originalAggregationText); }); datalensTest('Redo restores field aggregation change', async ({page}) => { const aggregationSelectBtns = page.locator( slct(DatasetFieldsTabQa.FieldAggregationColumnBtn), ); const { targetOption, targetOptionText, originalSelectText: originalAggregationText, selectBtn: aggregationSelectBtn, } = await datasetPage.datasetFieldsTable.changeFieldSelect(aggregationSelectBtns); if (!targetOption || !aggregationSelectBtn) { return; } const validatePromise = getValidatePromise(page); await targetOption.click(); await validatePromise; // Undo const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const afterUndo = await aggregationSelectBtn.textContent(); expect(afterUndo).toBe(originalAggregationText); // Redo const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const afterRedo = await aggregationSelectBtn.textContent(); expect(afterRedo).toBe(targetOptionText); }); datalensTest('Undo and redo restore field edit via field editor dialog', async ({page}) => { const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput(); const originalName = await fieldInput.inputValue(); await fieldInput.hover(); // Open field editor via context menu const contextMenuBtn = page.locator(slct(DatasetFieldsTabQa.FieldContextMenuBtn)).first(); await contextMenuBtn.click(); const editItem = page.locator(slct(DatasetFieldContextMenuItemsQA.EDIT)); await editItem.click(); const fieldEditorDialog = page.locator(slct(FieldEditorQa.Dialog)); await expect(fieldEditorDialog).toBeVisible(); const fieldEditor = new FieldEditor(page); const changedName = `${originalName}_edited`; await fieldEditor.changeName(changedName); const applyPromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL); await fieldEditor.clickToApplyButton(); await applyPromise; // Undo const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const afterUndo = await fieldInput.inputValue(); expect(afterUndo).toBe(originalName); // Redo const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const afterRedo = await fieldInput.inputValue(); expect(afterRedo).toBe(changedName); }); datalensTest('Undo reverts batch deletion of selected fields', async ({page}) => { const rowsCountBefore = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); await datasetPage.datasetFieldsTable.selectTwoCheckboxes(); // Click batch delete button const actionsPanel = page.locator(slct(DatasetFieldsTabQa.BatchActionsPanel)); await expect(actionsPanel).toBeVisible(); const deleteBtn = actionsPanel.locator(slct(DatasetFieldTabBatchPanelQa.BatchDelete)); await deleteBtn.click(); const applyDelete = await page.waitForSelector(slct(DialogConfirmQA.ApplyButton)); const deletePromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL); await applyDelete.click(); await deletePromise; const rowsCountAfterDelete = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterDelete).toBe(rowsCountBefore - 2); const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const rowsCountAfterUndo = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterUndo).toBe(rowsCountBefore); const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const rowsCountAfterRedo = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterRedo).toBe(rowsCountBefore - 2); }); datalensTest('Undo reverts batch hide of selected fields', async ({page}) => { const settingsBtn = page.locator(slct(DatasetFieldsTabQa.TableSettingsBtn)); await settingsBtn.click(); const showHiddenMenuItem = await page.waitForSelector( slct(DatasetEditorTableSettingsItems.ShowHidden), ); await showHiddenMenuItem.click(); const rowsCountBefore = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); await datasetPage.datasetFieldsTable.selectTwoCheckboxes(); const actionsPanel = page.locator(slct(DatasetFieldsTabQa.BatchActionsPanel)); await expect(actionsPanel).toBeVisible(); const hideBtn = actionsPanel.locator('button').nth(1); await hideBtn.click(); const applyHide = await page.waitForSelector(slct(DialogConfirmQA.ApplyButton)); await applyHide.click(); // After hiding, the hidden fields should disappear (Show hidden is off by default) const rowsCountAfterHide = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterHide).toBe(rowsCountBefore - 2); // Undo the batch hide const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const rowsCountAfterUndo = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterUndo).toBe(rowsCountBefore); const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const rowsCountAfterRedo = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterRedo).toBe(rowsCountBefore - 2); }); datalensTest('Multiple redo steps restore multiple changes', async ({page}) => { const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput(); const step1Value = 'step1'; const step2Value = 'step2'; // Two changes const {originalValue} = (await datasetPage.renameFirstField({value: step1Value})) ?? {}; await datasetPage.renameFirstField({value: step2Value}); const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const afterFirstUndo = await fieldInput.inputValue(); expect(afterFirstUndo).toBe(step1Value); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const afterUndoAll = await fieldInput.inputValue(); expect(afterUndoAll).toBe(originalValue); // Redo first await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const afterFirstRedo = await fieldInput.inputValue(); expect(afterFirstRedo).toBe(step1Value); // Redo second await expect(redoBtn).toBeEnabled(); await redoBtn.click(); const afterSecondRedo = await fieldInput.inputValue(); expect(afterSecondRedo).toBe(step2Value); }); datalensTest('Undo button becomes disabled at the beginning of history', async ({page}) => { await datasetPage.renameFirstField(); const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); await expect(undoBtn).toBeDisabled(); }); datalensTest('Redo button becomes disabled at the end of history', async ({page}) => { await datasetPage.renameFirstField(); const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); await redoBtn.click(); await expect(redoBtn).toBeDisabled(); }); datalensTest('Keyboard shortcut Cmd/Ctrl+Z triggers undo', async ({page}) => { const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput(); const {originalValue} = (await datasetPage.renameFirstField()) ?? {}; const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); // Click outside the input to unfocus before using hotkey await page.locator(slct(DatasetFieldsTabQa.DatasetEditor)).click(); await page.keyboard.press(`ControlOrMeta+z`); const restoredValue = await fieldInput.inputValue(); expect(restoredValue).toBe(originalValue); }); datalensTest('Keyboard shortcut Cmd/Ctrl+Shift+Z triggers redo', async ({page}) => { const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput(); const {newValue: changedValue} = (await datasetPage.renameFirstField()) ?? {}; // Undo via button const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); // Click outside the input to unfocus before using hotkey await page.locator(slct(DatasetFieldsTabQa.DatasetEditor)).click(); await page.keyboard.press(`ControlOrMeta+Shift+z`); const restoredValue = await fieldInput.inputValue(); expect(restoredValue).toBe(changedValue); }); datalensTest('Ctrl+Z hotkey does not trigger undo when dialog is open', async ({page}) => { const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput(); const {newValue: changedValue} = (await datasetPage.renameFirstField()) ?? {}; const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(undoBtn).toBeEnabled(); await datasetPage.openTab(DATASET_TAB.PARAMETERS); await datasetPage.datasetTabSection.clickAddButton(); await datasetPage.dialogParameter.waitForDialogParameter(); await page.waitForSelector(slct(DialogParameterQA.Dialog)); const parametersDialog = page.locator(slct(DialogParameterQA.Dialog)); await expect(parametersDialog).toBeVisible(); await expect(undoBtn).toBeHidden(); await expect(redoBtn).toBeHidden(); // Try to undo via hotkey — should have no effect await page.keyboard.press('ControlOrMeta+z'); // Close the dialog const cancelBtn = page.locator(slct(DialogParameterQA.Cancel)); await cancelBtn.click(); await expect(parametersDialog).toBeHidden(); await datasetPage.openTab(DATASET_TAB.DATASET); // The field value should still be the changed value (undo was blocked) const currentValue = await fieldInput.inputValue(); expect(currentValue).toBe(changedValue); // Undo button should be visible and enabled again await expect(undoBtn).toBeVisible(); await expect(undoBtn).toBeEnabled(); }); datalensTest( 'Ctrl+Shift+Z hotkey does not trigger redo when dialog is open', async ({page}) => { const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput(); const {originalValue} = (await datasetPage.renameFirstField()) ?? {}; // Undo the change first const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const redoBtn = page.locator(slct(EditHistoryQA.RedoBtn)); await expect(redoBtn).toBeEnabled(); const entryContextBtn = page.locator(slct(ActionPanelQA.MoreBtn)); await entryContextBtn.click(); const relatedEntitiesBtn = await page.waitForSelector( slct(ActionPanelEntryContextMenuQa.RelatedEntities), ); await relatedEntitiesBtn.click(); await page.waitForSelector(slct(DialogRelatedEntitiesQA.Dialog)); const relatedEntitiesDialog = page.locator(slct(DialogRelatedEntitiesQA.Dialog)); await expect(relatedEntitiesDialog).toBeVisible(); await expect(undoBtn).toBeHidden(); await expect(redoBtn).toBeHidden(); // Try to redo via hotkey — should have no effect await page.keyboard.press('ControlOrMeta+Shift+z'); await page.keyboard.press('Escape'); await expect(relatedEntitiesDialog).toBeHidden(); // The field value should still be the original value (redo was blocked) const currentValue = await fieldInput.inputValue(); expect(currentValue).toBe(originalValue); // Redo button should be visible and enabled again await expect(redoBtn).toBeVisible(); await expect(redoBtn).toBeEnabled(); }, ); datalensTest('Undo reverts mixed action types in correct order', async ({page}) => { const rowsCountBefore = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); const fieldInput = datasetPage.datasetFieldsTable.getFieldNameInput(); // // Step 1: Rename a field const {newValue, originalValue} = (await datasetPage.renameFirstField()) ?? {}; // Step 2: Duplicate a field via context menu await fieldInput.hover(); const contextMenuBtn = page.locator(slct(DatasetFieldsTabQa.FieldContextMenuBtn)).first(); await contextMenuBtn.click(); const duplicateItem = page.locator(slct(DatasetFieldContextMenuItemsQA.DUPLICATE)); const dupPromise = datasetPage.waitForSuccessfulResponse(VALIDATE_DATASET_URL); await duplicateItem.click(); await dupPromise; const rowsCountAfterDup = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterDup).toBe(rowsCountBefore + 1); const undoBtn = page.locator(slct(EditHistoryQA.UndoBtn)); // Undo step 2 (duplicate) await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const rowsCountAfterUndoDup = await page .locator(slct(DatasetFieldsTabQa.FieldNameColumnInput)) .count(); expect(rowsCountAfterUndoDup).toBe(rowsCountBefore); const afterUndoFirst = await fieldInput.inputValue(); expect(afterUndoFirst).toBe(newValue); // Undo step 1 (rename) await expect(undoBtn).toBeEnabled(); await undoBtn.click(); const afterUndoAll = await fieldInput.inputValue(); expect(afterUndoAll).toBe(originalValue); }); });