Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -472,28 +472,40 @@
</ng-template>
<nz-collapse-panel [nzHeader]="systemHeaderTpl">
<div class="system-panel-inner">
<div class="package-header-row">
<div class="package-column-label">Package</div>
<div class="package-column-label">Version</div>
<div
*ngIf="systemPackagesLoading"
class="system-loading">
<span
nz-icon
nzType="loading"
nzTheme="outline"></span>
Fetching system packages…
</div>

<div
*ngFor="let pkg of systemPackages"
class="package-row system-row">
<div class="system-package-inputs">
<input
nz-input
class="system-input"
[disabled]="true"
[ngModel]="pkg.name" />
<ng-container *ngIf="!systemPackagesLoading">
<div class="package-header-row">
<div class="package-column-label">Package</div>
<div class="package-column-label">Version</div>
</div>

<input
nz-input
class="system-input"
[disabled]="true"
[ngModel]="pkg.version" />
<div
*ngFor="let pkg of systemPackages"
class="package-row system-row">
<div class="system-package-inputs">
<input
nz-input
class="system-input"
[disabled]="true"
[ngModel]="pkg.name" />

<input
nz-input
class="system-input"
[disabled]="true"
[ngModel]="pkg.version" />
</div>
</div>
</div>
</ng-container>
</div>
</nz-collapse-panel>
</nz-collapse>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ import { NzModalModule, NzModalService } from "ng-zorro-antd/modal";
import { ComputingUnitStatusService } from "../../../common/service/computing-unit/computing-unit-status/computing-unit-status.service";
import { MockComputingUnitStatusService } from "../../../common/service/computing-unit/computing-unit-status/mock-computing-unit-status.service";
import { commonTestProviders } from "../../../common/testing/test-utils";
import { Subject, of, throwError } from "rxjs";
import {
PackageResponse,
PvePackageResponse,
WorkflowPveService,
} from "../../service/virtual-environment/virtual-environment.service";
import { DashboardWorkflowComputingUnit } from "../../../common/type/workflow-computing-unit";

describe("PowerButtonComponent", () => {
let component: ComputingUnitSelectionComponent;
Expand Down Expand Up @@ -82,4 +89,69 @@ describe("PowerButtonComponent", () => {
it("should create", () => {
expect(component).toBeTruthy();
});

describe("getPVEs() systemPackagesLoading flag", () => {
const selectedUnit = {
computingUnit: { cuid: 123 },
} as unknown as DashboardWorkflowComputingUnit;

let pveService: WorkflowPveService;

beforeEach(() => {
pveService = TestBed.inject(WorkflowPveService);
component.selectedComputingUnit = selectedUnit;
component.systemPackagesLoading = false;
component.pves = [];
component.systemPackages = [];
});

it("sets systemPackagesLoading=true immediately and keeps it true while /pve/system is in flight", () => {
const pvesResp: PvePackageResponse[] = [{ pveName: "env-a", userPackages: ["numpy==1.26.0"] }];
const systemSubject = new Subject<PackageResponse>();
vi.spyOn(pveService, "fetchPVEs").mockReturnValue(of(pvesResp));
vi.spyOn(pveService, "getSystemPackages").mockReturnValue(systemSubject.asObservable());

component.getPVEs();

expect(component.systemPackagesLoading).toBe(true);
expect(component.pves.length).toBe(1);
expect(component.pves[0].name).toBe("env-a");

systemSubject.next({ system: ["pandas==2.0.0"] });
systemSubject.complete();

expect(component.systemPackagesLoading).toBe(false);
expect(component.systemPackages).toEqual([{ name: "pandas", version: "2.0.0" }]);
});

it("clears systemPackagesLoading when /pve/system errors", () => {
vi.spyOn(pveService, "fetchPVEs").mockReturnValue(of([] as PvePackageResponse[]));
vi.spyOn(pveService, "getSystemPackages").mockReturnValue(throwError(() => new Error("system fetch failed")));
vi.spyOn(console, "error").mockImplementation(() => {});

component.getPVEs();

expect(component.systemPackagesLoading).toBe(false);
expect(component.systemPackages).toEqual([]);
});

it("clears systemPackagesLoading and resets state when /pve/pves errors", () => {
const fetchPvesSpy = vi
.spyOn(pveService, "fetchPVEs")
.mockReturnValue(throwError(() => new Error("pves fetch failed")));
const getSystemSpy = vi.spyOn(pveService, "getSystemPackages");
vi.spyOn(console, "error").mockImplementation(() => {});

component.pves = [{ name: "stale" }] as any;
component.systemPackages = [{ name: "stale", version: "0.0.0" }];

component.getPVEs();

expect(fetchPvesSpy).toHaveBeenCalledWith(123);
expect(getSystemSpy).not.toHaveBeenCalled();
expect(component.systemPackagesLoading).toBe(false);
expect(component.pves).toEqual([]);
expect(component.systemPackages).toEqual([]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ export class ComputingUnitSelectionComponent implements OnInit {
// variables for creating a virtual environment
pves: PveDraft[] = [];
systemPackages: { name: string; version: string }[] = [];
// True while a /pve/system response is in flight. The server resolves
// the full pinned set with a `pip freeze` against a throwaway venv,
// which can take 30–60s on the first request after a server restart.
systemPackagesLoading = false;
pveModalVisible = false;

// current workflow's Id, will change with wid in the workflowActionService.metadata
Expand Down Expand Up @@ -782,6 +786,7 @@ export class ComputingUnitSelectionComponent implements OnInit {

getPVEs(): void {
const cuId = this.selectedComputingUnit!.computingUnit.cuid;
this.systemPackagesLoading = true;

this.workflowPveService
.fetchPVEs(cuId)
Expand Down Expand Up @@ -812,17 +817,20 @@ export class ComputingUnitSelectionComponent implements OnInit {
version: (version ?? "").trim(),
};
});
this.systemPackagesLoading = false;
},
error: (err: unknown) => {
console.error("Failed to fetch system packages:", err);
this.systemPackages = [];
this.systemPackagesLoading = false;
},
});
},
error: (err: unknown) => {
console.error("Failed to fetch PVEs:", err);
this.pves = [];
this.systemPackages = [];
this.systemPackagesLoading = false;
},
});
}
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,15 @@ hr {
gap: 6px;
}

.system-loading {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 4px;
color: rgba(0, 0, 0, 0.55);
font-style: italic;
}

.package-header-row,
.package-inputs {
display: grid;
Expand Down
Loading