-
Notifications
You must be signed in to change notification settings - Fork 667
Add VM Wizard test cases for Kubevirt plugin #2053
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| /* eslint-disable no-await-in-loop, no-console, no-underscore-dangle */ | ||
| import { execSync } from 'child_process'; | ||
| import { | ||
| $, | ||
| by, | ||
| ElementFinder, | ||
| browser, | ||
| ExpectedConditions as until, | ||
| element, | ||
| ElementArrayFinder, | ||
| } from 'protractor'; | ||
| import { config } from '../../../../integration-tests/protractor.conf'; | ||
|
|
||
| export function resolveTimeout(timeout: number, defaultTimeout: number) { | ||
| return timeout !== undefined ? timeout : defaultTimeout; | ||
| } | ||
|
|
||
| export function removeLeakedResources(leakedResources: Set<string>) { | ||
| const leakedArray: string[] = [...leakedResources]; | ||
| if (leakedArray.length > 0) { | ||
| console.error(`Leaked ${leakedArray.join()}`); | ||
| leakedArray | ||
| .map((r) => JSON.parse(r) as { name: string; namespace: string; kind: string }) | ||
| .forEach(({ name, namespace, kind }) => { | ||
| try { | ||
| execSync(`kubectl delete -n ${namespace} --cascade ${kind} ${name}`); | ||
| } catch (error) { | ||
| console.error(`Failed to delete ${kind} ${name}:\n${error}`); | ||
| } | ||
| }); | ||
| } | ||
| leakedResources.clear(); | ||
| } | ||
|
|
||
| export function addLeakableResource(leakedResources: Set<string>, resource) { | ||
| leakedResources.add( | ||
| JSON.stringify({ | ||
| name: resource.metadata.name, | ||
| namespace: resource.metadata.namespace, | ||
| kind: resource.kind, | ||
| }), | ||
| ); | ||
| } | ||
|
|
||
| export function removeLeakableResource(leakedResources: Set<string>, resource) { | ||
| leakedResources.delete( | ||
| JSON.stringify({ | ||
| name: resource.metadata.name, | ||
| namespace: resource.metadata.namespace, | ||
| kind: resource.kind, | ||
| }), | ||
| ); | ||
| } | ||
|
|
||
| export function createResource(resource) { | ||
| execSync(`echo '${JSON.stringify(resource)}' | kubectl create -f -`); | ||
| } | ||
|
|
||
| export function createResources(resources) { | ||
| resources.forEach(createResource); | ||
| } | ||
|
|
||
| export function deleteResource(resource) { | ||
| const kind = resource.kind === 'NetworkAttachmentDefinition' ? 'net-attach-def' : resource.kind; | ||
| execSync( | ||
| `kubectl delete -n ${resource.metadata.namespace} --cascade ${kind} ${resource.metadata.name}`, | ||
| ); | ||
| } | ||
|
|
||
| export function deleteResources(resources) { | ||
| resources.forEach(deleteResource); | ||
| } | ||
|
|
||
| export async function withResource( | ||
| resourceSet: Set<string>, | ||
| resource: any, | ||
| callback: Function, | ||
| keepResource: boolean = false, | ||
| ) { | ||
| addLeakableResource(resourceSet, resource); | ||
| await callback(); | ||
| if (!keepResource) { | ||
| deleteResource(resource); | ||
| removeLeakableResource(resourceSet, resource); | ||
| } | ||
| } | ||
|
|
||
| export async function click(elem: ElementFinder, timeout?: number) { | ||
| const _timeout = resolveTimeout(timeout, config.jasmineNodeOpts.defaultTimeoutInterval); | ||
| await browser.wait(until.elementToBeClickable(elem), _timeout); | ||
| await elem.click(); | ||
| } | ||
|
|
||
| export async function selectDropdownOption(dropdownId: string, option: string) { | ||
| await click($(dropdownId)); | ||
| await browser.wait(until.presenceOf(element(by.linkText(option)))); | ||
| await $(`${dropdownId} + ul`) | ||
| .element(by.linkText(option)) | ||
| .click(); | ||
| } | ||
|
|
||
| export async function getDropdownOptions(dropdownId: string): Promise<string[]> { | ||
| const options = []; | ||
| await $(`${dropdownId} + ul`) | ||
| .$$('li') | ||
| .each((elem) => { | ||
| elem | ||
| .getText() | ||
| .then((text) => { | ||
| options.push(text); | ||
| }) | ||
| .catch((err) => { | ||
| return Promise.reject(err); | ||
| }); | ||
| }); | ||
| return options; | ||
| } | ||
|
|
||
| export async function asyncForEach(iterable, callback) { | ||
| const array = [...iterable]; | ||
| for (let index = 0; index < array.length; index++) { | ||
| await callback(array[index], index, array); | ||
| } | ||
| } | ||
|
|
||
| export const waitForCount = (elementArrayFinder: ElementArrayFinder, targetCount: number) => { | ||
| return async () => { | ||
| const count = await elementArrayFinder.count(); | ||
| return count === targetCount; | ||
| }; | ||
| }; | ||
|
|
||
| export const waitForStringInElement = (elem: ElementFinder, needle: string) => { | ||
| return async () => { | ||
| const content = await elem.getText(); | ||
| return content.includes(needle); | ||
| }; | ||
| }; | ||
|
|
||
| export const waitForStringNotInElement = (elem: ElementFinder, needle: string) => { | ||
| return async () => { | ||
| const content = await elem.getText(); | ||
| return !content.includes(needle); | ||
| }; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import { browser } from 'protractor'; | ||
| import { appHost, testName } from '../../../../../integration-tests/protractor.conf'; | ||
| import { clickHorizontalTab } from '../../../../../integration-tests/views/horizontal-nav.view'; | ||
| import { isLoaded, resourceTitle } from '../../../../../integration-tests/views/crud.view'; | ||
|
|
||
| export class DetailView { | ||
| readonly name: string; | ||
|
|
||
| readonly namespace: string; | ||
|
|
||
| readonly kind: string; | ||
|
|
||
| constructor(instance) { | ||
| this.name = instance.name; | ||
| this.namespace = instance.namespace; | ||
| this.kind = instance.kind; | ||
| } | ||
|
|
||
| static async getResourceTitle() { | ||
| return resourceTitle.getText(); | ||
| } | ||
|
|
||
| async navigateToTab(tabName: string) { | ||
| if (!(await resourceTitle.isPresent()) || (await resourceTitle.getText()) !== this.name) { | ||
| await browser.get(`${appHost}/k8s/ns/${this.namespace}/${this.kind}/${this.name}`); | ||
| await isLoaded(); | ||
| } | ||
| await clickHorizontalTab(tabName); | ||
| await isLoaded(); | ||
| } | ||
|
|
||
| async navigateToListView() { | ||
| const vmsListUrl = (namespace) => `${appHost}/k8s/ns/${namespace}/${this.kind}`; | ||
| const currentUrl = await browser.getCurrentUrl(); | ||
| if (![vmsListUrl(testName), vmsListUrl('all-namespaces')].includes(currentUrl)) { | ||
| await browser.get(vmsListUrl(this.namespace)); | ||
| await isLoaded(); | ||
| } | ||
| } | ||
|
|
||
| asResource() { | ||
| return { | ||
| kind: this.kind, | ||
| metadata: { | ||
| namespace: this.namespace, | ||
| name: this.name, | ||
| }, | ||
| }; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| /* eslint-disable no-await-in-loop */ | ||
|
||
| import { selectDropdownOption, click } from '../../../../console-shared/src/test-utils/utils'; | ||
| import { isLoaded, resourceRows } from '../../../../../integration-tests/views/crud.view'; | ||
| import { TABS, diskTabCol, networkTabCol } from '../utils/consts'; | ||
| import { StorageResource, NetworkResource } from '../utils/types'; | ||
| import { fillInput } from '../utils/utils'; | ||
| import * as kubevirtDetailView from '../../views/kubevirtDetailView.view'; | ||
| import { confirmAction } from '../../views/vm.actions.view'; | ||
| import { DetailView } from './detailView'; | ||
|
|
||
| export class KubevirtDetailView extends DetailView { | ||
| async getAttachedDisks(): Promise<StorageResource[]> { | ||
| await this.navigateToTab(TABS.DISKS); | ||
| const resources = []; | ||
| for (const row of await resourceRows) { | ||
| const cells = row.$$('div'); | ||
| resources.push({ | ||
| name: await cells.get(diskTabCol.name).getText(), | ||
| size: (await cells.get(diskTabCol.size).getText()).match(/^\d*/)[0], | ||
| storageClass: await cells.get(diskTabCol.storageClass).getText(), | ||
| }); | ||
| } | ||
| return resources; | ||
| } | ||
|
|
||
| async getAttachedNICs(): Promise<NetworkResource[]> { | ||
| await this.navigateToTab(TABS.NICS); | ||
| const resources = []; | ||
| for (const row of await resourceRows) { | ||
| const cells = row.$$('div'); | ||
| resources.push({ | ||
| name: await cells.get(networkTabCol.name).getText(), | ||
| mac: await cells.get(networkTabCol.mac).getText(), | ||
| networkDefinition: await cells.get(networkTabCol.networkDefinition).getText(), | ||
| binding: await cells.get(networkTabCol.binding).getText(), | ||
| }); | ||
| } | ||
| return resources; | ||
| } | ||
|
|
||
| async addDisk(disk: StorageResource) { | ||
| await this.navigateToTab(TABS.DISKS); | ||
| await click(kubevirtDetailView.createDisk, 1000); | ||
| await fillInput(kubevirtDetailView.diskName, disk.name); | ||
| await fillInput(kubevirtDetailView.diskSize, disk.size); | ||
| await selectDropdownOption(kubevirtDetailView.diskStorageClassDropdownId, disk.storageClass); | ||
| await click(kubevirtDetailView.applyBtn); | ||
| await isLoaded(); | ||
| } | ||
|
|
||
| async removeDisk(name: string) { | ||
| await this.navigateToTab(TABS.DISKS); | ||
| await kubevirtDetailView.selectKebabOption(name, 'Delete'); | ||
| await confirmAction(); | ||
| } | ||
|
|
||
| async addNIC(nic: NetworkResource) { | ||
| await this.navigateToTab(TABS.NICS); | ||
|
||
| await click(kubevirtDetailView.createNic, 1000); | ||
| await fillInput(kubevirtDetailView.nicName, nic.name); | ||
| await selectDropdownOption(kubevirtDetailView.networkTypeDropdownId, nic.networkDefinition); | ||
| await selectDropdownOption(kubevirtDetailView.networkBindingId, nic.binding); | ||
| await fillInput(kubevirtDetailView.macAddress, nic.mac); | ||
| await click(kubevirtDetailView.applyBtn); | ||
| await isLoaded(); | ||
| } | ||
|
|
||
| async removeNIC(name: string) { | ||
| await this.navigateToTab(TABS.NICS); | ||
| await kubevirtDetailView.selectKebabOption(name, 'Delete'); | ||
| await confirmAction(); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the runtime for this code is Node.js (i.e. Protractor or Jest) then you can simply add
/* eslint-env node */at the top of the file.
By doing that, the above rule disable list might not be necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
didn't help