Compare commits

..

29 Commits

Author SHA1 Message Date
Bikram Choudhury
ad863bdd58 added try-catch to the error code 2026-01-05 13:12:12 +05:30
Bikram Choudhury
0683bfc3af removed the sub partition key 2026-01-04 01:26:36 +05:30
Bikram Choudhury
1039d835f4 ignore nosql test files and list out the number of files runs on each shard 2026-01-03 00:39:08 +05:30
Bikram Choudhury
227c93a30b remove forward slash 2026-01-02 16:29:40 +05:30
Asier Isayas
1f1ccc7e0e only open scale and settings at the beginning of each test suite 2026-01-01 12:35:02 -05:00
Asier Isayas
7aa62437f0 Don't refresh databases when opening Scale+Settings and only delete database if running locally 2025-12-31 15:48:22 -05:00
Asier Isayas
7ddedf3314 dont delete database after every test. leave it to the CI 2025-12-31 13:20:20 -05:00
Asier Isayas
644226d800 create and delete a container for each individual test 2025-12-31 12:23:38 -05:00
Asier Isayas
e2e9cece5f close notification console window after seeing desired log 2025-12-31 11:04:54 -05:00
Asier Isayas
d235be6a3b when refreshing containers, open console window to check for status completion 2025-12-31 10:10:33 -05:00
Asier Isayas
87d5f84f2e refresh databases in test instead of product code 2025-12-30 16:59:17 -05:00
Asier Isayas
2758e6ac72 increase time for change partition key request 2025-12-30 15:19:45 -05:00
Asier Isayas
1232596c5a increase time for change partition key request 2025-12-30 14:51:05 -05:00
Asier Isayas
eff736596f reload all databases before loading offers 2025-12-30 14:09:53 -05:00
Asier Isayas
44de3ade5c When opening scale and settings, refresh databases 2025-12-30 12:20:20 -05:00
Asier Isayas
459b2c7050 refresh tree before opening scale and settings 2025-12-30 11:22:23 -05:00
Asier Isayas
1736e24429 when disposing of database during playwright test, refresh tree to remove deleted database 2025-12-29 17:13:22 -05:00
Asier Isayas
ff5ebda58e record network calls on all retries 2025-12-29 15:54:33 -05:00
Asier Isayas
79e9f3a843 record network traces 2025-12-29 15:53:18 -05:00
Asier Isayas
9bdb995e14 refactor scale setup and tear down to be within each test 2025-12-29 14:01:44 -05:00
Asier Isayas
de293d330c Merge branch 'master' of https://github.com/Azure/cosmos-explorer into users/jawelton/reenable-keys-mongocassandratests-122325 2025-12-29 11:07:32 -05:00
Asier Isayas
e9f12298cd run scale tests serially 2025-12-29 10:43:46 -05:00
Asier Isayas
d6a84af0a2 for scale and settings, dont create sample data in container 2025-12-26 21:09:36 -05:00
Asier Isayas
5a24db2230 create and delete container for every individual scale test 2025-12-26 18:10:15 -05:00
Asier Isayas
45049425c9 get new table button 2025-12-26 17:32:09 -05:00
Asier Isayas
74363ddfe9 click global new... button then collection in playwright tests 2025-12-26 14:24:38 -05:00
Jade Welton
ef1e26fc0c Another small bump to test shard count. 2025-12-26 07:15:48 -08:00
Jade Welton
e75af41844 Increase number of shards for playwright tests. 2025-12-26 06:47:37 -08:00
Jade Welton
7ac6a264bb Temporarily re-enable key based auth for Mongo and Cassandra tests. 2025-12-23 13:54:37 -08:00
50 changed files with 375 additions and 475 deletions

View File

@@ -164,8 +164,8 @@ jobs:
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
shardTotal: [16]
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
shardTotal: [20]
steps:
- uses: actions/checkout@v4
- name: Use Node.js 18.x
@@ -198,18 +198,20 @@ jobs:
GREMLIN_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-gremlin.documents.azure.com/.default" -o tsv --query accessToken)
echo "::add-mask::$GREMLIN_TESTACCOUNT_TOKEN"
echo GREMLIN_TESTACCOUNT_TOKEN=$GREMLIN_TESTACCOUNT_TOKEN >> $GITHUB_ENV
CASSANDRA_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-cassandra.documents.azure.com/.default" -o tsv --query accessToken)
echo "::add-mask::$CASSANDRA_TESTACCOUNT_TOKEN"
echo CASSANDRA_TESTACCOUNT_TOKEN=$CASSANDRA_TESTACCOUNT_TOKEN >> $GITHUB_ENV
MONGO_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-mongo.documents.azure.com/.default" -o tsv --query accessToken)
echo "::add-mask::$MONGO_TESTACCOUNT_TOKEN"
echo MONGO_TESTACCOUNT_TOKEN=$MONGO_TESTACCOUNT_TOKEN >> $GITHUB_ENV
MONGO32_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-mongo32.documents.azure.com/.default" -o tsv --query accessToken)
echo "::add-mask::$MONGO32_TESTACCOUNT_TOKEN"
echo MONGO32_TESTACCOUNT_TOKEN=$MONGO32_TESTACCOUNT_TOKEN >> $GITHUB_ENV
MONGO_READONLY_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-mongo-readonly.documents.azure.com/.default" -o tsv --query accessToken)
echo "::add-mask::$MONGO_READONLY_TESTACCOUNT_TOKEN"
echo MONGO_READONLY_TESTACCOUNT_TOKEN=$MONGO_READONLY_TESTACCOUNT_TOKEN >> $GITHUB_ENV
# CASSANDRA_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-cassandra.documents.azure.com/.default" -o tsv --query accessToken)
# echo "::add-mask::$CASSANDRA_TESTACCOUNT_TOKEN"
# echo CASSANDRA_TESTACCOUNT_TOKEN=$CASSANDRA_TESTACCOUNT_TOKEN >> $GITHUB_ENV
# MONGO_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-mongo.documents.azure.com/.default" -o tsv --query accessToken)
# echo "::add-mask::$MONGO_TESTACCOUNT_TOKEN"
# echo MONGO_TESTACCOUNT_TOKEN=$MONGO_TESTACCOUNT_TOKEN >> $GITHUB_ENV
# MONGO32_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-mongo32.documents.azure.com/.default" -o tsv --query accessToken)
# echo "::add-mask::$MONGO32_TESTACCOUNT_TOKEN"
# echo MONGO32_TESTACCOUNT_TOKEN=$MONGO32_TESTACCOUNT_TOKEN >> $GITHUB_ENV
# MONGO_READONLY_TESTACCOUNT_TOKEN=$(az account get-access-token --scope "https://github-e2etests-mongo-readonly.documents.azure.com/.default" -o tsv --query accessToken)
# echo "::add-mask::$MONGO_READONLY_TESTACCOUNT_TOKEN"
# echo MONGO_READONLY_TESTACCOUNT_TOKEN=$MONGO_READONLY_TESTACCOUNT_TOKEN >> $GITHUB_ENV
- name: List tests in this shard
run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --list
- name: Run test shard ${{ matrix['shardIndex'] }} of ${{ matrix['shardTotal']}}
run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --workers=3
- name: Upload blob report to GitHub Actions Artifacts

View File

@@ -406,11 +406,7 @@ body {
width: 440px;
min-height: 565px;
}
.dataExplorerLoaderforcopyJobs{
width: 100%;
min-height: 565px;
right: 0;
}
.dataExplorerTabLoaderContainer {
left: initial;
top: initial;

2
package-lock.json generated
View File

@@ -116,8 +116,8 @@
"tinykeys": "2.1.0",
"underscore": "1.12.1",
"utility-types": "3.10.0",
"web-vitals": "4.2.4",
"uuid": "9.0.0",
"web-vitals": "4.2.4",
"zustand": "3.5.0"
},
"devDependencies": {

View File

@@ -4,6 +4,12 @@ import { defineConfig, devices } from "@playwright/test";
*/
export default defineConfig({
testDir: "test",
testIgnore: [
"**/mongo/**",
"**/cassandra/**",
"**/gremlin/**",
"**/tables/**",
],
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 3 : 0,
@@ -11,8 +17,8 @@ export default defineConfig({
reporter: process.env.CI ? "blob" : "html",
timeout: 10 * 60 * 1000,
use: {
trace: "off",
video: "off",
trace: "on-all-retries",
video: "on-first-retry",
screenshot: "on",
testIdAttribute: "data-test",
contextOptions: {

View File

@@ -1,5 +1,4 @@
import { Overlay, Spinner, SpinnerSize } from "@fluentui/react";
import { useThemeStore } from "hooks/useTheme";
import React from "react";
interface LoadingOverlayProps {
@@ -8,7 +7,6 @@ interface LoadingOverlayProps {
}
const LoadingOverlay: React.FC<LoadingOverlayProps> = ({ isLoading, label }) => {
const isDarkMode = useThemeStore((state) => state.isDarkMode);
if (!isLoading) {
return null;
}
@@ -17,7 +15,7 @@ const LoadingOverlay: React.FC<LoadingOverlayProps> = ({ isLoading, label }) =>
<Overlay
styles={{
root: {
backgroundColor: isDarkMode ? "rgba(32, 31, 30, 0.9)" : "rgba(255,255,255,0.9)",
backgroundColor: "rgba(255,255,255,0.9)",
zIndex: 9999,
display: "flex",
alignItems: "center",
@@ -25,11 +23,7 @@ const LoadingOverlay: React.FC<LoadingOverlayProps> = ({ isLoading, label }) =>
},
}}
>
<Spinner
size={SpinnerSize.large}
label={label}
styles={{ label: { fontWeight: 600, color: isDarkMode ? "#ffffff" : "#323130" } }}
/>
<Spinner size={SpinnerSize.large} label={label} styles={{ label: { fontWeight: 600 } }} />
</Overlay>
);
};

View File

@@ -11,14 +11,3 @@
gap: 8px;
align-items: center;
}
/* Override dark mode inherit for pagination icons */
body.isDarkMode .pager-container .ms-Button .ms-Button-icon,
body.isDarkMode .pager-container .ms-Button i {
color: var(--colorBrandForeground1);
}
body.isDarkMode .pager-container .ms-Button:disabled .ms-Button-icon,
body.isDarkMode .pager-container .ms-Button:disabled i {
color: var(--colorNeutralForegroundDisabled);
}

View File

@@ -31,7 +31,6 @@ const iconButtonStyles = {
outline: "none",
},
};
const textStyle: React.CSSProperties = { color: "var(--colorNeutralForeground1)" };
const Pager: React.FC<PagerProps> = ({
startIndex,
@@ -60,7 +59,7 @@ const Pager: React.FC<PagerProps> = ({
return (
<div className={className || "pager-container"}>
{showItemCount && (
<Text style={textStyle}>
<Text>
Showing {startIndex + 1} - {endIndex} of {totalCount} items
</Text>
)}
@@ -83,7 +82,7 @@ const Pager: React.FC<PagerProps> = ({
disabled={disabled || currentPage === 1}
styles={iconButtonStyles}
/>
<Text style={textStyle}>
<Text>
Page {currentPage} of {totalPages}
</Text>
<IconButton

View File

@@ -35,24 +35,30 @@ export const getDataTransferJobs = async (
accountName: string,
): Promise<DataTransferJobGetResults[]> => {
let dataTransferJobs: DataTransferJobGetResults[] = [];
let dataTransferFeeds: DataTransferJobFeedResults = await listByDatabaseAccount(
subscriptionId,
resourceGroup,
accountName,
);
dataTransferJobs = [...dataTransferJobs, ...(dataTransferFeeds?.value || [])];
while (dataTransferFeeds?.nextLink) {
const nextResponse = await window.fetch(dataTransferFeeds.nextLink, {
headers: {
Authorization: userContext.authorizationToken,
},
});
if (nextResponse.ok) {
dataTransferFeeds = await nextResponse.json();
dataTransferJobs = [...dataTransferJobs, ...(dataTransferFeeds?.value || [])];
} else {
break;
try {
let dataTransferFeeds: DataTransferJobFeedResults = await listByDatabaseAccount(
subscriptionId,
resourceGroup,
accountName,
);
dataTransferJobs = [...dataTransferJobs, ...(dataTransferFeeds?.value || [])];
while (dataTransferFeeds?.nextLink) {
const nextResponse = await window.fetch(dataTransferFeeds.nextLink, {
headers: {
Authorization: userContext.authorizationToken,
},
});
if (nextResponse.ok) {
dataTransferFeeds = await nextResponse.json();
dataTransferJobs = [...dataTransferJobs, ...(dataTransferFeeds?.value || [])];
} else {
break;
}
}
} catch (error) {
// Handle network errors gracefully (e.g., in test environment)
console.warn("Failed to fetch next page of data transfer jobs:", error);
throw new Error(error.message || JSON.stringify(error));
}
return dataTransferJobs;
};

View File

@@ -1,28 +1,24 @@
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
import React from "react";
import { useThemeStore } from "../../../hooks/useTheme";
import { StyleConstants } from "../../../Common/StyleConstants";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import * as CommandBarUtil from "../../Menus/CommandBar/CommandBarUtil";
import { getThemeTokens } from "../../Theme/ThemeUtil";
import { ContainerCopyProps } from "../Types/CopyJobTypes";
import { getCommandBarButtons } from "./Utils";
const backgroundColor = StyleConstants.BaseLight;
const rootStyle = {
root: {
backgroundColor: backgroundColor,
},
};
const CopyJobCommandBar: React.FC<ContainerCopyProps> = ({ explorer }) => {
const isDarkMode = useThemeStore((state) => state.isDarkMode);
const themeTokens = getThemeTokens(isDarkMode);
const backgroundColor = themeTokens.colorNeutralBackground1;
const rootStyle = {
root: {
backgroundColor: backgroundColor,
},
};
const commandBarItems: CommandButtonComponentProps[] = getCommandBarButtons(explorer);
const controlButtons: ICommandBarItemProps[] = CommandBarUtil.convertButton(commandBarItems, backgroundColor);
return (
<div className="commandBarContainer" style={{ backgroundColor }}>
<div className="commandBarContainer">
<FluentCommandBar
ariaLabel="Use left and right arrow keys to navigate between commands"
styles={rootStyle}

View File

@@ -82,9 +82,9 @@ describe("CommandBar Utils", () => {
it("should include feedback button when platform is Portal", () => {
const buttons = getCommandBarButtons(mockExplorer);
expect(buttons.length).toBe(4);
expect(buttons.length).toBe(3);
const feedbackButton = buttons[3];
const feedbackButton = buttons[2];
expect(feedbackButton).toBeDefined();
expect(feedbackButton.ariaLabel).toBe("Provide feedback on copy jobs");
expect(feedbackButton.tooltipText).toBe("Feedback");
@@ -107,7 +107,7 @@ describe("CommandBar Utils", () => {
const { getCommandBarButtons: getCommandBarButtonsEmulator } = await import("./Utils");
const buttons = getCommandBarButtonsEmulator(mockExplorer);
expect(buttons.length).toBe(3);
expect(buttons.length).toBe(2);
});
it("should call openCreateCopyJobPanel when create button is clicked", () => {
@@ -131,7 +131,7 @@ describe("CommandBar Utils", () => {
it("should call openContainerCopyFeedbackBlade when feedback button is clicked", () => {
const buttons = getCommandBarButtons(mockExplorer);
const feedbackButton = buttons[3];
const feedbackButton = buttons[2];
feedbackButton.onCommandClick({} as React.SyntheticEvent);
@@ -148,10 +148,7 @@ describe("CommandBar Utils", () => {
expect(buttons[1].iconAlt).toBe("Refresh");
expect(buttons[2].iconSrc).toBeDefined();
expect(buttons[2].iconAlt).toBe("Dark Theme");
expect(buttons[3].iconSrc).toBeDefined();
expect(buttons[3].iconAlt).toBe("Feedback");
expect(buttons[2].iconAlt).toBe("Feedback");
});
it("should handle null MonitorCopyJobsRefState ref gracefully", () => {
@@ -205,13 +202,12 @@ describe("CommandBar Utils", () => {
});
});
it("should maintain button order: create, refresh, themeToggle, feedback", () => {
it("should maintain button order: create, refresh, feedback", () => {
const buttons = getCommandBarButtons(mockExplorer);
expect(buttons[0].tooltipText).toBe("Create Copy Job");
expect(buttons[1].tooltipText).toBe("Refresh");
expect(buttons[2].tooltipText).toBe("Dark Theme");
expect(buttons[3].tooltipText).toBe("Feedback");
expect(buttons[2].tooltipText).toBe("Feedback");
});
});
@@ -233,7 +229,7 @@ describe("CommandBar Utils", () => {
buttons[1].onCommandClick({} as React.SyntheticEvent);
expect(mockRefreshJobList).toHaveBeenCalled();
buttons[3].onCommandClick({} as React.SyntheticEvent);
buttons[2].onCommandClick({} as React.SyntheticEvent);
expect(mockOpenContainerCopyFeedbackBlade).toHaveBeenCalled();
});
});

View File

@@ -1,10 +1,7 @@
import AddIcon from "../../../../images/Add.svg";
import FeedbackIcon from "../../../../images/Feedback-Command.svg";
import MoonIcon from "../../../../images/MoonIcon.svg";
import RefreshIcon from "../../../../images/refresh-cosmos.svg";
import SunIcon from "../../../../images/SunIcon.svg";
import { configContext, Platform } from "../../../ConfigContext";
import { useThemeStore } from "../../../hooks/useTheme";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import Explorer from "../../Explorer";
import * as Actions from "../Actions/CopyJobActions";
@@ -14,7 +11,6 @@ import { CopyJobCommandBarBtnType } from "../Types/CopyJobTypes";
function getCopyJobBtns(explorer: Explorer): CopyJobCommandBarBtnType[] {
const monitorCopyJobsRef = MonitorCopyJobsRefState((state) => state.ref);
const isDarkMode = useThemeStore.getState().isDarkMode;
const buttons: CopyJobCommandBarBtnType[] = [
{
key: "createCopyJob",
@@ -30,15 +26,7 @@ function getCopyJobBtns(explorer: Explorer): CopyJobCommandBarBtnType[] {
ariaLabel: ContainerCopyMessages.refreshButtonAriaLabel,
onClick: () => monitorCopyJobsRef?.refreshJobList(),
},
{
key: "themeToggle",
iconSrc: isDarkMode ? SunIcon : MoonIcon,
label: isDarkMode ? "Light Theme" : "Dark Theme",
ariaLabel: isDarkMode ? "Switch to Light Theme" : "Switch to Dark Theme",
onClick: () => useThemeStore.getState().toggleTheme(),
},
];
if (configContext.platform === Platform.Portal) {
buttons.push({
key: "feedback",

View File

@@ -12,12 +12,7 @@ import useToggle from "./hooks/useToggle";
const managedIdentityTooltip = (
<Text>
{ContainerCopyMessages.addManagedIdentity.tooltip.content} &nbsp;
<Link
style={{ color: "var(--colorBrandForeground1)" }}
href={ContainerCopyMessages.addManagedIdentity.tooltip.href}
target="_blank"
rel="noopener noreferrer"
>
<Link href={ContainerCopyMessages.addManagedIdentity.tooltip.href} target="_blank" rel="noopener noreferrer">
{ContainerCopyMessages.addManagedIdentity.tooltip.hrefText}
</Link>
</Text>
@@ -31,7 +26,7 @@ const AddManagedIdentity: React.FC<AddManagedIdentityProps> = () => {
return (
<Stack className="addManagedIdentityContainer" tokens={{ childrenGap: 15, padding: "0 0 0 20px" }}>
<Text className="themeText">
<Text>
{ContainerCopyMessages.addManagedIdentity.description}&ensp;
<Link href={ContainerCopyMessages.addManagedIdentity.descriptionHref} target="_blank" rel="noopener noreferrer">
{ContainerCopyMessages.addManagedIdentity.descriptionHrefText}

View File

@@ -13,12 +13,7 @@ import useToggle from "./hooks/useToggle";
const TooltipContent = (
<Text>
{ContainerCopyMessages.readPermissionAssigned.tooltip.content} &nbsp;
<Link
style={{ color: "var(--colorBrandForeground1)" }}
href={ContainerCopyMessages.readPermissionAssigned.tooltip.href}
target="_blank"
rel="noopener noreferrer"
>
<Link href={ContainerCopyMessages.readPermissionAssigned.tooltip.href} target="_blank" rel="noopener noreferrer">
{ContainerCopyMessages.readPermissionAssigned.tooltip.hrefText}
</Link>
</Text>

View File

@@ -47,8 +47,8 @@ const PermissionGroup: React.FC<PermissionGroupConfig> = ({ title, description,
tokens={{ childrenGap: 15 }}
styles={{
root: {
background: "var(--colorNeutralBackground2)",
border: "1px solid var(--colorNeutralStroke1)",
background: "#fafafa",
border: "1px solid #e1e1e1",
borderRadius: 8,
padding: 16,
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
@@ -56,11 +56,11 @@ const PermissionGroup: React.FC<PermissionGroupConfig> = ({ title, description,
}}
>
<Stack tokens={{ childrenGap: 5 }}>
<Text variant="medium" style={{ fontWeight: 600, color: "var(--colorNeutralForeground1)" }}>
<Text variant="medium" style={{ fontWeight: 600 }}>
{title}
</Text>
{description && (
<Text variant="small" styles={{ root: { color: "var(--colorNeutralForeground2)" } }}>
<Text variant="small" styles={{ root: { color: "#605E5C" } }}>
{description}
</Text>
)}
@@ -100,7 +100,7 @@ const AssignPermissions = () => {
return (
<Stack className="assignPermissionsContainer" tokens={{ childrenGap: 20 }}>
<Text variant="medium" style={{ color: "var(--colorNeutralForeground1)" }}>
<Text variant="medium">
{isSameAccount && copyJobState.migrationType === CopyJobMigrationType.Online
? ContainerCopyMessages.assignPermissions.intraAccountOnlineDescription(
copyJobState?.source?.account?.name || "",

View File

@@ -12,12 +12,7 @@ import useToggle from "./hooks/useToggle";
const managedIdentityTooltip = (
<Text>
{ContainerCopyMessages.defaultManagedIdentity.tooltip.content} &nbsp;
<Link
style={{ color: "var(--colorBrandForeground1)" }}
href={ContainerCopyMessages.defaultManagedIdentity.tooltip.href}
target="_blank"
rel="noopener noreferrer"
>
<Link href={ContainerCopyMessages.defaultManagedIdentity.tooltip.href} target="_blank" rel="noopener noreferrer">
{ContainerCopyMessages.defaultManagedIdentity.tooltip.hrefText}
</Link>
</Text>

View File

@@ -13,12 +13,7 @@ import InfoTooltip from "../Components/InfoTooltip";
const tooltipContent = (
<Text>
{ContainerCopyMessages.pointInTimeRestore.tooltip.content} &nbsp;
<Link
style={{ color: "var(--colorBrandForeground1)" }}
href={ContainerCopyMessages.pointInTimeRestore.tooltip.href}
target="_blank"
rel="noopener noreferrer"
>
<Link href={ContainerCopyMessages.pointInTimeRestore.tooltip.href} target="_blank" rel="noopener noreferrer">
{ContainerCopyMessages.pointInTimeRestore.tooltip.hrefText}
</Link>
</Text>

View File

@@ -5,7 +5,7 @@ exports[`AddManagedIdentity Snapshot Tests renders initial state correctly 1`] =
class="ms-Stack addManagedIdentityContainer css-109"
>
<span
class="themeText css-110"
class="css-110"
>
A system-assigned managed identity is restricted to one per resource and is tied to the lifecycle of this resource. Once enabled, you can grant permissions to the managed identity by using Azure role-based access control (Azure RBAC). The managed identity is authenticated with Microsoft Entra ID, so you dont have to store any credentials in code.
@@ -92,7 +92,7 @@ exports[`AddManagedIdentity Snapshot Tests renders loading state 1`] = `
class="ms-Stack addManagedIdentityContainer css-109"
>
<span
class="themeText css-110"
class="css-110"
>
A system-assigned managed identity is restricted to one per resource and is tied to the lifecycle of this resource. Once enabled, you can grant permissions to the managed identity by using Azure role-based access control (Azure RBAC). The managed identity is authenticated with Microsoft Entra ID, so you dont have to store any credentials in code.
@@ -192,13 +192,13 @@ exports[`AddManagedIdentity Snapshot Tests renders loading state 1`] = `
</div>
</div>
<span
class="themeText css-124"
class="css-124"
style="font-weight: 600;"
>
Enable system assigned managed identity
</span>
<span
class="themeText css-110"
class="css-110"
>
Enable system-assigned managed identity on the test-target-account. To confirm, click the "Yes" button.
</span>
@@ -261,7 +261,7 @@ exports[`AddManagedIdentity Snapshot Tests renders with toggle on and popover vi
class="ms-Stack addManagedIdentityContainer css-109"
>
<span
class="themeText css-110"
class="css-110"
>
A system-assigned managed identity is restricted to one per resource and is tied to the lifecycle of this resource. Once enabled, you can grant permissions to the managed identity by using Azure role-based access control (Azure RBAC). The managed identity is authenticated with Microsoft Entra ID, so you dont have to store any credentials in code.
@@ -345,13 +345,13 @@ exports[`AddManagedIdentity Snapshot Tests renders with toggle on and popover vi
style="max-width: 450px;"
>
<span
class="themeText css-124"
class="css-124"
style="font-weight: 600;"
>
Enable system assigned managed identity
</span>
<span
class="themeText css-110"
class="css-110"
>
Enable system-assigned managed identity on the test-target-account. To confirm, click the "Yes" button.
</span>

View File

@@ -22,10 +22,10 @@ const PopoverContainer: React.FC<PopoverContainerProps> = React.memo(
style={{ maxWidth: 450 }}
>
<LoadingOverlay isLoading={isLoading} label={ContainerCopyMessages.popoverOverlaySpinnerLabel} />
<Text variant="mediumPlus" className="themeText" style={{ fontWeight: 600 }}>
<Text variant="mediumPlus" style={{ fontWeight: 600 }}>
{title}
</Text>
<Text className="themeText">{children}</Text>
<Text>{children}</Text>
<Stack horizontal tokens={{ childrenGap: 20 }}>
<PrimaryButton text={"Yes"} onClick={onPrimary} disabled={isLoading} />
<DefaultButton text="No" onClick={onCancel} disabled={isLoading} />

View File

@@ -7,11 +7,11 @@ exports[`PopoverMessage Component Edge Cases should handle empty string title 1`
style="max-width: 450px;"
>
<span
class="themeText css-110"
class="css-110"
style="font-weight: 600;"
/>
<span
class="themeText css-111"
class="css-111"
>
<div>
Test content
@@ -74,7 +74,7 @@ exports[`PopoverMessage Component Edge Cases should handle null children 1`] = `
style="max-width: 450px;"
>
<span
class="themeText css-110"
class="css-110"
style="font-weight: 600;"
>
Test Title
@@ -136,7 +136,7 @@ exports[`PopoverMessage Component Edge Cases should handle undefined children 1`
style="max-width: 450px;"
>
<span
class="themeText css-110"
class="css-110"
style="font-weight: 600;"
>
Test Title
@@ -198,13 +198,13 @@ exports[`PopoverMessage Component Edge Cases should handle very long title 1`] =
style="max-width: 450px;"
>
<span
class="themeText css-110"
class="css-110"
style="font-weight: 600;"
>
This is a very long title that might cause layout issues or text wrapping in the popover component
</span>
<span
class="themeText css-111"
class="css-111"
>
<div>
Test content
@@ -269,13 +269,13 @@ exports[`PopoverMessage Component Rendering should render correctly when visible
style="max-width: 450px;"
>
<span
class="themeText css-110"
class="css-110"
style="font-weight: 600;"
>
Test Title
</span>
<span
class="themeText css-111"
class="css-111"
>
<div>
Test content
@@ -338,13 +338,13 @@ exports[`PopoverMessage Component Rendering should render correctly with differe
style="max-width: 450px;"
>
<span
class="themeText css-110"
class="css-110"
style="font-weight: 600;"
>
Test Title
</span>
<span
class="themeText css-111"
class="css-111"
>
<div>
<p>
@@ -412,13 +412,13 @@ exports[`PopoverMessage Component Rendering should render correctly with differe
style="max-width: 450px;"
>
<span
class="themeText css-110"
class="css-110"
style="font-weight: 600;"
>
Custom Title
</span>
<span
class="themeText css-111"
class="css-111"
>
<div>
Test content
@@ -485,13 +485,13 @@ exports[`PopoverMessage Component Rendering should render correctly with loading
data-testid="loading-overlay"
/>
<span
class="themeText css-110"
class="css-110"
style="font-weight: 600;"
>
Test Title
</span>
<span
class="themeText css-111"
class="css-111"
>
<div>
Test content

View File

@@ -41,7 +41,7 @@ const AddCollectionPanelWrapper: React.FunctionComponent<AddCollectionPanelWrapp
return (
<Stack className="addCollectionPanelWrapper">
<Stack.Item className="addCollectionPanelHeader">
<Text className="themeText">{ContainerCopyMessages.createNewContainerSubHeading}</Text>
<Text>{ContainerCopyMessages.createNewContainerSubHeading}</Text>
</Stack.Item>
<Stack.Item className="addCollectionPanelBody">
<AddCollectionPanel explorer={explorer} isCopyJobFlow={true} onSubmitSuccess={handleAddCollectionSuccess} />

View File

@@ -9,7 +9,7 @@ exports[`AddCollectionPanelWrapper Component Rendering should match snapshot 1`]
class="ms-StackItem addCollectionPanelHeader css-110"
>
<span
class="themeText css-111"
class="css-111"
>
Select the properties for your container.
</span>
@@ -50,7 +50,7 @@ exports[`AddCollectionPanelWrapper Component Rendering should match snapshot wit
class="ms-StackItem addCollectionPanelHeader css-110"
>
<span
class="themeText css-111"
class="css-111"
>
Select the properties for your container.
</span>
@@ -91,7 +91,7 @@ exports[`AddCollectionPanelWrapper Component Rendering should match snapshot wit
class="ms-StackItem addCollectionPanelHeader css-110"
>
<span
class="themeText css-111"
class="css-111"
>
Select the properties for your container.
</span>
@@ -132,7 +132,7 @@ exports[`AddCollectionPanelWrapper Component Rendering should match snapshot wit
class="ms-StackItem addCollectionPanelHeader css-110"
>
<span
class="themeText css-111"
class="css-111"
>
Select the properties for your container.
</span>

View File

@@ -36,12 +36,12 @@ const PreviewCopyJob: React.FC = () => {
<TextField value={jobName} onChange={onJobNameChange} />
</FieldRow>
<Stack>
<Text className="bold themeText">{ContainerCopyMessages.sourceSubscriptionLabel}</Text>
<Text className="themeText">{copyJobState.source?.subscription?.displayName}</Text>
<Text className="bold">{ContainerCopyMessages.sourceSubscriptionLabel}</Text>
<Text>{copyJobState.source?.subscription?.displayName}</Text>
</Stack>
<Stack>
<Text className="bold themeText">{ContainerCopyMessages.sourceAccountLabel}</Text>
<Text className="themeText">{copyJobState.source?.account?.name}</Text>
<Text className="bold">{ContainerCopyMessages.sourceAccountLabel}</Text>
<Text>{copyJobState.source?.account?.name}</Text>
</Stack>
<Stack>
<DetailsList

View File

@@ -1,6 +1,6 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/display-name */
import { Checkbox, ICheckboxStyles, Stack } from "@fluentui/react";
import { Checkbox, Stack } from "@fluentui/react";
import React from "react";
import ContainerCopyMessages from "../../../../ContainerCopyMessages";
@@ -9,25 +9,8 @@ interface MigrationTypeCheckboxProps {
onChange: (_ev?: React.FormEvent, checked?: boolean) => void;
}
const checkboxStyles: ICheckboxStyles = {
text: { color: "var(--colorNeutralForeground1)" },
checkbox: { borderColor: "var(--colorNeutralStroke1)" },
root: {
selectors: {
":hover .ms-Checkbox-text": { color: "var(--colorNeutralForeground1)" },
},
},
};
export const MigrationTypeCheckbox: React.FC<MigrationTypeCheckboxProps> = React.memo(({ checked, onChange }) => {
return (
<Stack horizontal horizontalAlign="space-between" className="migrationTypeRow">
<Checkbox
label={ContainerCopyMessages.migrationTypeCheckboxLabel}
checked={checked}
onChange={onChange}
styles={checkboxStyles}
/>
</Stack>
);
});
export const MigrationTypeCheckbox: React.FC<MigrationTypeCheckboxProps> = React.memo(({ checked, onChange }) => (
<Stack horizontal horizontalAlign="space-between" className="migrationTypeRow">
<Checkbox label={ContainerCopyMessages.migrationTypeCheckboxLabel} checked={checked} onChange={onChange} />
</Stack>
));

View File

@@ -21,7 +21,7 @@ const SelectAccount = React.memo(() => {
return (
<Stack data-test="Panel:SelectAccountContainer" className="selectAccountContainer" tokens={{ childrenGap: 15 }}>
<Text className="themeText">{ContainerCopyMessages.selectAccountDescription}</Text>
<Text>{ContainerCopyMessages.selectAccountDescription}</Text>
<SubscriptionDropdown />

View File

@@ -6,7 +6,7 @@ exports[`SelectAccount Component Rendering should render correctly with snapshot
data-test="Panel:SelectAccountContainer"
>
<span
class="themeText css-110"
class="css-110"
>
Please select a source account from which to copy.
</span>

View File

@@ -48,7 +48,7 @@ const SelectSourceAndTargetContainers = ({ showAddCollectionPanel }: SelectSourc
return (
<Stack className="selectSourceAndTargetContainers" tokens={{ childrenGap: 25 }}>
<span className="themeText">{ContainerCopyMessages.selectSourceAndTargetContainersDescription}</span>
<span>{ContainerCopyMessages.selectSourceAndTargetContainersDescription}</span>
<DatabaseContainerSection
heading={ContainerCopyMessages.sourceContainerSubHeading}
databaseOptions={sourceDatabaseOptions}

View File

@@ -41,11 +41,7 @@ export const DatabaseContainerSection = ({
onChange={containerOnChange}
/>
{handleOnDemandCreateContainer && (
<ActionButton
className="create-container-link-btn"
style={{ color: "var(--colorBrandForeground1)" }}
onClick={() => handleOnDemandCreateContainer()}
>
<ActionButton className="create-container-link-btn" onClick={() => handleOnDemandCreateContainer()}>
{ContainerCopyMessages.createContainerButtonLabel}
</ActionButton>
)}

View File

@@ -1,6 +1,5 @@
import { DetailsList, DetailsListLayoutMode, IColumn, Stack, Text } from "@fluentui/react";
import React, { memo } from "react";
import { useThemeStore } from "../../../../hooks/useTheme";
import ContainerCopyMessages from "../../ContainerCopyMessages";
import { CopyJobStatusType } from "../../Enums/CopyJobEnums";
import { CopyJobType } from "../../Types/CopyJobTypes";
@@ -64,19 +63,6 @@ const getCopyJobDetailsListColumns = (): IColumn[] => {
};
const CopyJobDetails: React.FC<CopyJobDetailsProps> = ({ job }) => {
const isDarkMode = useThemeStore((state) => state.isDarkMode);
const errorMessageStyle: React.CSSProperties = {
whiteSpace: "pre-wrap",
...(isDarkMode && {
whiteSpace: "pre-wrap",
backgroundColor: "var(--colorNeutralBackground2)",
color: "var(--colorNeutralForeground1)",
padding: "10px",
borderRadius: "4px",
}),
};
const selectedContainers = [
{
sourceContainerName: job?.Source?.containerName || "N/A",
@@ -91,10 +77,10 @@ const CopyJobDetails: React.FC<CopyJobDetailsProps> = ({ job }) => {
<Stack className="copyJobDetailsContainer" tokens={{ childrenGap: 15 }} data-testid="copy-job-details">
{job.Error ? (
<Stack.Item data-testid="error-stack" style={sectionCss.verticalAlign}>
<Text className="bold themeText" style={sectionCss.headingText}>
<Text className="bold" style={sectionCss.headingText}>
{ContainerCopyMessages.errorTitle}
</Text>
<Text as="pre" style={errorMessageStyle}>
<Text as="pre" style={{ whiteSpace: "pre-wrap" }}>
{job.Error.message}
</Text>
</Stack.Item>
@@ -102,16 +88,16 @@ const CopyJobDetails: React.FC<CopyJobDetailsProps> = ({ job }) => {
<Stack.Item data-testid="selectedcollection-stack">
<Stack tokens={{ childrenGap: 15 }}>
<Stack.Item style={sectionCss.verticalAlign}>
<Text className="bold themeText">{ContainerCopyMessages.MonitorJobs.Columns.lastUpdatedTime}</Text>
<Text className="themeText">{job.LastUpdatedTime}</Text>
<Text className="bold">{ContainerCopyMessages.MonitorJobs.Columns.lastUpdatedTime}</Text>
<Text>{job.LastUpdatedTime}</Text>
</Stack.Item>
<Stack.Item style={sectionCss.verticalAlign}>
<Text className="bold themeText">{ContainerCopyMessages.sourceAccountLabel}</Text>
<Text className="themeText">{job.Source?.remoteAccountName}</Text>
<Text className="bold">{ContainerCopyMessages.sourceAccountLabel}</Text>
<Text>{job.Source?.remoteAccountName}</Text>
</Stack.Item>
<Stack.Item style={sectionCss.verticalAlign}>
<Text className="bold themeText">{ContainerCopyMessages.MonitorJobs.Columns.mode}</Text>
<Text className="themeText">{job.Mode}</Text>
<Text className="bold">{ContainerCopyMessages.MonitorJobs.Columns.mode}</Text>
<Text>{job.Mode}</Text>
</Stack.Item>
</Stack>
</Stack.Item>

View File

@@ -1,14 +1,30 @@
import { FontIcon, mergeStyles, Spinner, SpinnerSize, Stack, Text } from "@fluentui/react";
import { FontIcon, getTheme, mergeStyles, mergeStyleSets, Spinner, SpinnerSize, Stack, Text } from "@fluentui/react";
import PropTypes from "prop-types";
import React from "react";
import ContainerCopyMessages from "../../ContainerCopyMessages";
import { CopyJobStatusType } from "../../Enums/CopyJobEnums";
const theme = getTheme();
const iconClass = mergeStyles({
fontSize: "16px",
marginRight: "8px",
});
const classNames = mergeStyleSets({
[CopyJobStatusType.Pending]: [{ color: theme.semanticColors.bodySubtext }, iconClass],
[CopyJobStatusType.InProgress]: [{ color: theme.palette.themePrimary }, iconClass],
[CopyJobStatusType.Running]: [{ color: theme.palette.themePrimary }, iconClass],
[CopyJobStatusType.Partitioning]: [{ color: theme.palette.themePrimary }, iconClass],
[CopyJobStatusType.Paused]: [{ color: theme.palette.themePrimary }, iconClass],
[CopyJobStatusType.Skipped]: [{ color: theme.semanticColors.bodySubtext }, iconClass],
[CopyJobStatusType.Cancelled]: [{ color: theme.semanticColors.bodySubtext }, iconClass],
[CopyJobStatusType.Failed]: [{ color: theme.semanticColors.errorIcon }, iconClass],
[CopyJobStatusType.Faulted]: [{ color: theme.semanticColors.errorIcon }, iconClass],
[CopyJobStatusType.Completed]: [{ color: theme.semanticColors.successIcon }, iconClass],
unknown: [{ color: theme.semanticColors.bodySubtext }, iconClass],
});
const iconMap: Partial<Record<CopyJobStatusType, string>> = {
[CopyJobStatusType.Pending]: "Clock",
[CopyJobStatusType.Paused]: "CirclePause",
@@ -19,17 +35,6 @@ const iconMap: Partial<Record<CopyJobStatusType, string>> = {
[CopyJobStatusType.Completed]: "CompletedSolid",
};
// Icon colors for different statuses
const statusIconColors: Partial<Record<CopyJobStatusType, string>> = {
[CopyJobStatusType.Failed]: "var(--colorPaletteRedForeground1)",
[CopyJobStatusType.Faulted]: "var(--colorPaletteRedForeground1)",
[CopyJobStatusType.Completed]: "#107c10", // Green color for success
[CopyJobStatusType.InProgress]: "var(--colorBrandForeground1)",
[CopyJobStatusType.Running]: "var(--colorBrandForeground1)",
[CopyJobStatusType.Partitioning]: "var(--colorBrandForeground1)",
[CopyJobStatusType.Paused]: "var(--colorBrandForeground1)",
};
export interface CopyJobStatusWithIconProps {
status: CopyJobStatusType;
}
@@ -42,17 +47,19 @@ const CopyJobStatusWithIcon: React.FC<CopyJobStatusWithIconProps> = React.memo((
CopyJobStatusType.InProgress,
CopyJobStatusType.Partitioning,
].includes(status);
const iconColor = statusIconColors[status] || "var(--colorNeutralForeground2)";
const iconStyle = mergeStyles(iconClass, { color: iconColor });
return (
<Stack horizontal verticalAlign="center">
{isSpinnerStatus ? (
<Spinner size={SpinnerSize.small} style={{ marginRight: "8px" }} />
) : (
<FontIcon aria-label={status} iconName={iconMap[status] || "UnknownSolid"} className={iconStyle} />
<FontIcon
aria-label={status}
iconName={iconMap[status] || "UnknownSolid"}
className={classNames[status] || classNames.unknown}
/>
)}
<Text className="themeText">{statusText}</Text>
<Text>{statusText}</Text>
</Stack>
);
});

View File

@@ -15,8 +15,6 @@ import {
} from "@fluentui/react";
import React, { useEffect } from "react";
import Pager from "../../../../Common/Pager";
import { useThemeStore } from "../../../../hooks/useTheme";
import { getThemeTokens } from "../../../Theme/ThemeUtil";
import { openCopyJobDetailsPanel } from "../../Actions/CopyJobActions";
import { CopyJobType, HandleJobActionClickType } from "../../Types/CopyJobTypes";
import { getColumns } from "./CopyJobColumns";
@@ -28,15 +26,13 @@ interface CopyJobsListProps {
}
const styles = {
container: { height: "100%" } as React.CSSProperties,
container: { height: "calc(100vh - 25em)" } as React.CSSProperties,
stackItem: { position: "relative", marginBottom: "20px" } as React.CSSProperties,
};
const PAGE_SIZE = 10;
const CopyJobsList: React.FC<CopyJobsListProps> = ({ jobs, handleActionClick, pageSize = PAGE_SIZE }) => {
const isDarkMode = useThemeStore((state) => state.isDarkMode);
const themeTokens = getThemeTokens(isDarkMode);
const [startIndex, setStartIndex] = React.useState(0);
const [sortedJobs, setSortedJobs] = React.useState<CopyJobType[]>(jobs);
const [sortedColumnKey, setSortedColumnKey] = React.useState<string | undefined>(undefined);
@@ -91,28 +87,11 @@ const CopyJobsList: React.FC<CopyJobsListProps> = ({ jobs, handleActionClick, pa
enableShimmer={false}
constrainMode={ConstrainMode.unconstrained}
layoutMode={DetailsListLayoutMode.justified}
onRenderDetailsHeader={(props, defaultRender) => {
const bgColor = themeTokens.colorNeutralBackground3;
const textColor = themeTokens.colorNeutralForeground1;
return (
<Sticky stickyPosition={StickyPositionType.Header} isScrollSynced stickyBackgroundColor={bgColor}>
<div style={{ backgroundColor: bgColor }}>
{defaultRender({
...props,
styles: {
root: {
backgroundColor: bgColor,
selectors: {
".ms-DetailsHeader-cellTitle": { color: textColor },
".ms-DetailsHeader-cellName": { color: textColor },
},
},
},
})}
</div>
</Sticky>
);
}}
onRenderDetailsHeader={(props, defaultRender) => (
<Sticky stickyPosition={StickyPositionType.Header} isScrollSynced>
{defaultRender({ ...props })}
</Sticky>
)}
/>
</ScrollablePane>
</Stack.Item>

View File

@@ -13,7 +13,7 @@ exports[`CopyJobStatusWithIcon Spinner Status Types renders InProgress with spin
/>
</div>
<span
class="themeText css-112"
class="css-112"
>
Running
</span>
@@ -33,7 +33,7 @@ exports[`CopyJobStatusWithIcon Spinner Status Types renders Partitioning with sp
/>
</div>
<span
class="themeText css-112"
class="css-112"
>
Running
</span>
@@ -53,7 +53,7 @@ exports[`CopyJobStatusWithIcon Spinner Status Types renders Running with spinner
/>
</div>
<span
class="themeText css-112"
class="css-112"
>
Running
</span>
@@ -66,7 +66,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
>
<i
aria-label="Cancelled"
class="ms-Icon root-105 css-118 mocked-styles"
class="ms-Icon root-105 css-118 mocked-style-Cancelled"
data-icon-name="StatusErrorFull"
role="img"
style="font-family: "FabricMDL2Icons-4";"
@@ -74,7 +74,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
</i>
<span
class="themeText css-112"
class="css-112"
>
Cancelled
</span>
@@ -87,7 +87,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
>
<i
aria-label="Completed"
class="ms-Icon root-105 css-120 mocked-styles"
class="ms-Icon root-105 css-120 mocked-style-Completed"
data-icon-name="CompletedSolid"
role="img"
style="font-family: "FabricMDL2Icons-5";"
@@ -95,7 +95,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
</i>
<span
class="themeText css-112"
class="css-112"
>
Completed
</span>
@@ -108,7 +108,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
>
<i
aria-label="Failed"
class="ms-Icon root-105 css-118 mocked-styles"
class="ms-Icon root-105 css-118 mocked-style-Failed"
data-icon-name="StatusErrorFull"
role="img"
style="font-family: "FabricMDL2Icons-4";"
@@ -116,7 +116,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
</i>
<span
class="themeText css-112"
class="css-112"
>
Failed
</span>
@@ -129,7 +129,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
>
<i
aria-label="Faulted"
class="ms-Icon root-105 css-118 mocked-styles"
class="ms-Icon root-105 css-118 mocked-style-Faulted"
data-icon-name="StatusErrorFull"
role="img"
style="font-family: "FabricMDL2Icons-4";"
@@ -137,7 +137,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
</i>
<span
class="themeText css-112"
class="css-112"
>
Failed
</span>
@@ -150,7 +150,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
>
<i
aria-label="Paused"
class="ms-Icon root-105 css-114 mocked-styles"
class="ms-Icon root-105 css-114 mocked-style-Paused"
data-icon-name="CirclePause"
role="img"
style="font-family: "FabricMDL2Icons-11";"
@@ -158,7 +158,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
</i>
<span
class="themeText css-112"
class="css-112"
>
Paused
</span>
@@ -171,7 +171,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
>
<i
aria-label="Pending"
class="ms-Icon root-105 css-111 mocked-styles"
class="ms-Icon root-105 css-111 mocked-style-Pending"
data-icon-name="Clock"
role="img"
style="font-family: "FabricMDL2Icons-2";"
@@ -179,7 +179,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
</i>
<span
class="themeText css-112"
class="css-112"
>
Queued
</span>
@@ -192,7 +192,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
>
<i
aria-label="Skipped"
class="ms-Icon root-105 css-116 mocked-styles"
class="ms-Icon root-105 css-116 mocked-style-Skipped"
data-icon-name="StatusCircleBlock2"
role="img"
style="font-family: "FabricMDL2Icons-9";"
@@ -200,7 +200,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
</i>
<span
class="themeText css-112"
class="css-112"
>
Cancelled
</span>

View File

@@ -1,30 +1,6 @@
@import "../../../less/Common/Constants.less";
// Common theme-aware classes
.themeText {
color: var(--colorNeutralForeground1);
}
.themeTextSecondary {
color: var(--colorNeutralForeground2);
}
.themeLinkText {
color: var(--colorBrandForeground1);
}
.themeBackground {
background-color: var(--colorNeutralBackground1);
}
.themeBackgroundSecondary {
background-color: var(--colorNeutralBackground2);
}
#containerCopyWrapper {
background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
.centerContent {
justify-content: center;
align-items: center;
@@ -33,30 +9,20 @@
.noCopyJobsMessage {
font-weight: 600;
margin: 0 auto;
color: var(--colorNeutralForeground2);
color: @FocusColor;
}
button.createCopyJobButton {
color: var(--colorBrandForeground1);
color: @LinkColor;
}
}
}
.createCopyJobScreensContainer {
height: 100%;
padding: 1em 1.5em;
background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
.pointInTimeRestoreContainer, .onlineCopyContainer {
position: relative;
}
.toggle-label {
color: var(--colorNeutralForeground1);
}
.accordionHeaderText {
color: var(--colorNeutralForeground1);
}
label {
padding: 0;
@@ -105,7 +71,7 @@
}
.foreground {
z-index: 10;
background-color: var(--colorNeutralBackground2);
background-color: #f9f9f9;
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transform: translate(0%, -9%);
@@ -114,40 +80,14 @@
.createCopyJobErrorMessageBar {
margin-bottom: 2em;
}
body.isDarkMode & {
.ms-TooltipHost .ms-Image {
filter: invert(1);
}
.ms-TextField {
.ms-TextField-fieldGroup {
background-color: var(--colorNeutralBackground1);
border-color: var(--colorNeutralStroke1);
}
.ms-TextField-field {
color: var(--colorNeutralForeground1);
background-color: var(--colorNeutralBackground1);
&::placeholder {
color: var(--colorNeutralForeground4);
}
}
.ms-Label {
color: var(--colorNeutralForeground1);
}
}
}
.create-container-link-btn {
padding: 0;
height: 25px;
color: var(--colorBrandForeground1);
color: @LinkColor;
&:focus {
outline: none;
}
}
/* Create collection panel */
@@ -165,6 +105,7 @@
width: 100%;
max-width: 100%;
margin: 0 auto;
.ms-DetailsList {
width: 100%;
@@ -173,33 +114,33 @@
padding: @DefaultSpace 20px;
font-weight: 600;
font-size: @DefaultFontSize;
color: var(--colorNeutralForeground1);
background-color: var(--colorNeutralBackground2);
border-bottom: @ButtonBorderWidth solid var(--colorNeutralStroke1);
color: @BaseHigh;
background-color: @BaseLow;
border-bottom: @ButtonBorderWidth solid @BaseMedium;
&:hover {
background-color: var(--colorNeutralBackground3);
background-color: @BaseMediumLow;
}
}
}
.ms-DetailsRow {
border-bottom: @ButtonBorderWidth solid var(--colorNeutralStroke1);
border-bottom: @ButtonBorderWidth solid @BaseMedium;
&:hover {
background-color: var(--colorNeutralBackground2);
background-color: @BaseMediumLow;
}
.ms-DetailsRow-cell {
padding: @MediumSpace 20px;
font-size: @DefaultFontSize;
color: var(--colorNeutralForeground1);
color: @BaseHigh;
min-height: 48px;
display: flex;
align-items: center;
.jobNameLink {
color: var(--colorBrandForeground1);
color: @LinkColor;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
@@ -227,7 +168,7 @@
}
.ms-DetailsRow-cell {
font-size: @DefaultFontSize;
color: var(--colorNeutralForeground1);
color: @BaseHigh;
}
}
}

View File

@@ -103,10 +103,7 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
offText="Inactive"
checked={bucket.maxThroughputPercentage !== 100}
onChange={(event, checked) => onToggle(bucket.id, checked)}
styles={{
root: { marginBottom: 0 },
text: { fontSize: 12, color: "var(--colorNeutralForeground1)" },
}}
styles={{ root: { marginBottom: 0 }, text: { fontSize: 12 } }}
></Toggle>
</Stack>
))}

View File

@@ -53,7 +53,6 @@ type VectorEmbeddingPolicyProperty = "dataType" | "distanceFunction" | "indexTyp
const labelStyles = {
root: {
fontSize: 12,
color: "var(--colorNeutralForeground1)",
},
};
@@ -64,8 +63,6 @@ const textFieldStyles: IStyleFunctionOrObject<ITextFieldStyleProps, ITextFieldSt
field: {
fontSize: 12,
padding: "0 8px",
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)",
},
};

View File

@@ -437,13 +437,14 @@ export default class Explorer {
public onRefreshResourcesClick = async (): Promise<void> => {
if (isFabricMirroredKey()) {
scheduleRefreshFabricToken(true).then(() => this.refreshAllDatabases());
return;
} else {
await (userContext.authType === AuthType.ResourceToken
? this.refreshDatabaseForResourceToken()
: this.refreshAllDatabases());
await this.refreshNotebookList();
}
await (userContext.authType === AuthType.ResourceToken
? this.refreshDatabaseForResourceToken()
: this.refreshAllDatabases());
await this.refreshNotebookList();
logConsoleInfo("Successfully refreshed databases");
};
// Facade

View File

@@ -853,7 +853,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
{!isSynapseLinkEnabled() && (
<Stack className="panelGroupSpacing">
<Text variant="small" style={{ color: "var(--colorNeutralForeground1)" }}>
<Text variant="small">
Azure Synapse Link is required for creating an analytical store{" "}
{getCollectionName().toLocaleLowerCase()}. Enable Synapse Link for this Cosmos DB account. <br />
<Link

View File

@@ -475,11 +475,6 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
className="panelGroupSpacing"
>
<Text
style={
{
"color": "var(--colorNeutralForeground1)",
}
}
variant="small"
>
Azure Synapse Link is required for creating an analytical store

View File

@@ -2,7 +2,7 @@ import React from "react";
import LoadingIndicator_3Squares from "../../../images/LoadingIndicator_3Squares.gif";
export const PanelLoadingScreen: React.FunctionComponent = () => (
<div id="loadingScreen" className="dataExplorerLoaderContainer dataExplorerLoaderforcopyJobs">
<div id="loadingScreen" className="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer">
<img className="dataExplorerLoader" src={LoadingIndicator_3Squares} />
</div>
);

View File

@@ -598,6 +598,9 @@ export default class Collection implements ViewModels.Collection {
public onSettingsClick = async (): Promise<void> => {
useSelectedNode.getState().setSelectedNode(this);
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
// // Refresh all databases in case they were deleted outside of user session
await this.container.onRefreshResourcesClick();
throughputCap && throughputCap !== -1 ? await useDatabases.getState().loadAllOffers() : await this.loadOffer();
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings);
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {

View File

@@ -125,10 +125,7 @@ const App = (): JSX.Element => {
<KeyboardShortcutRoot>
<div className="flexContainer" aria-hidden="false">
{userContext.features.enableContainerCopy && userContext.apiType === "SQL" ? (
<>
<ContainerCopyPanel explorer={explorer} />
<SidePanel />
</>
<ContainerCopyPanel explorer={explorer} />
) : (
<DivExplorer explorer={explorer} />
)}

View File

@@ -316,6 +316,11 @@ body.isDarkMode {
background-color: transparent;
}
// High specificity override for any nested elements
* {
color: var(--colorNeutralForeground1);
}
// Ensure links maintain proper colors
.ms-Link {
color: var(--colorBrandForeground1);
@@ -433,6 +438,7 @@ body.isDarkMode {
button {
&:not(.ms-Button):not(.ms-IconButton) {
background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
&:hover {

View File

@@ -8,7 +8,8 @@ test("Cassandra keyspace and table CRUD", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.Cassandra);
await explorer.globalCommandButton("New Table").click();
const newTableButton = await explorer.globalCommandButton("New Table");
await newTableButton.click();
await explorer.whilePanelOpen(
"Add Table",
async (panel, okButton) => {

View File

@@ -352,8 +352,9 @@ export class DataExplorer {
*
* There's only a single "primary" button, but we still require you to pass the label to confirm you're selecting the right button.
*/
globalCommandButton(label: string): Locator {
return this.frame.getByTestId("GlobalCommands").getByText(label);
async globalCommandButton(label: string): Promise<Locator> {
await this.frame.getByTestId("GlobalCommands").click();
return this.frame.getByRole("menuitem", { name: label });
}
/** Select the command bar button with the specified label */
@@ -459,6 +460,15 @@ export class DataExplorer {
const containerNode = await this.waitForContainerNode(context.database.id, context.container.id);
await containerNode.expand();
// // refresh tree to remove deleted database
// const consoleMessages = await this.getNotificationConsoleMessages();
// const refreshButton = this.frame.getByTestId("Sidebar/RefreshButton");
// await refreshButton.click();
// await expect(consoleMessages).toContainText("Successfully refreshed databases", {
// timeout: ONE_MINUTE_MS,
// });
// await this.collapseNotificationConsole();
const scaleAndSettingsButton = this.frame.getByTestId(
`TreeNode:${context.database.id}/${context.container.id}/Scale & Settings`,
);
@@ -466,10 +476,35 @@ export class DataExplorer {
}
/** Gets the console message element */
getConsoleMessage(): Locator {
getConsoleHeaderStatus(): Locator {
return this.frame.getByTestId("notification-console/header-status");
}
async expandNotificationConsole(): Promise<void> {
await this.setNotificationConsoleExpanded(true);
}
async collapseNotificationConsole(): Promise<void> {
await this.setNotificationConsoleExpanded(false);
}
async setNotificationConsoleExpanded(expanded: boolean): Promise<void> {
const notificationConsoleToggleButton = this.frame.getByTestId("NotificationConsole/ExpandCollapseButton");
const alt = await notificationConsoleToggleButton.locator("img").getAttribute("alt");
// When expanded, the icon says "Collapse icon"
if (expanded && alt === "Expand icon") {
await notificationConsoleToggleButton.click();
} else if (!expanded && alt === "Collapse icon") {
await notificationConsoleToggleButton.click();
}
}
async getNotificationConsoleMessages(): Promise<Locator> {
await this.setNotificationConsoleExpanded(true);
return this.frame.getByTestId("NotificationConsole/Contents");
}
async getDropdownItemByName(name: string, ariaLabel?: string): Promise<Locator> {
const dropdownItemsWrapper = this.frame.locator("div.ms-Dropdown-items");
if (ariaLabel) {

View File

@@ -9,7 +9,8 @@ test("Gremlin graph CRUD", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.Gremlin);
// Create new database and graph
await explorer.globalCommandButton("New Graph").click();
const newGraphButton = await explorer.globalCommandButton("New Graph");
await newGraphButton.click();
await explorer.whilePanelOpen(
"New Graph",
async (panel, okButton) => {

View File

@@ -14,7 +14,8 @@ import { DataExplorer, TEST_AUTOSCALE_THROUGHPUT_RU, TestAccount, generateUnique
const explorer = await DataExplorer.open(page, accountType);
await explorer.globalCommandButton("New Collection").click();
const newCollectionButton = await explorer.globalCommandButton("New Collection");
await newCollectionButton.click();
await explorer.whilePanelOpen(
"New Collection",
async (panel, okButton) => {

View File

@@ -8,7 +8,8 @@ test("SQL database and container CRUD", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.SQL);
await explorer.globalCommandButton("New Container").click();
const newContainerButton = await explorer.globalCommandButton("New Container");
await newContainerButton.click();
await explorer.whilePanelOpen(
"New Container",
async (panel, okButton) => {

View File

@@ -6,7 +6,7 @@ test.describe("Change Partition Key", () => {
let pageInstance: Page;
let context: TestContainerContext = null!;
let explorer: DataExplorer = null!;
const newPartitionKeyPath = "/newPartitionKey";
const newPartitionKeyPath = "newPartitionKey";
const newContainerId = "testcontainer_1";
test.beforeAll("Create Test Database", async () => {
@@ -20,12 +20,16 @@ test.describe("Change Partition Key", () => {
// Click Scale & Settings and open Partition Key tab
await explorer.openScaleAndSettings(context);
const PartitionKeyTab = explorer.frame.getByTestId("settings-tab-header/PartitionKeyTab");
await expect(PartitionKeyTab).toBeVisible();
await PartitionKeyTab.click();
});
test.afterAll("Delete Test Database", async () => {
await context?.dispose();
});
if (!process.env.CI) {
test.afterEach("Delete Test Database", async () => {
await context?.dispose();
});
}
test("Change partition key path", async () => {
await expect(explorer.frame.getByText("/partitionKey")).toBeVisible();
@@ -53,16 +57,16 @@ test.describe("Change Partition Key", () => {
changePkPanel.getByTestId("new-container-partition-key-input").fill(newPartitionKeyPath);
await expect(changePkPanel.getByTestId("add-sub-partition-key-button")).toBeVisible();
changePkPanel.getByTestId("add-sub-partition-key-button").click();
await expect(changePkPanel.getByTestId("new-container-sub-partition-key-input-0")).toBeVisible();
await expect(changePkPanel.getByTestId("remove-sub-partition-key-button-0")).toBeVisible();
await expect(changePkPanel.getByTestId("hierarchical-partitioning-info-text")).toBeVisible();
changePkPanel.getByTestId("new-container-sub-partition-key-input-0").fill("/customerId");
// changePkPanel.getByTestId("add-sub-partition-key-button").click();
// await expect(changePkPanel.getByTestId("new-container-sub-partition-key-input-0")).toBeVisible();
// await expect(changePkPanel.getByTestId("remove-sub-partition-key-button-0")).toBeVisible();
// await expect(changePkPanel.getByTestId("hierarchical-partitioning-info-text")).toBeVisible();
// changePkPanel.getByTestId("new-container-sub-partition-key-input-0").fill("customerId");
await changePkPanel.getByTestId("Panel/OkButton").click();
await pageInstance.waitForLoadState("networkidle");
await expect(changePkPanel).not.toBeVisible({ timeout: 60 * 1000 });
// await pageInstance.waitForLoadState("networkidle");
await expect(changePkPanel).not.toBeVisible({ timeout: 5 * 60 * 1000 });
// Verify partition key change job
const jobText = explorer.frame.getByText(/Partition key change job/);

View File

@@ -9,39 +9,38 @@ import {
} from "../../fx";
import { createTestSQLContainer, TestContainerContext } from "../../testData";
test.describe("Autoscale and Manual throughput", () => {
test.describe("Autoscale throughput", () => {
let context: TestContainerContext = null!;
let explorer: DataExplorer = null!;
test.beforeAll("Create Test Database", async () => {
context = await createTestSQLContainer({ includeTestData: true });
});
test.beforeEach("Open container settings", async ({ page }) => {
test.beforeAll("Create Test Database", async ({ browser }) => {
context = await createTestSQLContainer();
const page = await browser.newPage();
explorer = await DataExplorer.open(page, TestAccount.SQL);
// Click Scale & Settings and open Scale tab
await explorer.openScaleAndSettings(context);
const scaleTab = explorer.frame.getByTestId("settings-tab-header/ScaleTab");
await scaleTab.click();
await switchManualToAutoscaleThroughput();
});
test.afterAll("Delete Test Database", async () => {
await context?.dispose();
});
if (!process.env.CI) {
test.afterAll("Delete Test Database", async () => {
await context?.dispose();
});
}
test("Update autoscale max throughput", async () => {
// By default the created container has manual throughput (Containers created via JS SDK v4.7.0 cannot be created with autoscale throughput)
await switchManualToAutoscaleThroughput();
// Update autoscale max throughput
await getThroughputInput("autopilot").fill(TEST_AUTOSCALE_MAX_THROUGHPUT_RU_2K.toString());
await getThroughputInput(explorer, "autopilot").fill(TEST_AUTOSCALE_MAX_THROUGHPUT_RU_2K.toString());
// Save
await explorer.commandBarButton(CommandBarButton.Save).click();
// Read console message
await expect(explorer.getConsoleMessage()).toContainText(
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated offer for collection ${context.container.id}`,
{
timeout: 2 * ONE_MINUTE_MS,
@@ -50,9 +49,6 @@ test.describe("Autoscale and Manual throughput", () => {
});
test("Update autoscale max throughput passed allowed limit", async () => {
// By default the created container has manual throughput (Containers created via JS SDK v4.7.0 cannot be created with autoscale throughput)
await switchManualToAutoscaleThroughput();
// Get soft allowed max throughput and remove commas
const softAllowedMaxThroughputString = await explorer.frame
.getByTestId("soft-allowed-maximum-throughput")
@@ -60,29 +56,65 @@ test.describe("Autoscale and Manual throughput", () => {
const softAllowedMaxThroughput = Number(softAllowedMaxThroughputString.replace(/,/g, ""));
// Try to set autoscale max throughput above allowed limit
await getThroughputInput("autopilot").fill((softAllowedMaxThroughput * 10).toString());
await getThroughputInput(explorer, "autopilot").fill((softAllowedMaxThroughput * 10).toString());
await expect(explorer.commandBarButton(CommandBarButton.Save)).toBeDisabled();
await expect(getThroughputInputErrorMessage("autopilot")).toContainText(
await expect(getThroughputInputErrorMessage(explorer, "autopilot")).toContainText(
"This update isn't possible because it would increase the total throughput",
);
});
test("Update autoscale max throughput with invalid increment", async () => {
// By default the created container has manual throughput (Containers created via JS SDK v4.7.0 cannot be created with autoscale throughput)
await switchManualToAutoscaleThroughput();
// Try to set autoscale max throughput with invalid increment
await getThroughputInput("autopilot").fill("1100");
await getThroughputInput(explorer, "autopilot").fill("1100");
await expect(explorer.commandBarButton(CommandBarButton.Save)).toBeDisabled();
await expect(getThroughputInputErrorMessage("autopilot")).toContainText(
await expect(getThroughputInputErrorMessage(explorer, "autopilot")).toContainText(
"Throughput value must be in increments of 1000",
);
});
test("Update manual throughput", async () => {
await getThroughputInput("manual").fill(TEST_MANUAL_THROUGHPUT_RU_2K.toString());
const switchManualToAutoscaleThroughput = async (): Promise<void> => {
const autoscaleRadioButton = explorer.frame.getByText("Autoscale", { exact: true });
await autoscaleRadioButton.click();
await expect(explorer.commandBarButton(CommandBarButton.Save)).toBeEnabled();
await explorer.commandBarButton(CommandBarButton.Save).click();
await expect(explorer.getConsoleMessage()).toContainText(
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated offer for collection ${context.container.id}`,
{
timeout: ONE_MINUTE_MS,
},
);
};
});
test.describe("Manual throughput", () => {
let context: TestContainerContext = null!;
let explorer: DataExplorer = null!;
test.beforeAll("Create Test Database & Open container settings", async ({ browser }) => {
context = await createTestSQLContainer();
const page = await browser.newPage();
explorer = await DataExplorer.open(page, TestAccount.SQL);
// Click Scale & Settings and open Scale tab
await explorer.openScaleAndSettings(context);
const scaleTab = explorer.frame.getByTestId("settings-tab-header/ScaleTab");
await scaleTab.click();
});
// test.beforeEach("Open container settings", async ({ page }) => {
// });
if (!process.env.CI) {
test.afterAll("Delete Test Database", async () => {
await context?.dispose();
});
}
test("Update manual throughput", async () => {
await getThroughputInput(explorer, "manual").fill(TEST_MANUAL_THROUGHPUT_RU_2K.toString());
await explorer.commandBarButton(CommandBarButton.Save).click();
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated offer for collection ${context.container.id}`,
{
timeout: 2 * ONE_MINUTE_MS,
@@ -98,32 +130,19 @@ test.describe("Autoscale and Manual throughput", () => {
const softAllowedMaxThroughput = Number(softAllowedMaxThroughputString.replace(/,/g, ""));
// Try to set manual throughput above allowed limit
await getThroughputInput("manual").fill((softAllowedMaxThroughput * 10).toString());
await getThroughputInput(explorer, "manual").fill((softAllowedMaxThroughput * 10).toString());
await expect(explorer.commandBarButton(CommandBarButton.Save)).toBeDisabled();
await expect(getThroughputInputErrorMessage("manual")).toContainText(
await expect(getThroughputInputErrorMessage(explorer, "manual")).toContainText(
"This update isn't possible because it would increase the total throughput",
);
});
// Helper methods
const getThroughputInput = (type: "manual" | "autopilot"): Locator => {
return explorer.frame.getByTestId(`${type}-throughput-input`);
};
const getThroughputInputErrorMessage = (type: "manual" | "autopilot"): Locator => {
return explorer.frame.getByTestId(`${type}-throughput-input-error`);
};
const switchManualToAutoscaleThroughput = async (): Promise<void> => {
const autoscaleRadioButton = explorer.frame.getByText("Autoscale", { exact: true });
await autoscaleRadioButton.click();
await expect(explorer.commandBarButton(CommandBarButton.Save)).toBeEnabled();
await explorer.commandBarButton(CommandBarButton.Save).click();
await expect(explorer.getConsoleMessage()).toContainText(
`Successfully updated offer for collection ${context.container.id}`,
{
timeout: ONE_MINUTE_MS,
},
);
};
});
// Helper methods
const getThroughputInput = (explorer: DataExplorer, type: "manual" | "autopilot"): Locator => {
return explorer.frame.getByTestId(`${type}-throughput-input`);
};
const getThroughputInputErrorMessage = (explorer: DataExplorer, type: "manual" | "autopilot"): Locator => {
return explorer.frame.getByTestId(`${type}-throughput-input-error`);
};

View File

@@ -2,18 +2,14 @@ import { expect, test } from "@playwright/test";
import { CommandBarButton, DataExplorer, ONE_MINUTE_MS, TestAccount } from "../../fx";
import { createTestSQLContainer, TestContainerContext } from "../../testData";
test.describe("Settings under Scale & Settings", () => {
test.describe.serial("Settings under Scale & Settings", () => {
let context: TestContainerContext = null!;
let explorer: DataExplorer = null!;
test.beforeAll("Create Test Database", async () => {
context = await createTestSQLContainer({ includeTestData: true });
});
test.beforeEach("Open Settings tab under Scale & Settings", async ({ page }) => {
test.beforeAll("Create Test Database", async ({ browser }) => {
context = await createTestSQLContainer();
const page = await browser.newPage();
explorer = await DataExplorer.open(page, TestAccount.SQL);
const containerNode = await explorer.waitForContainerNode(context.database.id, context.container.id);
await containerNode.expand();
// Click Scale & Settings and open Scale tab
await explorer.openScaleAndSettings(context);
@@ -21,18 +17,35 @@ test.describe("Settings under Scale & Settings", () => {
await settingsTab.click();
});
test.afterAll("Delete Test Database", async () => {
await context?.dispose();
});
// test.beforeEach("Open container settings", async ({ page }) => {
// explorer = await DataExplorer.open(page, TestAccount.SQL);
// // Click Scale & Settings and open Scale tab
// await explorer.openScaleAndSettings(context);
// const settingsTab = explorer.frame.getByTestId("settings-tab-header/SubSettingsTab");
// await settingsTab.click();
// });
// test.afterEach("Delete Test Database", async () => {
// await context?.dispose();
// });
if (!process.env.CI) {
test.afterAll("Delete Test Database", async () => {
await context?.dispose();
});
}
test("Update TTL to On (no default)", async () => {
const ttlOnNoDefaultRadioButton = explorer.frame.getByRole("radio", { name: "ttl-on-no-default-option" });
await ttlOnNoDefaultRadioButton.click();
await explorer.commandBarButton(CommandBarButton.Save).click();
await expect(explorer.getConsoleMessage()).toContainText(`Successfully updated container ${context.container.id}`, {
timeout: ONE_MINUTE_MS,
});
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated container ${context.container.id}`,
{
timeout: ONE_MINUTE_MS,
},
);
});
test("Update TTL to On (with user entry)", async () => {
@@ -44,27 +57,11 @@ test.describe("Settings under Scale & Settings", () => {
await ttlInput.fill("30000");
await explorer.commandBarButton(CommandBarButton.Save).click();
await expect(explorer.getConsoleMessage()).toContainText(`Successfully updated container ${context.container.id}`, {
timeout: ONE_MINUTE_MS,
});
});
test("Update TTL to Off", async () => {
// By default TTL is set to off so we need to first set it to On
const ttlOnNoDefaultRadioButton = explorer.frame.getByRole("radio", { name: "ttl-on-no-default-option" });
await ttlOnNoDefaultRadioButton.click();
await explorer.commandBarButton(CommandBarButton.Save).click();
await expect(explorer.getConsoleMessage()).toContainText(`Successfully updated container ${context.container.id}`, {
timeout: ONE_MINUTE_MS,
});
// Set it to Off
const ttlOffRadioButton = explorer.frame.getByRole("radio", { name: "ttl-off-option" });
await ttlOffRadioButton.click();
await explorer.commandBarButton(CommandBarButton.Save).click();
await expect(explorer.getConsoleMessage()).toContainText(`Successfully updated container ${context.container.id}`, {
timeout: ONE_MINUTE_MS,
});
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated container ${context.container.id}`,
{
timeout: ONE_MINUTE_MS,
},
);
});
});

View File

@@ -7,7 +7,8 @@ test("Tables CRUD", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.Tables);
await explorer.globalCommandButton("New Table").click();
const newTableButton = explorer.frame.getByTestId("GlobalCommands").getByRole("button", { name: "New Table" });
await newTableButton.click();
await explorer.whilePanelOpen(
"New Table",
async (panel, okButton) => {