mengw15 commented on code in PR #8102:
URL: https://github.com/apache/texera/pull/8102#discussion_r3886276725
##########
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.spec.ts:
##########
@@ -1586,4 +1588,298 @@ describe("PowerButtonComponent", () => {
}
});
});
+
+ // ──────────────────────────────────────────────────────────────────────────
+ // Rows of the dropdown menu, driven through the markup ng-zorro stamps into
+ // the CDK overlay rather than by calling the handlers on the instance.
+ // ──────────────────────────────────────────────────────────────────────────
+ describe("dropdown menu rows (rendered)", () => {
+ /**
+ * ng-zorro gates the dropdown's overlay behind an `auditTime(150)` that it
+ * schedules outside the Angular zone, so neither `fakeAsync`'s `tick()`
nor
+ * Vitest's fake timers reach it — zone.js captured the native timer before
+ * either could patch it. Poll for the rows instead of sleeping a fixed
+ * amount, so a slow runner costs extra iterations rather than a failure.
+ */
+ async function openDropdown(): Promise<HTMLElement[]> {
+
fixture.debugElement.query(By.css(".computing-units-dropdown-button")).nativeElement.click();
+ for (let i = 0; i < 80 &&
document.querySelectorAll(".computing-unit-option").length === 0; i++) {
+ await new Promise(resolve => setTimeout(resolve, 25));
+ fixture.detectChanges();
+ }
+ fixture.detectChanges();
+ const rows =
Array.from(document.querySelectorAll<HTMLElement>(".computing-unit-option"));
+ expect(rows.length).toBeGreaterThan(0);
+ return rows;
+ }
+
+ function click(element: Element | null | undefined): void {
+ expect(element).toBeTruthy();
+ element!.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ fixture.detectChanges();
+ }
+
+ let selectSpy: ReturnType<typeof vi.spyOn>;
+
+ beforeEach(() => {
+ component.workflowId = 5;
+ component.allComputingUnits = [
+ makeComputingUnit({ cuid: 1, name: "Alpha" }),
+ makeComputingUnit({ cuid: 2, name: "Beta" }),
+ ];
+ selectSpy = vi
+ .spyOn(TestBed.inject(ComputingUnitStatusService),
"selectComputingUnit")
+ .mockImplementation(() => {});
+ fixture.detectChanges();
+ });
+
+ afterEach(() => {
+ // Clear the overlay contents rather than the container itself, which
CDK caches.
+ document.querySelectorAll(".cdk-overlay-container").forEach(el =>
(el.innerHTML = ""));
+ vi.restoreAllMocks();
+ });
+
+ it("selects the unit whose row is clicked", async () => {
+ const rows = await openDropdown();
+
+ click(rows[1]);
+
+ expect(component.selectedComputingUnit?.computingUnit.cuid).toBe(2);
+ expect(selectSpy).toHaveBeenCalledWith(5, 2);
+ });
+
+ it("commits a rename when the inline editor loses focus", async () => {
+ const renameSpy = vi
+ .spyOn(TestBed.inject(WorkflowComputingUnitManagingService),
"renameComputingUnit")
+ .mockReturnValue(of({} as any));
+ component.editingNameOfUnit = 1;
+ component.editingUnitName = "Alpha";
+ const rows = await openDropdown();
+
+ const editor =
rows[0].querySelector<HTMLInputElement>(".unit-name-edit-input");
+ expect(editor?.value).toBe("Alpha");
+ editor!.value = "Renamed";
+ editor!.dispatchEvent(new FocusEvent("focusout", { bubbles: true }));
+ fixture.detectChanges();
+
+ expect(renameSpy).toHaveBeenCalledWith(1, "Renamed");
+ });
+
+ it("commits a rename when Enter is pressed in the inline editor", async ()
=> {
+ const renameSpy = vi
+ .spyOn(TestBed.inject(WorkflowComputingUnitManagingService),
"renameComputingUnit")
+ .mockReturnValue(of({} as any));
+ component.editingNameOfUnit = 1;
+ component.editingUnitName = "Alpha";
+ const rows = await openDropdown();
+
+ const editor =
rows[0].querySelector<HTMLInputElement>(".unit-name-edit-input");
+ editor!.value = "Entered";
+ editor!.dispatchEvent(new KeyboardEvent("keyup", { key: "Enter",
bubbles: true }));
+ fixture.detectChanges();
+
+ expect(renameSpy).toHaveBeenCalledWith(1, "Entered");
+ });
+
+ it("abandons the rename when Escape is pressed in the inline editor",
async () => {
+ const renameSpy =
vi.spyOn(TestBed.inject(WorkflowComputingUnitManagingService),
"renameComputingUnit");
+ component.editingNameOfUnit = 1;
+ component.editingUnitName = "Alpha";
+ const rows = await openDropdown();
+
+ rows[0]
+ .querySelector<HTMLInputElement>(".unit-name-edit-input")!
+ .dispatchEvent(new KeyboardEvent("keyup", { key: "Escape", bubbles:
true }));
+ fixture.detectChanges();
+
+ expect(component.editingNameOfUnit).toBeNull();
+ expect(renameSpy).not.toHaveBeenCalled();
+ });
+
+ it("swallows a click inside the inline editor instead of selecting the
row", async () => {
+ component.editingNameOfUnit = 1;
+ component.editingUnitName = "Alpha";
+ const rows = await openDropdown();
+
+ click(rows[0].querySelector(".unit-name-edit-input"));
+
+ expect(selectSpy).not.toHaveBeenCalled();
+ expect(component.editingNameOfUnit).toBe(1);
+ });
+ it("opens the inline editor from the pencil without selecting the row",
async () => {
+ const rows = await openDropdown();
+
+ click(rows[0].querySelector(".anticon-edit"));
+
+ expect(component.editingNameOfUnit).toBe(1);
+ expect(component.editingUnitName).toBe("Alpha");
+ expect(selectSpy).not.toHaveBeenCalled();
+ });
+
+ it("opens the python-environment modal from the plus icon without
selecting the row", async () => {
+ const rows = await openDropdown();
+
+ click(rows[1].querySelector(".anticon-plus"));
+
+ expect(component.pveModalVisible).toBe(true);
+ expect(component.selectedComputingUnit?.computingUnit.cuid).toBe(2);
+ expect(selectSpy).not.toHaveBeenCalled();
+ });
+
+ it("opens share access from the share icon without selecting the row",
async () => {
+ TestBed.inject(GuiConfigService).env.sharingComputingUnitEnabled = true;
+ const shareSpy = vi
+ .spyOn(TestBed.inject(ComputingUnitActionsService),
"openShareAccessModal")
+ .mockImplementation(() => {});
+ fixture.detectChanges();
+ const rows = await openDropdown();
+
+ click(rows[0].querySelector(".anticon-share-alt"));
+
+ expect(shareSpy).toHaveBeenCalledWith(1, true);
+ expect(selectSpy).not.toHaveBeenCalled();
+ });
Review Comment:
Restored in the block's afterEach. Worth noting the leak is not reachable
today: this spec re-runs TestBed.configureTestingModule in beforeEach and
MockGuiConfigService keeps its config in an instance field, so each test gets
its own. Restoring anyway so the block stays correct if these tests ever come
to share one config instance.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]