feat: Add "Duplicate Tab" support for Items, Query, and Settings tabs (#2498)

* feat: Add "Duplicate Tab" support for Items, Query, and Settings tabs

* Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* added UT

* Fix QueryTab useTabs import path for duplicate tab test mock

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
BChoudhury-ms
2026-05-27 15:54:45 +05:30
committed by GitHub
parent 41ae13ea3a
commit 481e5cde3e
10 changed files with 419 additions and 2 deletions
+105
View File
@@ -0,0 +1,105 @@
import { expect, test } from "@playwright/test";
import { DataExplorer, TestAccount } from "../fx";
import { createTestSQLContainer, TestContainerContext } from "../testData";
let context: TestContainerContext = null!;
let explorer: DataExplorer = null!;
test.beforeAll("Create Test Database", async () => {
context = await createTestSQLContainer({ includeTestData: false });
});
test.afterAll("Delete Test Database", async () => {
await context?.dispose();
});
test.beforeEach("Open Data Explorer", async ({ page }) => {
explorer = await DataExplorer.open(page, TestAccount.SQL);
});
test("Duplicate Items tab opens a second Items tab", async () => {
// Open Items tab
const containerNode = await explorer.waitForContainerNode(context.database.id, context.container.id);
await containerNode.expand();
const itemsNode = await explorer.waitForContainerItemsNode(context.database.id, context.container.id);
await itemsNode.element.click();
const documentsTab = explorer.documentsTab("tab0");
await documentsTab.documentsFilter.waitFor({ timeout: 30_000 });
// Right-click the tab nav header
await explorer.tabNavHeader("tab0").click({ button: "right" });
// "Duplicate tab" should be visible in the context menu
const duplicateMenuItem = explorer.tabContextMenuItem("Duplicate tab");
await expect(duplicateMenuItem).toBeVisible();
await duplicateMenuItem.click();
// A second tab should appear
const tab1 = explorer.tab("tab1");
await expect(tab1).toBeAttached({ timeout: 30_000 });
// The duplicated tab should also show the Documents content
const duplicatedTab = explorer.documentsTab("tab1");
await duplicatedTab.documentsFilter.waitFor({ timeout: 30_000 });
});
test("Duplicate Query tab preserves query text in new tab", async () => {
// Open a new SQL query tab via container context menu
const containerNode = await explorer.waitForContainerNode(context.database.id, context.container.id);
await containerNode.openContextMenu();
await containerNode.contextMenuItem("New SQL Query").click();
const queryTab = explorer.queryTab("tab0");
const editor = queryTab.editor();
await editor.locator.waitFor({ timeout: 30_000 });
// Type a custom query
const customQuery = 'SELECT * FROM c WHERE c.id = "duplicate-query-test"';
await editor.setText(customQuery);
// Right-click the tab nav header
await explorer.tabNavHeader("tab0").click({ button: "right" });
const duplicateMenuItem = explorer.tabContextMenuItem("Duplicate tab");
await expect(duplicateMenuItem).toBeVisible();
await duplicateMenuItem.click();
// Second query tab should appear
const tab1 = explorer.tab("tab1");
await expect(tab1).toBeAttached({ timeout: 30_000 });
// The duplicated tab should contain the same query text
const duplicatedQueryTab = explorer.queryTab("tab1");
await duplicatedQueryTab.editor().locator.waitFor({ timeout: 30_000 });
const editorText = await duplicatedQueryTab.editor().text();
expect(editorText).toContain("duplicate-query-test");
});
test("Right-click context menu does not appear for the Home tab", async () => {
// The Home tab (ReactTabKind) is never duplicable — no context menu should appear
await explorer.tabNavHeader("Home").click({ button: "right" });
// Neither menu item should be visible
await expect(explorer.tabContextMenuItem("Duplicate tab")).not.toBeVisible();
await expect(explorer.tabContextMenuItem("Close tab")).not.toBeVisible();
});
test("Close tab from right-click menu closes the tab", async () => {
// Open Items tab
const containerNode = await explorer.waitForContainerNode(context.database.id, context.container.id);
await containerNode.expand();
const itemsNode = await explorer.waitForContainerItemsNode(context.database.id, context.container.id);
await itemsNode.element.click();
const documentsTab = explorer.documentsTab("tab0");
await documentsTab.documentsFilter.waitFor({ timeout: 30_000 });
// Right-click the tab nav header and close the tab
await explorer.tabNavHeader("tab0").click({ button: "right" });
await explorer.tabContextMenuItem("Close tab").click();
// The tab pane should be removed
await expect(explorer.tab("tab0")).not.toBeAttached({ timeout: 15_000 });
});