Skip to content

Commit ce9cdf4

Browse files
authored
Merge pull request #531 from DanielMicrosoft/35679842-refactoring-editorclient-config
[Refactor]: WSEditorClientConfig to function-based components.
2 parents 98858ad + 9878dc5 commit ce9cdf4

File tree

7 files changed

+775
-1030
lines changed

7 files changed

+775
-1030
lines changed

src/web/src/__tests__/api/workspaceApi.test.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,12 @@ describe("Workspace API", () => {
9797

9898
expect(result).toEqual({
9999
name: "test-workspace-1",
100-
plane: "azure-cli",
100+
plane: "data-planetest-workspace-1",
101+
resourceProvider: "test-workspace-1",
101102
folder: "/workspaces/test-workspace-1",
102-
commandTree: {},
103+
commandTree: {
104+
names: ["aaz"],
105+
},
103106
});
104107
});
105108
});

src/web/src/__tests__/components/WSEditorClientConfig.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ describe("WSEditorClientConfigDialog", () => {
5454
beforeEach(() => {
5555
vi.clearAllMocks();
5656
(specsApi.getPlanes as any).mockResolvedValue(mockPlanes);
57+
(specsApi.getSwaggerModules as any).mockResolvedValue(["storage", "compute"]);
5758
(specsApi.getResourceProviders as any).mockResolvedValue(mockResourceProviders);
5859
(specsApi.getProviderResources as any).mockResolvedValue(mockProviderResources);
5960
(errorHandlerApi.getErrorMessage as any).mockReturnValue("Mock error message");
@@ -255,7 +256,7 @@ describe("WSEditorClientConfigDialog", () => {
255256
await user.click(updateButton);
256257

257258
await waitFor(() => {
258-
expect(screen.getByText("Plane is required.")).toBeInTheDocument();
259+
expect(screen.getByText("Module is required.")).toBeInTheDocument();
259260
});
260261
});
261262
});

src/web/src/__tests__/integration/WSEditorClientConfig.integration.test.tsx

Lines changed: 1 addition & 245 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach, beforeAll, afterEach, afterAll }
22
import { screen, waitFor } from "@testing-library/react";
33
import userEvent from "@testing-library/user-event";
44
import { setupServer } from "msw/node";
5-
import { http, HttpResponse } from "msw";
65
import { render } from "../test-utils";
76
import WSEditorClientConfigDialog from "../../views/workspace/components/WSEditor/WSEditorClientConfig";
87

@@ -71,44 +70,6 @@ describe("WSEditorClientConfigDialog - Integration", () => {
7170
expect(screen.queryByText("Cancel")).not.toBeInTheDocument();
7271
});
7372

74-
it.skip("should cascade load planes → modules → providers → versions", async () => {
75-
// @NOTE: skipping this workflow for now, there is servere delay in loading, will revisit once loading states are improved.
76-
render(<WSEditorClientConfigDialog workspaceUrl={mockWorkspaceUrl} open={true} onClose={mockOnClose} />);
77-
78-
// Switch to the resource property tab
79-
const resourcePropertyTab = screen.getByRole("tab", { name: /By resource property/i });
80-
await userEvent.click(resourcePropertyTab);
81-
82-
// --- MODULES ---
83-
const moduleInput = screen.getByRole("combobox", { name: /Module/i });
84-
await userEvent.click(moduleInput);
85-
86-
// Wait for the popper to render an option (it will display "storage", not "Microsoft.Storage")
87-
const storageOption = await screen.findByRole("option", { name: /storage/i });
88-
await userEvent.click(storageOption);
89-
90-
// --- PROVIDERS ---
91-
const providerInput = screen.getByRole("combobox", { name: /Resource Provider/i });
92-
await userEvent.click(providerInput);
93-
94-
// Providers are stripped of common prefix, so if API returned ["Microsoft.Storage"],
95-
// and `commonPrefix = "Microsoft."`, you’ll actually see "Storage" in the DOM
96-
const rpOption = await screen.findByRole("option", { name: /Storage/i });
97-
await userEvent.click(rpOption);
98-
99-
// --- VERSIONS ---
100-
const versionInput = screen.getByRole("combobox", { name: /API Version/i });
101-
await userEvent.click(versionInput);
102-
103-
const versionOption = await screen.findByRole("option", { name: /2021-04-01/i });
104-
await userEvent.click(versionOption);
105-
106-
// Final assertions (all cascades complete)
107-
expect(moduleInput).toHaveValue("storage");
108-
expect(providerInput).toHaveValue("Storage");
109-
expect(versionInput).toHaveValue("2021-04-01");
110-
});
111-
11273
it("should handle API errors gracefully during cascade loading", async () => {
11374
const user = userEvent.setup();
11475
render(
@@ -133,7 +94,7 @@ describe("WSEditorClientConfigDialog - Integration", () => {
13394
});
13495

13596
describe("Complete User Workflows", () => {
136-
it("should complete template config setup end-to-end", async () => {
97+
it("should handle user inputs for relevant fields", async () => {
13798
const user = userEvent.setup();
13899
render(<WSEditorClientConfigDialog workspaceUrl={mockWorkspaceUrl} open={true} onClose={mockOnClose} />);
139100

@@ -163,210 +124,5 @@ describe("WSEditorClientConfigDialog - Integration", () => {
163124
expect(mockOnClose).toHaveBeenCalledWith(true);
164125
});
165126
});
166-
167-
it.skip("should complete resource config setup end-to-end", async () => {
168-
// @NOTE: revisit once workflows and loading states are improved
169-
server.use(
170-
http.get(`*/workspaces${mockWorkspaceUrl}/client-config`, () => {
171-
return new HttpResponse(null, { status: 404 });
172-
}),
173-
http.put(`*/workspaces${mockWorkspaceUrl}/client-config`, async ({ request }) => {
174-
const body = await request.json();
175-
expect(body).toEqual({
176-
templates: undefined,
177-
cloudMetadata: undefined,
178-
resource: {
179-
plane: "azure-cli",
180-
module: "storage",
181-
version: "2021-04-01",
182-
id: "storageAccounts",
183-
subresource: "properties.primaryEndpoints.blob",
184-
},
185-
auth: {
186-
aad: {
187-
scopes: ["https://storage.azure.com/.default"],
188-
},
189-
},
190-
});
191-
return HttpResponse.json({ success: true });
192-
}),
193-
);
194-
195-
const user = userEvent.setup();
196-
render(<WSEditorClientConfigDialog workspaceUrl={mockWorkspaceUrl} open={true} onClose={mockOnClose} />);
197-
198-
await waitFor(() => {
199-
expect(screen.getByText("By resource property")).toBeInTheDocument();
200-
});
201-
202-
const resourceTab = screen.getByText("By resource property");
203-
await user.click(resourceTab);
204-
205-
await waitFor(() => {
206-
expect(screen.getByRole("combobox", { name: /module/i })).toBeInTheDocument();
207-
});
208-
209-
const moduleInput = screen.getByRole("combobox", { name: /module/i });
210-
await user.click(moduleInput);
211-
await waitFor(() => {
212-
expect(screen.getByText("storage")).toBeInTheDocument();
213-
});
214-
await user.click(screen.getByText("storage"));
215-
216-
await waitFor(() => {
217-
const rpInput = screen.getByLabelText("Resource Provider");
218-
expect(rpInput).toBeInTheDocument();
219-
});
220-
const rpInput = screen.getByLabelText("Resource Provider");
221-
await user.click(rpInput);
222-
await waitFor(() => {
223-
expect(screen.getByText("Microsoft.Storage")).toBeInTheDocument();
224-
});
225-
await user.click(screen.getByText("Microsoft.Storage"));
226-
227-
await waitFor(() => {
228-
const versionInput = screen.getByLabelText("API Version");
229-
expect(versionInput).toBeInTheDocument();
230-
});
231-
const versionInput = screen.getByLabelText("API Version");
232-
await user.click(versionInput);
233-
await waitFor(() => {
234-
expect(screen.getByText("2021-04-01")).toBeInTheDocument();
235-
});
236-
await user.click(screen.getByText("2021-04-01"));
237-
238-
await waitFor(() => {
239-
const resourceIdInput = screen.getByLabelText("Resource ID");
240-
expect(resourceIdInput).toBeInTheDocument();
241-
});
242-
const resourceIdInput = screen.getByLabelText("Resource ID");
243-
await user.click(resourceIdInput);
244-
await waitFor(() => {
245-
expect(screen.getByText("storageAccounts")).toBeInTheDocument();
246-
});
247-
await user.click(screen.getByText("storageAccounts"));
248-
249-
const subresourceInput = screen.getByLabelText("Endpoint Property Index");
250-
await user.type(subresourceInput, "properties.primaryEndpoints.blob");
251-
252-
const aadScopeInput = screen.getByPlaceholderText(/Input Microsoft Entra\(AAD\) auth Scope/);
253-
await user.clear(aadScopeInput);
254-
await user.type(aadScopeInput, "https://storage.azure.com/.default");
255-
256-
const updateButton = screen.getByText("Update");
257-
await user.click(updateButton);
258-
259-
await waitFor(() => {
260-
expect(mockOnClose).toHaveBeenCalledWith(true);
261-
});
262-
});
263-
264-
it.skip("should handle network errors during submission", async () => {
265-
// @NOTE: revisit once workflows and loading states are improved
266-
const user = userEvent.setup();
267-
render(
268-
<WSEditorClientConfigDialog
269-
workspaceUrl={`${mockWorkspaceUrl}?simulate404=false`}
270-
open={true}
271-
onClose={mockOnClose}
272-
/>,
273-
);
274-
275-
await waitFor(() => {
276-
expect(document.querySelector("#AzureCloud")).toBeInTheDocument();
277-
});
278-
279-
const azureCloudInput = document.querySelector("#AzureCloud") as HTMLElement;
280-
await user.type(azureCloudInput, "https://{vaultName}.vault.azure.net");
281-
282-
const aadScopeInput = screen.getByPlaceholderText(/Input Microsoft Entra\(AAD\) auth Scope/);
283-
await user.type(aadScopeInput, "https://management.azure.com/.default");
284-
285-
const updateButton = screen.getByText("Update");
286-
await user.click(updateButton);
287-
288-
await waitFor(() => {
289-
expect(screen.getByText(/ResponseError:/)).toBeInTheDocument();
290-
});
291-
292-
expect(mockOnClose).not.toHaveBeenCalled();
293-
});
294-
295-
it.skip("should handle error recovery - fix validation error and retry", async () => {
296-
// @NOTE: revisit once workflows and loading states are improved
297-
server.use(
298-
http.get(`*/workspaces${mockWorkspaceUrl}/client-config`, () => {
299-
return new HttpResponse(null, { status: 404 });
300-
}),
301-
http.put(`*/workspaces${mockWorkspaceUrl}/client-config`, () => {
302-
return HttpResponse.json({ success: true });
303-
}),
304-
);
305-
306-
const user = userEvent.setup();
307-
render(<WSEditorClientConfigDialog workspaceUrl={mockWorkspaceUrl} open={true} onClose={mockOnClose} />);
308-
309-
await waitFor(() => {
310-
expect(screen.getByText("Update")).toBeInTheDocument();
311-
});
312-
313-
const updateButton = screen.getByText("Update");
314-
await user.click(updateButton);
315-
316-
await waitFor(() => {
317-
expect(screen.getByText("Azure Cloud Endpoint Template is required.")).toBeInTheDocument();
318-
});
319-
320-
const azureCloudInput = document.querySelector("#AzureCloud") as HTMLElement;
321-
await user.type(azureCloudInput, "https://{vaultName}.vault.azure.net");
322-
323-
const aadScopeInput = screen.getByPlaceholderText(/Input Microsoft Entra\(AAD\) auth Scope/);
324-
await user.type(aadScopeInput, "https://management.azure.com/.default");
325-
326-
await user.click(updateButton);
327-
328-
await waitFor(() => {
329-
expect(mockOnClose).toHaveBeenCalledWith(true);
330-
});
331-
});
332-
});
333-
334-
describe("Real-time Validation", () => {
335-
it.skip("should validate template URLs in real-time", async () => {
336-
// @NOTE: revisit once error/loading states are cleared up
337-
const user = userEvent.setup();
338-
render(<WSEditorClientConfigDialog workspaceUrl={mockWorkspaceUrl} open={true} onClose={mockOnClose} />);
339-
340-
await waitFor(() => {
341-
expect(document.querySelector("#AzureCloud")).toBeInTheDocument();
342-
});
343-
344-
const azureCloudInput = document.querySelector("#AzureCloud") as HTMLElement;
345-
await user.type(azureCloudInput, "invalid-url");
346-
347-
const updateButton = screen.getByText("Update");
348-
await user.click(updateButton);
349-
350-
await waitFor(
351-
() => {
352-
expect(screen.queryByText("Azure Cloud Endpoint Template is invalid.")).not.toBeInTheDocument();
353-
},
354-
{ timeout: 2000 },
355-
);
356-
await user.clear(azureCloudInput);
357-
await user.type(azureCloudInput, "https://{vaultName}.vault.azure.net");
358-
359-
// Add AAD scope
360-
const aadScopeInput = screen.getByPlaceholderText(/Input Microsoft Entra\(AAD\) auth Scope/);
361-
await user.type(aadScopeInput, "https://management.azure.com/.default");
362-
363-
// Should be able to submit now
364-
await user.click(updateButton);
365-
366-
// Error should clear and submission should proceed
367-
await waitFor(() => {
368-
expect(screen.queryByText("Azure Cloud Endpoint Template is invalid.")).not.toBeInTheDocument();
369-
});
370-
});
371127
});
372128
});

0 commit comments

Comments
 (0)