mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-22 03:04:07 +00:00
271 lines
10 KiB
TypeScript
271 lines
10 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
import { expect, Frame, Locator, Page, test } from "@playwright/test";
|
|
import { set } from "lodash";
|
|
import { ContainerCopy, getAccountName, TestAccount } from "../../fx";
|
|
|
|
const VISIBLE_TIMEOUT_MS = 30 * 1000;
|
|
|
|
test.describe("Container Copy - Permission Screen Verification", () => {
|
|
let page: Page;
|
|
let wrapper: Locator;
|
|
let panel: Locator;
|
|
let frame: Frame;
|
|
let targetAccountName: string;
|
|
let expectedSourceAccountName: string;
|
|
|
|
test.beforeEach("Setup for each test", async ({ browser }) => {
|
|
page = await browser.newPage();
|
|
({ wrapper, frame } = await ContainerCopy.open(page, TestAccount.SQLContainerCopyOnly));
|
|
targetAccountName = getAccountName(TestAccount.SQLContainerCopyOnly);
|
|
});
|
|
|
|
test.afterEach("Cleanup after each test", async () => {
|
|
await page.unroute(/.*/, (route) => route.continue());
|
|
await page.close();
|
|
});
|
|
|
|
test("Verify online container copy permissions panel functionality", async () => {
|
|
expect(wrapper).not.toBeNull();
|
|
|
|
// Verify all command bar buttons are visible
|
|
await wrapper.locator(".commandBarContainer").waitFor({ state: "visible", timeout: VISIBLE_TIMEOUT_MS });
|
|
|
|
const createCopyJobButton = wrapper.getByTestId("CommandBar/Button:Create Copy Job");
|
|
await expect(createCopyJobButton).toBeVisible();
|
|
await expect(wrapper.getByTestId("CommandBar/Button:Refresh")).toBeVisible();
|
|
await expect(wrapper.getByTestId("CommandBar/Button:Feedback")).toBeVisible();
|
|
|
|
// Open the Create Copy Job panel
|
|
await createCopyJobButton.click();
|
|
panel = frame.getByTestId("Panel:Create copy job");
|
|
await expect(panel).toBeVisible();
|
|
await expect(panel.getByRole("heading", { name: "Create copy job" })).toBeVisible();
|
|
|
|
// Select a different account for cross-account testing
|
|
const accountDropdown = panel.getByTestId("account-dropdown");
|
|
await accountDropdown.click();
|
|
|
|
const dropdownItemsWrapper = frame.locator("div.ms-Dropdown-items");
|
|
expect(await dropdownItemsWrapper.getAttribute("aria-label")).toEqual("Account");
|
|
|
|
const allDropdownItems = await dropdownItemsWrapper.locator(`button.ms-Dropdown-item[role='option']`).all();
|
|
|
|
const filteredItems = [];
|
|
for (const item of allDropdownItems) {
|
|
const testContent = (await item.textContent()) ?? "";
|
|
if (testContent.trim() !== targetAccountName.trim()) {
|
|
filteredItems.push(item);
|
|
}
|
|
}
|
|
|
|
if (filteredItems.length > 0) {
|
|
const firstDropdownItem = filteredItems[0];
|
|
expectedSourceAccountName = (await firstDropdownItem.textContent()) ?? "";
|
|
await firstDropdownItem.click();
|
|
} else {
|
|
throw new Error("No dropdown items available after filtering");
|
|
}
|
|
|
|
// Enable online migration mode
|
|
const migrationTypeContainer = panel.getByTestId("migration-type");
|
|
const onlineCopyRadioButton = migrationTypeContainer.getByRole("radio", { name: /Online mode/i });
|
|
await onlineCopyRadioButton.click({ force: true });
|
|
await expect(migrationTypeContainer.getByTestId("migration-type-description-online")).toBeVisible();
|
|
|
|
await panel.getByRole("button", { name: "Next" }).click();
|
|
|
|
// Verify Assign Permissions panel for online copy
|
|
const permissionScreen = panel.getByTestId("Panel:AssignPermissionsContainer");
|
|
await expect(permissionScreen).toBeVisible();
|
|
await expect(permissionScreen.getByText("Online container copy", { exact: true })).toBeVisible();
|
|
await expect(permissionScreen.getByText("Cross-account container copy", { exact: true })).toBeVisible();
|
|
|
|
// Setup API mocking for the source account
|
|
await page.route(`**/Microsoft.DocumentDB/databaseAccounts/${expectedSourceAccountName}**`, async (route) => {
|
|
const mockData = {
|
|
identity: {
|
|
type: "SystemAssigned",
|
|
principalId: "00-11-22-33",
|
|
},
|
|
properties: {
|
|
defaultIdentity: "SystemAssignedIdentity",
|
|
backupPolicy: {
|
|
type: "Continuous",
|
|
},
|
|
capabilities: [{ name: "EnableOnlineContainerCopy" }],
|
|
},
|
|
};
|
|
if (route.request().method() === "GET") {
|
|
const response = await route.fetch();
|
|
const actualData = await response.json();
|
|
const mergedData = { ...actualData };
|
|
|
|
set(mergedData, "identity", mockData.identity);
|
|
set(mergedData, "properties.defaultIdentity", mockData.properties.defaultIdentity);
|
|
set(mergedData, "properties.backupPolicy", mockData.properties.backupPolicy);
|
|
set(mergedData, "properties.capabilities", mockData.properties.capabilities);
|
|
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(mergedData),
|
|
});
|
|
} else {
|
|
await route.continue();
|
|
}
|
|
});
|
|
|
|
// Verify Point-in-Time Restore functionality
|
|
const expandedOnlineAccordionHeader = permissionScreen
|
|
.getByTestId("permission-group-container-onlineConfigs")
|
|
.locator("button[aria-expanded='true']");
|
|
await expect(expandedOnlineAccordionHeader).toBeVisible();
|
|
|
|
const accordionItem = expandedOnlineAccordionHeader
|
|
.locator("xpath=ancestor::*[contains(@class, 'fui-AccordionItem') or contains(@data-test, 'accordion-item')]")
|
|
.first();
|
|
|
|
const accordionPanel = accordionItem
|
|
.locator("[role='tabpanel'], .fui-AccordionPanel, [data-test*='panel']")
|
|
.first();
|
|
|
|
// Install clock mock and test PITR functionality
|
|
await page.clock.install({ time: new Date("2024-01-01T10:00:00Z") });
|
|
|
|
const pitrBtn = accordionPanel.getByTestId("pointInTimeRestore:PrimaryBtn");
|
|
await expect(pitrBtn).toBeVisible();
|
|
await pitrBtn.click();
|
|
|
|
// Verify new page opens with correct URL pattern
|
|
page.context().on("page", async (newPage) => {
|
|
const expectedUrlEndPattern = new RegExp(
|
|
`/providers/Microsoft.(DocumentDB|DocumentDb)/databaseAccounts/${expectedSourceAccountName}/backupRestore`,
|
|
);
|
|
expect(newPage.url()).toMatch(expectedUrlEndPattern);
|
|
await newPage.close();
|
|
});
|
|
|
|
const loadingOverlay = frame.locator("[data-test='loading-overlay']");
|
|
await expect(loadingOverlay).toBeVisible();
|
|
|
|
const refreshBtn = accordionPanel.getByTestId("pointInTimeRestore:RefreshBtn");
|
|
await expect(refreshBtn).not.toBeVisible();
|
|
|
|
// Fast forward time by 11 minutes
|
|
await page.clock.fastForward(11 * 60 * 1000);
|
|
|
|
await expect(refreshBtn).toBeVisible({ timeout: 5000 });
|
|
await expect(pitrBtn).not.toBeVisible();
|
|
|
|
// Setup additional API mocks for role assignments and permissions
|
|
await page.route(
|
|
`**/Microsoft.DocumentDB/databaseAccounts/${expectedSourceAccountName}/sqlRoleAssignments*`,
|
|
async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
value: [
|
|
{
|
|
principalId: "00-11-22-33",
|
|
roleDefinitionId: `Microsoft.DocumentDB/databaseAccounts/${expectedSourceAccountName}/77-88-99`,
|
|
},
|
|
],
|
|
}),
|
|
});
|
|
},
|
|
);
|
|
|
|
await page.route("**/Microsoft.DocumentDB/databaseAccounts/*/77-88-99**", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
value: [
|
|
{
|
|
name: "00000000-0000-0000-0000-000000000001",
|
|
},
|
|
],
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route(`**/Microsoft.DocumentDB/databaseAccounts/${targetAccountName}**`, async (route) => {
|
|
const mockData = {
|
|
identity: {
|
|
type: "SystemAssigned",
|
|
principalId: "00-11-22-33",
|
|
},
|
|
properties: {
|
|
defaultIdentity: "SystemAssignedIdentity",
|
|
backupPolicy: {
|
|
type: "Continuous",
|
|
},
|
|
capabilities: [{ name: "EnableOnlineContainerCopy" }],
|
|
},
|
|
};
|
|
|
|
if (route.request().method() === "PATCH") {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ status: "Succeeded" }),
|
|
});
|
|
} else if (route.request().method() === "GET") {
|
|
const response = await route.fetch();
|
|
const actualData = await response.json();
|
|
const mergedData = { ...actualData };
|
|
set(mergedData, "identity", mockData.identity);
|
|
set(mergedData, "properties.defaultIdentity", mockData.properties.defaultIdentity);
|
|
set(mergedData, "properties.backupPolicy", mockData.properties.backupPolicy);
|
|
set(mergedData, "properties.capabilities", mockData.properties.capabilities);
|
|
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(mergedData),
|
|
});
|
|
} else {
|
|
await route.continue();
|
|
}
|
|
});
|
|
|
|
// Verify cross-account permissions functionality
|
|
const expandedCrossAccordionHeader = permissionScreen
|
|
.getByTestId("permission-group-container-crossAccountConfigs")
|
|
.locator("button[aria-expanded='true']");
|
|
await expect(expandedCrossAccordionHeader).toBeVisible();
|
|
|
|
const crossAccordionItem = expandedCrossAccordionHeader
|
|
.locator("xpath=ancestor::*[contains(@class, 'fui-AccordionItem') or contains(@data-test, 'accordion-item')]")
|
|
.first();
|
|
|
|
const crossAccordionPanel = crossAccordionItem
|
|
.locator("[role='tabpanel'], .fui-AccordionPanel, [data-test*='panel']")
|
|
.first();
|
|
|
|
const toggleButton = crossAccordionPanel.getByTestId("btn-toggle");
|
|
await expect(toggleButton).toBeVisible();
|
|
await toggleButton.click();
|
|
|
|
// Verify popover functionality
|
|
const popover = frame.locator("[data-test='popover-container']");
|
|
await expect(popover).toBeVisible();
|
|
|
|
const yesButton = popover.getByRole("button", { name: /Yes/i });
|
|
const noButton = popover.getByRole("button", { name: /No/i });
|
|
await expect(yesButton).toBeVisible();
|
|
await expect(noButton).toBeVisible();
|
|
|
|
await yesButton.click();
|
|
|
|
// Verify loading states
|
|
await expect(loadingOverlay).toBeVisible();
|
|
await expect(loadingOverlay).toBeHidden({ timeout: 10 * 1000 });
|
|
await expect(popover).toBeHidden({ timeout: 10 * 1000 });
|
|
|
|
// Cancel the panel to clean up
|
|
await panel.getByRole("button", { name: "Cancel" }).click();
|
|
});
|
|
});
|