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
162 changes: 162 additions & 0 deletions apps/infra/src/components/organism/OsUpdate/OsUpdate.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* SPDX-FileCopyrightText: (C) 2025 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/

import { infra } from "@orch-ui/apis";
import {
assignedWorkloadHostTwo as mockHost,
osUpdatePolicyLatest,
osUpdatePolicyTarget,
} from "@orch-ui/utils";
import OsUpdate from "./OsUpdate";
import OsUpdatePom from "./OsUpdate.pom";

const pom = new OsUpdatePom();

// Mock host with OS update available
const mockHostWithUpdate: infra.HostResourceRead = {
...mockHost,
instance: {
...mockHost.instance!,
osUpdateAvailable: "Ubuntu 22.04 LTS",
updatePolicy: osUpdatePolicyTarget,
},
};

// Mock host without OS update policy assigned
const mockHostNoPolicy: infra.HostResourceRead = {
...mockHost,
instance: {
...mockHost.instance!,
osUpdateAvailable: "Ubuntu 22.04 LTS",
updatePolicy: undefined,
},
};

// Mock host without instance
const mockHostNoInstance: infra.HostResourceRead = {
...mockHost,
instance: undefined,
};

describe("<OsUpdate/>", () => {
describe("Basic Rendering", () => {
it("should render component with host data", () => {
pom.interceptApis([pom.api.getOsUpdatePolicies]);
cy.mount(<OsUpdate host={mockHostWithUpdate} />);
pom.waitForApis();
pom.root.should("exist");
});

it("should display update availability information", () => {
pom.interceptApis([pom.api.getOsUpdatePolicies]);
cy.mount(<OsUpdate host={mockHostWithUpdate} />);
pom.waitForApis();

cy.contains("Updates").should("exist");
pom.updatesAvailable.should("contain", "Ubuntu 22.04 LTS");
});

it("should display assigned OS update policy", () => {
pom.interceptApis([pom.api.getOsUpdatePolicies]);
cy.mount(<OsUpdate host={mockHostWithUpdate} />);
pom.waitForApis();

cy.contains("Assigned OS Update Policy").should("exist");
pom.assignedPolicy.should("contain", osUpdatePolicyTarget.name);
});

it("should show dash when no assigned policy", () => {
pom.interceptApis([pom.api.getOsUpdatePolicies]);
cy.mount(<OsUpdate host={mockHostNoPolicy} />);
pom.waitForApis();

pom.assignedPolicy.should("contain", "-");
});

it("should handle host without instance", () => {
pom.interceptApis([pom.api.getOsUpdatePolicies]);
cy.mount(<OsUpdate host={mockHostNoInstance} />);
pom.waitForApis();

pom.root.should("exist");
pom.updatesAvailable.should("contain", "-");
pom.assignedPolicy.should("contain", "-");
});
});

describe("OS Update Policy Dropdown", () => {
it("should load and display OS update policies", () => {
pom.interceptApis([pom.api.getOsUpdatePolicies]);
cy.mount(<OsUpdate host={mockHostWithUpdate} />);
pom.waitForApis();

pom.dropdown.selectDropdownValueByLabel(
pom.root,
"osUpdatePolicy",
osUpdatePolicyTarget.name,
);

// Should show available policies
cy.contains(osUpdatePolicyTarget.name).should("exist");
cy.contains(osUpdatePolicyLatest.name).should("exist");
});

it("should show loading state while fetching policies", () => {
// Don't wait for API to complete to test loading state
pom.interceptApis([pom.api.getOsUpdatePolicies]);
cy.mount(<OsUpdate host={mockHostWithUpdate} />);

pom.policyDropdown.should("contain", "Loading policies...");
});

it("should show no policies message when empty response", () => {
pom.interceptApis([pom.api.getOsUpdatePoliciesEmpty]);
cy.mount(<OsUpdate host={mockHostWithUpdate} />);
pom.waitForApis();

pom.policyDropdown.should("contain", "No OS Update Policies Available");
pom.policyDropdown.should("have.class", "spark-dropdown-is-disabled");
});

it("should handle API error gracefully", () => {
pom.interceptApis([pom.api.getOsUpdatePoliciesError500]);
cy.mount(<OsUpdate host={mockHostWithUpdate} />);
pom.waitForApis();

// Component should still render but dropdown should show error state
pom.root.should("exist");
pom.policyDropdown.should("have.class", "spark-dropdown-is-disabled");
});

it("should allow policy selection", () => {
pom.interceptApis([pom.api.getOsUpdatePolicies]);
cy.mount(<OsUpdate host={mockHostWithUpdate} />);
pom.waitForApis();

// Select a policy
pom.selectPolicy(osUpdatePolicyTarget.name);

// Apply button should become enabled
pom.el.applyPolicyBtn.should("not.have.class", "spark-button-disabled");
});
});

describe("Apply Policy Functionality", () => {
beforeEach(() => {
pom.interceptApis([pom.api.getOsUpdatePolicies]);
cy.mount(<OsUpdate host={mockHostWithUpdate} />);
pom.waitForApis();
});

it("should disable apply button when no policy selected", () => {
pom.applyButton.should("have.class", "spark-button-disabled");
});

it("should enable apply button when policy is selected", () => {
pom.selectPolicy(osUpdatePolicyTarget.name);
pom.applyButton.should("not.have.class", "spark-button-disabled");
});
});
});
106 changes: 106 additions & 0 deletions apps/infra/src/components/organism/OsUpdate/OsUpdate.pom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* SPDX-FileCopyrightText: (C) 2025 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/

import { infra } from "@orch-ui/apis";
import { SiDropdown } from "@orch-ui/poms";
import { CyApiDetails, CyPom, defaultActiveProject } from "@orch-ui/tests";
import { OsUpdatePolicyStore } from "@orch-ui/utils";

const dataCySelectors = [
"osProfiles",
"desiredOsProfiles",
"osUpdatePolicy",
"applyPolicyBtn",
] as const;
type Selectors = (typeof dataCySelectors)[number];

type ApiAliases =
| "getOsUpdatePolicies"
| "getOsUpdatePoliciesEmpty"
| "getOsUpdatePoliciesError500"
| "patchInstanceSuccess"
| "patchInstanceError";

const osUpdatePolicyStore = new OsUpdatePolicyStore();

const osUpdatePoliciesRoute = `**/v1/projects/${defaultActiveProject.name}/os-update-policies*`;
const patchInstanceRoute = (resourceId: string) =>
`**/v1/projects/${defaultActiveProject.name}/compute/instances/${resourceId}`;

const endpoints: CyApiDetails<
ApiAliases,
infra.OsUpdatePolicyListOsUpdatePolicyApiResponse | infra.InstanceResourceRead
> = {
getOsUpdatePolicies: {
route: osUpdatePoliciesRoute,
response: {
osUpdatePolicies: osUpdatePolicyStore.list(),
totalElements: osUpdatePolicyStore.resources.length,
hasNext: false,
},
},
getOsUpdatePoliciesEmpty: {
route: osUpdatePoliciesRoute,
response: {
osUpdatePolicies: [],
totalElements: 0,
hasNext: false,
},
},
getOsUpdatePoliciesError500: {
route: osUpdatePoliciesRoute,
statusCode: 500,
response: {},
},
patchInstanceSuccess: {
method: "PATCH",
route: patchInstanceRoute("*"),
response: {} as infra.InstanceResourceRead,
},
patchInstanceError: {
method: "PATCH",
route: patchInstanceRoute("*"),
statusCode: 500,
response: {},
},
};

class OsUpdatePom extends CyPom<Selectors, ApiAliases> {
public dropdown = new SiDropdown("osUpdatePolicy");

constructor(public rootCy: string = "osUpdate") {
super(rootCy, [...dataCySelectors], endpoints);
}

get updatesAvailable() {
return this.el.desiredOsProfiles.first();
}

get assignedPolicy() {
return this.el.desiredOsProfiles.last();
}

get policyDropdown() {
return this.el.osUpdatePolicy;
}

get applyButton() {
return this.el.applyPolicyBtn;
}

selectPolicy(policyName: string) {
this.dropdown.selectDropdownValueByLabel(
this.root,
"osUpdatePolicy",
policyName,
);
}

clickApply() {
this.applyButton.click();
}
}

export default OsUpdatePom;
29 changes: 29 additions & 0 deletions apps/infra/src/components/organism/OsUpdate/OsUpdate.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* SPDX-FileCopyrightText: (C) 2025 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/

.os-update {
padding: 1rem;
display: flex;
flex-direction: column;
gap: 1rem;

&__header {
margin-bottom: 0.5rem;
}

&__policy-selection {
display: flex;
flex-direction: column;
gap: 0.5rem;
}

&__actions {
margin-left: 1rem;
}

&__apply-button {
height: 100% !important;
}
}
Loading
Loading