mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-26 05:04:15 +00:00
Compare commits
2 Commits
copilot/su
...
copilot/su
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29e88cb754 | ||
|
|
9487879159 |
@@ -46,10 +46,6 @@ export type DataExploreMessageV3 =
|
|||||||
params: {
|
params: {
|
||||||
updateType: "created" | "deleted" | "settings";
|
updateType: "created" | "deleted" | "settings";
|
||||||
};
|
};
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: FabricMessageTypes.RestoreContainer;
|
|
||||||
params: [];
|
|
||||||
};
|
};
|
||||||
export interface GetCosmosTokenMessageOptions {
|
export interface GetCosmosTokenMessageOptions {
|
||||||
verb: "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace";
|
verb: "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { Dialog } from "../Controls/Dialog";
|
import { Dialog } from "../../Explorer/Controls/Dialog";
|
||||||
import { SidePanel } from "../Panes/PanelContainerComponent";
|
import { SidePanel } from "../../Explorer/Panes/PanelContainerComponent";
|
||||||
import CopyJobCommandBar from "./CommandBar/CopyJobCommandBar";
|
import CopyJobCommandBar from "./CommandBar/CopyJobCommandBar";
|
||||||
import "./containerCopyStyles.less";
|
import "./containerCopyStyles.less";
|
||||||
import { MonitorCopyJobsRefState } from "./MonitorCopyJobs/MonitorCopyJobRefState";
|
import { MonitorCopyJobsRefState } from "./MonitorCopyJobs/MonitorCopyJobRefState";
|
||||||
|
|||||||
@@ -516,7 +516,7 @@ describe("CopyJobActionMenu", () => {
|
|||||||
expect(screen.getByText("Cancel")).toBeInTheDocument();
|
expect(screen.getByText("Cancel")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should disable complete action when job is being updated", () => {
|
it("should handle complete action disabled state for online jobs", () => {
|
||||||
const job = createMockJob({
|
const job = createMockJob({
|
||||||
Status: CopyJobStatusType.InProgress,
|
Status: CopyJobStatusType.InProgress,
|
||||||
Mode: CopyJobMigrationType.Online,
|
Mode: CopyJobMigrationType.Online,
|
||||||
@@ -530,34 +530,8 @@ describe("CopyJobActionMenu", () => {
|
|||||||
const completeButton = screen.getByText("Complete");
|
const completeButton = screen.getByText("Complete");
|
||||||
fireEvent.click(completeButton);
|
fireEvent.click(completeButton);
|
||||||
|
|
||||||
// Simulate dialog confirmation to trigger state update
|
|
||||||
const [, , , onOkCallback] = mockShowOkCancelModalDialog.mock.calls[0];
|
|
||||||
onOkCallback();
|
|
||||||
|
|
||||||
fireEvent.click(actionButton);
|
fireEvent.click(actionButton);
|
||||||
const completeButtonAfterClick = screen.getByText("Complete").closest("button");
|
expect(screen.getByText("Complete")).toBeInTheDocument();
|
||||||
expect(completeButtonAfterClick).toBeInTheDocument();
|
|
||||||
expect(completeButtonAfterClick).toHaveAttribute("aria-disabled", "true");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should disable complete action when any other action is being performed", () => {
|
|
||||||
const job = createMockJob({
|
|
||||||
Status: CopyJobStatusType.InProgress,
|
|
||||||
Mode: CopyJobMigrationType.Online,
|
|
||||||
});
|
|
||||||
|
|
||||||
render(<TestComponentWrapper job={job} />);
|
|
||||||
|
|
||||||
const actionButton = screen.getByRole("button", { name: "Actions" });
|
|
||||||
fireEvent.click(actionButton);
|
|
||||||
|
|
||||||
const pauseButton = screen.getByText("Pause");
|
|
||||||
fireEvent.click(pauseButton);
|
|
||||||
fireEvent.click(actionButton);
|
|
||||||
|
|
||||||
const completeButtonAfterClick = screen.getByText("Complete").closest("button");
|
|
||||||
expect(completeButtonAfterClick).toBeInTheDocument();
|
|
||||||
expect(completeButtonAfterClick).toHaveAttribute("aria-disabled", "true");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ const CopyJobActionMenu: React.FC<CopyJobActionMenuProps> = ({ job, handleClick
|
|||||||
|
|
||||||
const getMenuItems = (): IContextualMenuProps["items"] => {
|
const getMenuItems = (): IContextualMenuProps["items"] => {
|
||||||
const isThisJobUpdating = updatingJobAction?.jobName === job.Name;
|
const isThisJobUpdating = updatingJobAction?.jobName === job.Name;
|
||||||
|
const updatingAction = updatingJobAction?.action;
|
||||||
|
|
||||||
const baseItems = [
|
const baseItems = [
|
||||||
{
|
{
|
||||||
@@ -104,7 +105,7 @@ const CopyJobActionMenu: React.FC<CopyJobActionMenuProps> = ({ job, handleClick
|
|||||||
text: ContainerCopyMessages.MonitorJobs.Actions.complete,
|
text: ContainerCopyMessages.MonitorJobs.Actions.complete,
|
||||||
iconProps: { iconName: "CheckMark" },
|
iconProps: { iconName: "CheckMark" },
|
||||||
onClick: () => showActionConfirmationDialog(job, CopyJobActions.complete),
|
onClick: () => showActionConfirmationDialog(job, CopyJobActions.complete),
|
||||||
disabled: isThisJobUpdating,
|
disabled: isThisJobUpdating && updatingAction === CopyJobActions.complete,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return filteredItems;
|
return filteredItems;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
AddGlobalSecondaryIndexPanelProps,
|
AddGlobalSecondaryIndexPanelProps,
|
||||||
} from "Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel";
|
} from "Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import { isFabric, isFabricNative, openRestoreContainerDialog } from "Platform/Fabric/FabricUtil";
|
import { isFabric, isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
@@ -35,7 +35,6 @@ import StoredProcedure from "./Tree/StoredProcedure";
|
|||||||
import Trigger from "./Tree/Trigger";
|
import Trigger from "./Tree/Trigger";
|
||||||
import UserDefinedFunction from "./Tree/UserDefinedFunction";
|
import UserDefinedFunction from "./Tree/UserDefinedFunction";
|
||||||
import { useSelectedNode } from "./useSelectedNode";
|
import { useSelectedNode } from "./useSelectedNode";
|
||||||
import { extractFeatures } from "../Platform/Hosted/extractFeatures";
|
|
||||||
|
|
||||||
export interface CollectionContextMenuButtonParams {
|
export interface CollectionContextMenuButtonParams {
|
||||||
databaseId: string;
|
databaseId: string;
|
||||||
@@ -61,17 +60,6 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isFabricNative() && !userContext.fabricContext?.isReadOnly) {
|
|
||||||
const features = extractFeatures();
|
|
||||||
if (features?.enableRestoreContainer) {
|
|
||||||
items.push({
|
|
||||||
iconSrc: AddCollectionIcon,
|
|
||||||
onClick: () => openRestoreContainerDialog(),
|
|
||||||
label: `Restore ${getCollectionName()}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isFabricNative() && (userContext.apiType !== "Tables" || userContext.features.enableSDKoperations)) {
|
if (!isFabricNative() && (userContext.apiType !== "Tables" || userContext.features.enableSDKoperations)) {
|
||||||
items.push({
|
items.push({
|
||||||
iconSrc: DeleteDatabaseIcon,
|
iconSrc: DeleteDatabaseIcon,
|
||||||
|
|||||||
@@ -54,6 +54,6 @@
|
|||||||
.mainButtonsContainer {
|
.mainButtonsContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0 16px;
|
gap: 0 16px;
|
||||||
margin: 40px auto
|
margin-bottom: 10px
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,23 +164,6 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
|
|||||||
const container = explorer;
|
const container = explorer;
|
||||||
const subscriptions: Array<{ dispose: () => void }> = [];
|
const subscriptions: Array<{ dispose: () => void }> = [];
|
||||||
|
|
||||||
let title: string;
|
|
||||||
let subtitle: string;
|
|
||||||
|
|
||||||
switch (userContext.apiType) {
|
|
||||||
case "Postgres":
|
|
||||||
title = "Welcome to Azure Cosmos DB for PostgreSQL";
|
|
||||||
subtitle = "Get started with our sample datasets, documentation, and additional tools.";
|
|
||||||
break;
|
|
||||||
case "VCoreMongo":
|
|
||||||
title = "Welcome to Azure DocumentDB (with MongoDB compatibility)";
|
|
||||||
subtitle = "Get started with our sample datasets, documentation, and additional tools.";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
title = "Welcome to Azure Cosmos DB";
|
|
||||||
subtitle = "Globally distributed, multi-model database service for any scale";
|
|
||||||
}
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
subscriptions.push(
|
subscriptions.push(
|
||||||
{
|
{
|
||||||
@@ -919,11 +902,10 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.splashScreenContainer}>
|
<div className={styles.splashScreenContainer}>
|
||||||
<div className={styles.splashScreen}>
|
<div className={styles.splashScreen}>
|
||||||
<h2 className={styles.title} role="heading" aria-label={title}>
|
<h2 className={styles.title} role="heading" aria-label="Welcome to Azure Cosmos DB">
|
||||||
{title}
|
Welcome to Azure Cosmos DB<span className="activePatch"></span>
|
||||||
<span className="activePatch"></span>
|
|
||||||
</h2>
|
</h2>
|
||||||
<div className={styles.subtitle}>{subtitle}</div>
|
<div className={styles.subtitle}>Globally distributed, multi-model database service for any scale</div>
|
||||||
{getSplashScreenButtons()}
|
{getSplashScreenButtons()}
|
||||||
{useCarousel.getState().showCoachMark && (
|
{useCarousel.getState().showCoachMark && (
|
||||||
<Coachmark
|
<Coachmark
|
||||||
|
|||||||
@@ -105,12 +105,6 @@ const requestAndStoreAccessToken = async (): Promise<void> => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const openRestoreContainerDialog = (): void => {
|
|
||||||
if (isFabricNative()) {
|
|
||||||
sendCachedDataMessage(FabricMessageTypes.RestoreContainer, []);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check token validity and schedule a refresh if necessary
|
* Check token validity and schedule a refresh if necessary
|
||||||
* @param tokenTimestamp
|
* @param tokenTimestamp
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ export type Features = {
|
|||||||
readonly disableConnectionStringLogin: boolean;
|
readonly disableConnectionStringLogin: boolean;
|
||||||
readonly enableContainerCopy: boolean;
|
readonly enableContainerCopy: boolean;
|
||||||
readonly enableCloudShell: boolean;
|
readonly enableCloudShell: boolean;
|
||||||
readonly enableRestoreContainer: boolean; // only for Fabric
|
|
||||||
|
|
||||||
// can be set via both flight and feature flag
|
// can be set via both flight and feature flag
|
||||||
autoscaleDefault: boolean;
|
autoscaleDefault: boolean;
|
||||||
@@ -112,7 +111,6 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
|||||||
enablePriorityBasedExecution: "true" === get("enableprioritybasedexecution"),
|
enablePriorityBasedExecution: "true" === get("enableprioritybasedexecution"),
|
||||||
disableConnectionStringLogin: "true" === get("disableconnectionstringlogin"),
|
disableConnectionStringLogin: "true" === get("disableconnectionstringlogin"),
|
||||||
enableContainerCopy: "true" === get("enablecontainercopy"),
|
enableContainerCopy: "true" === get("enablecontainercopy"),
|
||||||
enableRestoreContainer: "true" === get("enablerestorecontainer"),
|
|
||||||
enableCloudShell: true,
|
enableCloudShell: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { loadTheme } from "@fluentui/react";
|
|||||||
import "@testing-library/jest-dom";
|
import "@testing-library/jest-dom";
|
||||||
import { render, screen, waitFor } from "@testing-library/react";
|
import { render, screen, waitFor } from "@testing-library/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import * as StyleConstants from "../Common/StyleConstants";
|
import { updateStyles } from "../Common/StyleConstants";
|
||||||
import { Platform } from "../ConfigContext";
|
import { Platform } from "../ConfigContext";
|
||||||
import { useConfig } from "../hooks/useConfig";
|
import { useConfig } from "../hooks/useConfig";
|
||||||
import { useKnockoutExplorer } from "../hooks/useKnockoutExplorer";
|
import { useKnockoutExplorer } from "../hooks/useKnockoutExplorer";
|
||||||
@@ -150,13 +150,15 @@ describe("App", () => {
|
|||||||
let mockUpdateStyles: jest.Mock;
|
let mockUpdateStyles: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
|
||||||
mockStartScenario = jest.fn();
|
mockStartScenario = jest.fn();
|
||||||
mockCompletePhase = jest.fn();
|
mockCompletePhase = jest.fn();
|
||||||
|
|
||||||
mockUseKnockoutExplorer = jest.mocked(useKnockoutExplorer);
|
mockUseKnockoutExplorer = jest.mocked(useKnockoutExplorer);
|
||||||
mockUseConfig = jest.mocked(useConfig);
|
mockUseConfig = jest.mocked(useConfig);
|
||||||
mockLoadTheme = jest.mocked(loadTheme);
|
mockLoadTheme = jest.mocked(loadTheme);
|
||||||
mockUpdateStyles = jest.mocked(StyleConstants.updateStyles);
|
mockUpdateStyles = jest.mocked(updateStyles);
|
||||||
|
|
||||||
const mockUseMetricScenario = jest.mocked(useMetricScenario);
|
const mockUseMetricScenario = jest.mocked(useMetricScenario);
|
||||||
mockUseMetricScenario.mockReturnValue({
|
mockUseMetricScenario.mockReturnValue({
|
||||||
|
|||||||
@@ -88,6 +88,19 @@ describe("Root", () => {
|
|||||||
expect(mockUnsubscribe).toHaveBeenCalled();
|
expect(mockUnsubscribe).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should call getState to initialize theme", () => {
|
||||||
|
render(<Root />);
|
||||||
|
|
||||||
|
expect(mockThemeStore.getState).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle theme subscription properly", () => {
|
||||||
|
render(<Root />);
|
||||||
|
|
||||||
|
expect(mockThemeStore.subscribe).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockThemeStore.getState).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
test("should render without errors", () => {
|
test("should render without errors", () => {
|
||||||
expect(() => render(<Root />)).not.toThrow();
|
expect(() => render(<Root />)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ describe("AuthorizationUtils", () => {
|
|||||||
partitionKeyDefault: false,
|
partitionKeyDefault: false,
|
||||||
partitionKeyDefault2: false,
|
partitionKeyDefault2: false,
|
||||||
notebooksDownBanner: false,
|
notebooksDownBanner: false,
|
||||||
enableRestoreContainer: false,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ let CONTAINER_ID: string;
|
|||||||
|
|
||||||
// Set up test database and container with data before all tests
|
// Set up test database and container with data before all tests
|
||||||
test.beforeAll(async () => {
|
test.beforeAll(async () => {
|
||||||
testContainer = await createTestSQLContainer({ includeTestData: true });
|
testContainer = await createTestSQLContainer(true);
|
||||||
DATABASE_ID = testContainer.database.id;
|
DATABASE_ID = testContainer.database.id;
|
||||||
CONTAINER_ID = testContainer.container.id;
|
CONTAINER_ID = testContainer.container.id;
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user