diff --git a/cypress/constants/types.ts b/cypress/constants/types.ts index 26d65ece9..9d3696dde 100644 --- a/cypress/constants/types.ts +++ b/cypress/constants/types.ts @@ -27,3 +27,4 @@ export const HCI = { }; export const NETWORK_ATTACHMENT = 'k8s.cni.cncf.io.networkattachmentdefinition'; +export const PVC = 'persistentvolumeclaim'; diff --git a/cypress/cypress.env.json.example b/cypress/cypress.env.json.example index 0023316bc..e91df1e1b 100644 --- a/cypress/cypress.env.json.example +++ b/cypress/cypress.env.json.example @@ -14,9 +14,12 @@ } ] }, - "image": - { + "largeImage": { "name": "openSUSE-Leap-15.3-3-DVD-x86_64-Build38.1-Media.iso", "url": "https://mirrors.bfsu.edu.cn/opensuse/distribution/leap/15.3/iso/openSUSE-Leap-15.3-3-DVD-x86_64-Build38.1-Media.iso" - } + }, + "image": { + "name": "cirros-0.5.2-aarch64-disk.img", + "url": "https://github.com/cirros-dev/cirros/releases/download/0.5.2/cirros-0.5.2-aarch64-disk.img" + }, } \ No newline at end of file diff --git a/cypress/cypress.json b/cypress/cypress.json index 6e7bb0f7d..a29ffc650 100644 --- a/cypress/cypress.json +++ b/cypress/cypress.json @@ -34,10 +34,14 @@ "devPath": "" }] }, - "image": { + "largeImage": { "name": "openSUSE-Leap-15.3-3-DVD-x86_64-Build38.1-Media.iso", "url": "https://mirrors.bfsu.edu.cn/opensuse/distribution/leap/15.3/iso/openSUSE-Leap-15.3-3-DVD-x86_64-Build38.1-Media.iso" }, + "image": { + "name": "cirros-0.5.2-aarch64-disk.img", + "url": "https://github.com/cirros-dev/cirros/releases/download/0.5.2/cirros-0.5.2-aarch64-disk.img" + }, "nfsEndPoint": "nfs://ip" }, "retries": { diff --git a/cypress/pageobjects/image.po.ts b/cypress/pageobjects/image.po.ts index 3c4dde05d..f6be21113 100644 --- a/cypress/pageobjects/image.po.ts +++ b/cypress/pageobjects/image.po.ts @@ -1,4 +1,6 @@ import { Constants } from "@/constants/constants"; +import { HCI } from '@/constants/types' +import CruResourcePo from '@/utils/components/cru-resource.po'; import LabeledInputPo from '@/utils/components/labeled-input.po'; import { VolumePage } from "@/pageobjects/volume.po"; @@ -15,33 +17,21 @@ interface ValueInterface { labels?: any, } -export class ImagePage { - private actionMenu: string = '.list-unstyled.menu'; +export class ImagePage extends CruResourcePo { private downloadOrUploadRadios: string = '.radio-group'; private fileButton: string = '#file'; - private search: string = '.search'; private deleteButton: string = '.card-container.prompt-remove'; - private pageHead = 'main .outlet header h1'; - name() { - return new LabeledInputPo('.namespace-select > .labeled-input', `:contains("Name")`) + constructor() { + super({ + type: HCI.IMAGE + }); } url() { return new LabeledInputPo('.labeled-input', `:contains("URL")`); } - description() { - return new LabeledInputPo('.labeled-input', `:contains("Description")`) - } - - public goToList() { - cy.visit(constants.imagePage); - cy.get(this.pageHead).then($el => { - cy.url().should('exist', constants.imagePage); - }) - } - public goToCreate() { this.goToList(); cy.contains('Create').click(); @@ -49,7 +39,7 @@ export class ImagePage { public goToEdit(name: string) { this.goToList(); - cy.get(this.search).type(name); + cy.get(this.searchInput).type(name); const image = cy.contains(name); image.should('be.visible') @@ -106,7 +96,7 @@ export class ImagePage { public exportImage(vmName: string, imageName: string) { volumes.goToList(); - cy.get(this.search).type(vmName); + cy.get(this.searchInput).type(vmName); const volume = cy.contains(vmName); @@ -119,7 +109,7 @@ export class ImagePage { } public checkState(value: ValueInterface, valid: boolean = true) { - cy.get(this.search).type(value.name); + cy.get(this.searchInput).type(value.name); // state indicator for image upload progress percentage bar cy.contains(value.name).parentsUntil('tbody', 'tr').find('td.col-image-percentage-bar').contains(valid ? 'Completed' : '0%', { timeout: constants.timeout.uploadTimeout }).should('be.visible'); @@ -131,34 +121,6 @@ export class ImagePage { } } - // public checkImageInLH(imageName: string) { - // let imageId: any = null; - - // cy.intercept('GET', '/v1/harvester/harvesterhci.io.virtualmachineimages').as('getImageList'); - // this.goToList(); - - // cy.wait('@getImageList').then(res => { - // expect(res.response?.statusCode).to.equal(200); - - // const images = res.response?.body?.data || []; - - // imageId = images.find((image: any) => image.spec.displayName === imageName)?.id; - // expect(!!imageId).to.be.true; - // }); - - // cy.request({ method: 'GET', url: 'v1/longhorn.io.backingimages', headers: { - // 'Content-Type': 'application/json; charset=utf-8', - // 'accept': 'json' - // }}).as('getBackingImageList'); - - // cy.get('@getBackingImageList').should((response) => { - // const found = response.body.data.find((image: any) => imageId.replace('/', '-') === image.metadata.name); - - // expect(found?.metadata?.state?.error).to.be.false; - // expect(found?.metadata?.state?.name).to.equal('active'); - // }); - // } - public save( { upload, edit }: { upload?: boolean; edit?: boolean } = {} ) { cy.intercept(edit? 'PUT' : 'POST', `/v1/harvester/harvesterhci.io.virtualmachineimages${ upload ? '/*' : edit ? '/*/*' : '' }`).as('createImage'); cy.get('.cru-resource-footer').contains(!edit ? 'Create' : 'Save').click(); @@ -168,24 +130,21 @@ export class ImagePage { } public setValue(value: ValueInterface) { + this.namespace().select(value?.namespace) this.name().input(value?.name); this.url().input(value?.url); this.description().input(value?.description); } public delete(name: string) { - this.goToList(); - cy.get(this.search).type(name); - const image = cy.contains(name); + cy.visit(`/c/local/harvester/${this.type}`) - image.should('be.visible') - image.parentsUntil('tbody', 'tr').click(); - cy.contains('Delete').click(); + this.clickAction(name, 'Delete') cy.intercept('DELETE', '/v1/harvester/harvesterhci.io.virtualmachineimages/*/*').as('delete'); - cy.get(this.deleteButton).contains('Delete').click(); + cy.get(this.confirmRemove).contains('Delete').click(); cy.wait('@delete').then(res => { - expect(res.response?.statusCode).to.equal(200); - }); + expect(res.response?.statusCode, `Delete ${this.type}`).to.be.oneOf([200, 204]); + }) } } diff --git a/cypress/pageobjects/virtualmachine.po.ts b/cypress/pageobjects/virtualmachine.po.ts index d838398f3..a3e62be60 100644 --- a/cypress/pageobjects/virtualmachine.po.ts +++ b/cypress/pageobjects/virtualmachine.po.ts @@ -151,17 +151,12 @@ export class VmsPage { } public init() { + cy.intercept('GET', `v1/harvester/${HCI.IMAGE}s`).as('imageList'); image.goToList(); - cy.request({ - url: `v1/harvester/${HCI.IMAGE}s`, - headers: { - accept: 'application/json' - } - }).as('imageList') - cy.get('@imageList').should((res: any) => { - expect(res?.status, 'Check Image list').to.equal(200); - const images = res?.body?.data || [] + cy.wait('@imageList').should((res: any) => { + expect(res.response?.statusCode, 'Check Image list').to.equal(200); + const images = res?.response?.body?.data || [] const imageEnv = Cypress.env('image'); @@ -179,6 +174,7 @@ export class VmsPage { url, }) image.save() + image.checkState({ name, size: '16 MB' }); } }) } diff --git a/cypress/pageobjects/volume.po.ts b/cypress/pageobjects/volume.po.ts index 9ef38e031..18d3c6ffa 100644 --- a/cypress/pageobjects/volume.po.ts +++ b/cypress/pageobjects/volume.po.ts @@ -1,161 +1,121 @@ import { Constants } from "@/constants/constants"; -import LabeledInputPo from '@/utils/components/labeled-input.po'; -import LabeledSelectPo from '@/utils/components/labeled-select.po'; +import { HCI, PVC } from "@/constants/types"; +import CruResourcePo from "@/utils/components/cru-resource.po"; +import LabeledInputPo from "@/utils/components/labeled-input.po"; +import LabeledSelectPo from "@/utils/components/labeled-select.po"; const constants = new Constants(); interface ValueInterface { - namespace?: string, - name?: string, - description?: string, - image?: string, - size?: string, + namespace?: string; + name?: string; + description?: string; + image?: string; + size?: string; } -export class VolumePage { - private search: string = '.search'; - private actionMenu: string = '.list-unstyled.menu'; - private exportImageActions: string = '.card-actions'; - private deleteButton: string = '.card-container.prompt-remove'; - - name() { - return new LabeledInputPo('.namespace-select > .labeled-input', `:contains("Name")`) +export class VolumePage extends CruResourcePo { + constructor() { + super({ + type: HCI.VOLUME, + realType: PVC, + }); } - description() { - return new LabeledInputPo('.labeled-input', `:contains("Description")`) - } + private exportImageActions: string = ".card-actions"; exportImageName() { - return new LabeledInputPo('.card-container .labeled-input', `:contains("Name")`) + return new LabeledInputPo( + ".card-container .labeled-input", + `:contains("Name")` + ); } source() { - return new LabeledSelectPo('.labeled-select', `:contains("Source")`) + return new LabeledSelectPo(".labeled-select", `:contains("Source")`); } size() { - return new LabeledInputPo('.labeled-input', `:contains("Size")`) + return new LabeledInputPo(".labeled-input", `:contains("Size")`); } image() { - return new LabeledSelectPo('.labeled-select', `:contains("Image"):last`) - } - - public goToList() { - // cy.get('.nav').contains('Volumes').click(); - cy.visit(constants.volumePage) - cy.intercept('GET', '/v1/harvester/persistentvolumeclaims').as('goToVolumeList'); - cy.wait('@goToVolumeList'); - cy.url().should('eq', constants.volumePage) - } - - public goToCreate() { - this.goToList(); - cy.contains('Create').click(); - } - - public goToEdit(name: string) { - this.goToList(); - cy.get(this.search).type(name); - const volume = cy.contains(name); - - volume.should('be.visible') - volume.parentsUntil('tbody', 'tr').find('.icon-actions').click(); - cy.get(this.actionMenu).contains('Edit Config').click(); - } - - goToDetail(name: string) { - const volume = cy.contains(name) - - volume.should('be.visible') - volume.click() - } - - public create(value: ValueInterface) { - this.goToCreate(); - this.setValue(value); - this.save(); + return new LabeledSelectPo(".labeled-select", `:contains("Image"):last`); } public exportImage(volumeName: string, imageName: string) { - cy.contains(volumeName).parentsUntil('tbody', 'tr').find('.icon-actions').click(); - cy.get(this.actionMenu).contains('Export Image').click(); + cy.contains(volumeName) + .parentsUntil("tbody", "tr") + .find(".icon-actions") + .click(); + cy.get(this.actionMenu).contains("Export Image").click(); this.exportImageName().input(imageName); - cy.intercept('POST', `v1/harvester/persistentvolumeclaims/*/${ volumeName }?action=export`).as('exportImage'); - cy.get(this.exportImageActions).contains('Create').click(); - cy.wait('@exportImage').then(res => { - expect(res.response?.statusCode).to.equal(200); - expect(cy.contains('Succeed')); + cy.intercept( + "POST", + `v1/harvester/persistentvolumeclaims/*/${volumeName}?action=export` + ).as("exportImage"); + cy.get(this.exportImageActions).contains("Create").click(); + cy.wait("@exportImage").then((res) => { + expect(res.response?.statusCode).to.be.oneOf([200, 204]); + expect(cy.contains("Succeed")); }); } public checkState(value: ValueInterface) { // state indicator for status of volume status e.g. Ready - cy.contains(value.name || '').parentsUntil('tbody', 'tr').find('td.col-badge-state-formatter .bg-success').contains('Ready').should('be.visible'); + cy.contains(value.name || "") + .parentsUntil("tbody", "tr") + .find("td.col-badge-state-formatter .bg-success") + .contains("Ready") + .should("be.visible"); } public setValue(value: ValueInterface) { + this.size().input(value?.size); + this.name().input(value?.name); + if (!!value.image) { - this.source().select('VM Image'); + this.source().select("VM Image"); this.image().select(value?.image); } - - this.name().input(value?.name); - this.size().input(value?.size); - } - - public save( { edit }: { edit?: boolean } = {} ) { - cy.intercept(edit? 'PUT' : 'POST', `/v1/harvester/persistentvolumeclaims${ edit ? '/*/*' : '' }`).as('createVolume'); - cy.get('.cru-resource-footer').contains(!edit ? 'Create' : 'Save').click(); - cy.wait('@createVolume').then(res => { - expect(res.response?.statusCode).to.equal( edit ? 200 : 201 ); - }); - } - - public delete(name: string) { - this.goToList(); - cy.get(this.search).type(name); - const volume = cy.contains(name); - - volume.should('be.visible') - volume.parentsUntil('tbody', 'tr').click(); - cy.contains('Delete').click(); - - cy.intercept('DELETE', '/v1/harvester/persistentvolumeclaims/*/*').as('delete'); - cy.get(this.deleteButton).contains('Delete').click(); - cy.wait('@delete').then(res => { - expect(res.response?.statusCode).to.equal(200); - }); } public checkStateByVM(vmName: string) { this.goToList(); - cy.get(this.search).type(vmName); + cy.get(this.searchInput).type(vmName); const volume = cy.contains(vmName); - volume.should('be.visible'); + volume.should("be.visible"); // Get volume name from attahched VM in name field of Volume page - volume.parentsUntil('tbody', 'tr').find('td').eq(2).invoke('text').then((volumeName) => { - this.checkState({name: volumeName.trim()}); - }); + volume + .parentsUntil("tbody", "tr") + .find("td") + .eq(2) + .invoke("text") + .then((volumeName) => { + this.checkState({ name: volumeName.trim() }); + }); } public deleteVolumeByVM(vmName: string) { this.goToList(); - cy.get(this.search).type(vmName); + cy.get(this.searchInput).type(vmName); const volume = cy.contains(vmName); - volume.should('be.visible'); + volume.should("be.visible"); // Get volume name from attahched VM in name field of Volume page - volume.parentsUntil('tbody', 'tr').find('td').eq(2).invoke('text').then((volumeName) => { - this.delete(volumeName.trim()); - }); + volume + .parentsUntil("tbody", "tr") + .find("td") + .eq(2) + .invoke("text") + .then((volumeName) => { + this.delete("default", volumeName.trim()); + }); } - } diff --git a/cypress/testcases/image/images.spec.ts b/cypress/testcases/image/images.spec.ts index 215f6beae..afc1e4760 100644 --- a/cypress/testcases/image/images.spec.ts +++ b/cypress/testcases/image/images.spec.ts @@ -1,4 +1,4 @@ -import YAML from 'js-yaml' +import YAML from "js-yaml"; import { VmsPage } from "@/pageobjects/virtualmachine.po"; import { ImagePage } from "@/pageobjects/image.po"; @@ -18,52 +18,53 @@ const image = new ImagePage(); * 4. User should not be able to edit the URL * 5. User should be able to create a new image with same name. */ -describe('Create an image with valid image URL', () => { - const IMAGE_NAME = 'auto-image-valid-url-test'; - const IMAGE_URL = 'http://download.opensuse.org/repositories/Cloud:/Images:/Leap_15.3/images/openSUSE-Leap-15.3.x86_64-NoCloud.qcow2'; - const value = { - name: IMAGE_NAME, - url: IMAGE_URL, - size: '544 MB', - labels: { - y1: 'z1', - y2: 'z2' - } - } - - it('Create an image with valid image URL', () => { - cy.login(); - - // create IMAGE - image.goToCreate(); - image.create(value); - image.checkState(value); - - // edit IMAGE - image.goToEdit(IMAGE_NAME); - - const editValue = { - name: IMAGE_NAME, - url: 'xx', - description: 'Edit image test', - labels: { - edit: 'edit' - } - } - - image.edit(editValue); - - // delete IMAGE - image.delete(IMAGE_NAME); - - // create IMAGE with the same name - image.goToCreate(); - image.create(value); - image.checkState(value); - - // delete IMAGE - image.delete(IMAGE_NAME); - }); +describe("Create an image with valid image URL", () => { + const imageEnv = Cypress.env("image"); + const name = imageEnv.name; + const url = imageEnv.url; + const value = { + name, + url, + size: "16 MB", + labels: { + y1: "z1", + y2: "z2", + }, + }; + + it("Create an image with valid image URL", () => { + cy.login(); + + // create IMAGE + image.goToCreate(); + image.create(value); + image.checkState(value); + + // edit IMAGE + image.goToEdit(name); + + const editValue = { + name, + url: "xx", + description: "Edit image test", + labels: { + edit: "edit", + }, + }; + + image.edit(editValue); + + // delete IMAGE + image.delete(name); + + // create IMAGE with the same name + image.goToCreate(); + image.create(value); + image.checkState(value); + + // delete IMAGE + image.delete(name); + }); }); /** @@ -71,26 +72,26 @@ describe('Create an image with valid image URL', () => { * Expected Results * 1. Image state show as Failed */ - describe('Create image with invalid URL', () => { - const IMAGE_NAME = 'auto-image-invalid-url-test'; +describe("Create image with invalid URL", () => { + const IMAGE_NAME = "auto-image-invalid-url-test"; - it('Create image with invalid URL', () => { - cy.login(); + it("Create image with invalid URL", () => { + cy.login(); - // create invalid IMAGE - image.goToCreate(); + // create invalid IMAGE + image.goToCreate(); - const value = { - name: IMAGE_NAME, - url: 'https://test.img' - } + const value = { + name: IMAGE_NAME, + url: "https://test.img", + }; - image.create(value); - image.checkState(value, false); + image.create(value); + image.checkState(value, false); - // delete IMAGE - image.delete(IMAGE_NAME); - }); + // delete IMAGE + image.delete(IMAGE_NAME); + }); }); /** @@ -101,45 +102,45 @@ describe('Create an image with valid image URL', () => { * Expected Results * 1. image “img-1” will be deleted */ -describe('Delete VM with exported image', () => { - const IMAGE_NAME = 'img-1'; - const VM_NAME = 'vm-1'; - - it('Delete VM with exported image', () => { - cy.login(); - - const imageEnv = Cypress.env('image'); - - // create VM - const value = { - name: VM_NAME, - cpu: '2', - memory: '4', - image: imageEnv.name, - } +describe("Delete VM with exported image", () => { + const IMAGE_NAME = "img-1"; + const VM_NAME = "vm-1"; - vms.create(value); + it("Delete VM with exported image", () => { + cy.login(); - // check VM state - vms.goToConfigDetail(VM_NAME); - vms.goToYamlDetail(VM_NAME); + const imageEnv = Cypress.env("image"); - // export IMAGE - image.exportImage(VM_NAME, IMAGE_NAME); - - // check IMAGE state - image.goToList(); - image.checkState({ - name: IMAGE_NAME, - size: '10 GB' - }); + // create VM + const value = { + name: VM_NAME, + cpu: "2", + memory: "4", + image: imageEnv.name, + }; + + vms.create(value); + + // check VM state + vms.goToConfigDetail(VM_NAME); + vms.goToYamlDetail(VM_NAME); + + // export IMAGE + image.exportImage(VM_NAME, IMAGE_NAME); + + // check IMAGE state + image.goToList(); + image.checkState({ + name: IMAGE_NAME, + size: "10 GB", + }); - // delete VM - vms.delete(VM_NAME); + // delete VM + vms.delete(VM_NAME); - // delete IMAGE - image.delete(IMAGE_NAME); - }); + // delete IMAGE + image.delete(IMAGE_NAME); + }); }); /** @@ -150,59 +151,59 @@ describe('Delete VM with exported image', () => { * Expected Results * 1. image “img-1” will be updated */ -describe('Update image labels after deleting source VM', () => { - const IMAGE_NAME = 'img-1'; - const VM_NAME = 'vm-1'; - - it('Update image labels after deleting source VM', () => { - cy.login(); - - const imageEnv = Cypress.env('image'); - - const value = { - name: VM_NAME, - cpu: '2', - memory: '4', - image: imageEnv.name, - } - - // create VM - vms.create(value); - - // check VM state - vms.goToConfigDetail(VM_NAME); - vms.goToYamlDetail(VM_NAME); - - // export IMAGE - image.exportImage(VM_NAME, IMAGE_NAME); - - // check IMAGE state - image.goToList(); - image.checkState({ - name: IMAGE_NAME, - size: '10 GB' - }); - - // delete VM - vms.delete(VM_NAME); - - // edit IMAGE - image.goToEdit(IMAGE_NAME); - - const editValue = { - name: IMAGE_NAME, - url: 'xx', - description: 'Edit image test', - labels: { - edit: 'edit' - } - } - - image.edit(editValue, true); - - // delete IMAGE - image.delete(IMAGE_NAME); +describe("Update image labels after deleting source VM", () => { + const IMAGE_NAME = "img-1"; + const VM_NAME = "vm-1"; + + it("Update image labels after deleting source VM", () => { + cy.login(); + + const imageEnv = Cypress.env("image"); + + const value = { + name: VM_NAME, + cpu: "2", + memory: "4", + image: imageEnv.name, + }; + + // create VM + vms.create(value); + + // check VM state + vms.goToConfigDetail(VM_NAME); + vms.goToYamlDetail(VM_NAME); + + // export IMAGE + image.exportImage(VM_NAME, IMAGE_NAME); + + // check IMAGE state + image.goToList(); + image.checkState({ + name: IMAGE_NAME, + size: "10 GB", }); + + // delete VM + vms.delete(VM_NAME); + + // edit IMAGE + image.goToEdit(IMAGE_NAME); + + const editValue = { + name: IMAGE_NAME, + url: "xx", + description: "Edit image test", + labels: { + edit: "edit", + }, + }; + + image.edit(editValue, true); + + // delete IMAGE + image.delete(IMAGE_NAME); + }); }); /** @@ -264,47 +265,47 @@ describe('Update image labels after deleting source VM', () => { * Expected Results * 1. Image should upload. */ -describe('Create a ISO image via upload', () => { - const IMAGE_NAME = 'auto-image-iso-upload-test'; - const VM_NAME = 'auto-image-iso-test-vm'; - - it('Create a ISO image via upload', () => { - cy.login(); - - // create IMAGE - image.goToCreate(); - - const value = { - name: IMAGE_NAME, - size: '16 MB', - path: 'cypress/fixtures/cirros-0.5.2-aarch64-disk.img', - labels: { - from: 'upload', - y2: 'z2' - } - } - - image.createFromUpload(value); - image.checkState(value); - - // create VM - const vmValue = { - name: VM_NAME, - cpu: '2', - memory: '4', - image: IMAGE_NAME, - } - - vms.create(vmValue); - - // check VM state - vms.goToConfigDetail(VM_NAME); - vms.goToYamlDetail(VM_NAME); - - // delete VM - vms.delete(VM_NAME) - - // delete IMAGE - image.delete(IMAGE_NAME) - }); +describe("Create a ISO image via upload", () => { + const IMAGE_NAME = "auto-image-iso-upload-test"; + const VM_NAME = "auto-image-iso-test-vm"; + + it("Create a ISO image via upload", () => { + cy.login(); + + // create IMAGE + image.goToCreate(); + + const value = { + name: IMAGE_NAME, + size: "16 MB", + path: "cypress/fixtures/cirros-0.5.2-aarch64-disk.img", + labels: { + from: "upload", + y2: "z2", + }, + }; + + image.createFromUpload(value); + image.checkState(value); + + // create VM + const vmValue = { + name: VM_NAME, + cpu: "2", + memory: "4", + image: IMAGE_NAME, + }; + + vms.create(vmValue); + + // check VM state + vms.goToConfigDetail(VM_NAME); + vms.goToYamlDetail(VM_NAME); + + // delete VM + vms.delete(VM_NAME); + + // delete IMAGE + image.delete(IMAGE_NAME); + }); }); diff --git a/cypress/testcases/volume/volumes.spec.ts b/cypress/testcases/volume/volumes.spec.ts index e69de29bb..dd5378e5b 100644 --- a/cypress/testcases/volume/volumes.spec.ts +++ b/cypress/testcases/volume/volumes.spec.ts @@ -0,0 +1,209 @@ +import YAML from "js-yaml"; +import { VmsPage } from "@/pageobjects/virtualmachine.po"; +import { ImagePage } from "@/pageobjects/image.po"; +import { VolumePage } from "@/pageobjects/volume.po"; + +const vms = new VmsPage(); +const image = new ImagePage(); +const volumes = new VolumePage(); + +/** + + * 1. Create new VM + * 2. Export volume to image from volumes page + * 3. Create new VM from image + * Expected Results + * 1. image should upload/complete in images page + * 2. New VM should create + */ +describe("Create image from Volume", () => { + const IMAGE_NAME = "volume-export-image"; + const VM_NAME = "volume-export-vm"; + const ANOTHER_VM_NAME = "volume-export-vm-2"; + + it("Create image from Volume", () => { + cy.login(); + + // create VM + const imageEnv = Cypress.env("image"); + const value = { + name: VM_NAME, + cpu: "2", + memory: "4", + image: imageEnv.name, + }; + + vms.create(value); + + // check VM state + vms.goToConfigDetail(VM_NAME); + vms.goToYamlDetail(VM_NAME); + + // export IMAGE + image.exportImage(VM_NAME, IMAGE_NAME); + + // check IMAGE state + image.goToList(); + image.checkState({ + name: IMAGE_NAME, + size: "10 GB", + }); + + // delete VM + vms.delete(VM_NAME); + + // create VM + vms.create({ + name: ANOTHER_VM_NAME, + cpu: "2", + memory: "4", + image: IMAGE_NAME, + }); + + // check VM state + vms.goToConfigDetail(ANOTHER_VM_NAME); + vms.goToYamlDetail(ANOTHER_VM_NAME); + + // delete VM + vms.delete(ANOTHER_VM_NAME); + + // delete IMAGE + image.delete(IMAGE_NAME); + }); +}); + +/** + * 1. Navigate to volumes page + * 2. Click Create + * 3. Select an image + * 4. Input a size + * 5. Click Create + * Expected Results + * 1. Page should load + * 2. Volume should create successfully and go to succeeded in the list + */ +describe("Create volume root disk VM Image Form", () => { + const VOLUME_NAME = "volume-e2e-1"; + + it("Create volume root disk VM Image Form", () => { + cy.login(); + + const imageEnv = Cypress.env("image"); + + // create VOLUME + const value = { + name: VOLUME_NAME, + size: "10", + image: imageEnv.name, + }; + + volumes.create(value); + + // check state + volumes.checkState(value); + + // delete VOLUME + volumes.delete("default", VOLUME_NAME); + }); +}); + +/** + + * 1. Create a VM with a root volume + * 2. Delete the VM but not the volume + * 3. Verify Volume still exists + * 4. Delete the volume + * Expected Results + * 1. VM should create + * 2. VM should delete + * 3. Volume should still show in Volume list + * 4. Volume should delete + */ +describe("Delete volume that was attached to VM but now is not", () => { + const VM_NAME = "vm-e2e-1"; + + it("Delete volume that was attached to VM but now is not", () => { + cy.login(); + + const imageEnv = Cypress.env("image"); + + // create VM + const value = { + name: VM_NAME, + cpu: "2", + memory: "4", + image: imageEnv.name, + }; + + vms.create(value); + + // check VM state + vms.goToConfigDetail(VM_NAME); + vms.goToYamlDetail(VM_NAME); + + // delete VM + vms.delete(VM_NAME, { removeRootDisk: false }); + + // check VOLUME state + volumes.checkStateByVM(VM_NAME); + + // delete VOLUME + volumes.deleteVolumeByVM(VM_NAME); + }); +}); + +/** + + * 1. Create a VM with a root volume + * 2. Stop the vm + * 3. Navigate to volumes page + * 4. Edit Volume via form + * 5. increase size + * 6. Click Save + * 7. Check size of root disk + * Expected Results + * 1. VM should stop + * 2. VM should reboot after saving + * 3. Disk should be resized + */ +// describe.only('Edit volume increase size via form', () => { +// const VM_NAME = 'vm-e2e-increase-size'; +// let volumeName = ''; + +// it('Edit volume increase size via form', () => { +// cy.login(); + +// // create VM +// const value = { +// name: VM_NAME, +// cpu: '2', +// memory: '4', +// image: 'ubuntu-18.04-server-cloudimg-amd64.img', +// } + +// cy.intercept('POST', '/v1/harvester/kubevirt.io.virtualmachines/*').as('create'); +// vms.create(value); +// cy.wait('@create').then(res => { +// volumeName = res.response?.body?.metadata?.labels['harvesterhci.io/cloud-init-template'] +// }) + +// // check VM state +// vms.goToConfigDetail(VM_NAME); +// vms.goToYamlDetail(VM_NAME); + +// // stop VM +// vms.clickAction(VM_NAME, 'Stop'); + +// // increase VOLUME size +// volumes.edit({ +// name: volumeName, +// size: '30' +// }) + +// // check VOLUME state +// volumes.checkState(volumeName); + +// // delete VM +// vms.delete(VM_NAME); +// }); +// }); diff --git a/cypress/utils/components/cru-resource.po.ts b/cypress/utils/components/cru-resource.po.ts index 8c2339bee..fcff6a0b3 100644 --- a/cypress/utils/components/cru-resource.po.ts +++ b/cypress/utils/components/cru-resource.po.ts @@ -15,11 +15,11 @@ export default class CruResourcePo extends PagePo { public realType = ''; public storeType: string|undefined = undefined; - private footerButtons = '.cru-resource-footer' - private confirmRemove = '.card-container.prompt-remove' - private searchInput = '.search' - private actionMenu = '.list-unstyled.menu' - private actionMenuIcon = '.icon-actions' + public footerButtons = '.cru-resource-footer' + public confirmRemove = '.card-container.prompt-remove' + public searchInput = '.search' + public actionMenu = '.list-unstyled.menu' + public actionMenuIcon = '.icon-actions' namespace() { return new LabeledSelectPo('.labeled-select', `:contains("Namespace")`)