mirror of
https://github.com/podman-desktop/podman-desktop
synced 2026-05-24 10:18:53 +00:00
test: delete from volume page and prune volume e2e tests (#8128)
* test: reset head of the branch to fix rebase issue Signed-off-by: Daniel Villanueva <davillan@redhat.com> * test: code refactor Signed-off-by: Daniel Villanueva <davillan@redhat.com> * test: some code cleanup Signed-off-by: Daniel Villanueva <davillan@redhat.com> --------- Signed-off-by: Daniel Villanueva <davillan@redhat.com>
This commit is contained in:
parent
02eab4165d
commit
94e0619b1f
3 changed files with 148 additions and 2 deletions
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
|
||||
import { waitUntil } from '../../utility/wait';
|
||||
import { BasePage } from './base-page';
|
||||
|
||||
/**
|
||||
|
|
@ -70,6 +71,10 @@ export abstract class MainPage extends BasePage {
|
|||
return this.content.getByRole('table');
|
||||
}
|
||||
|
||||
async rowsAreVisible(): Promise<boolean> {
|
||||
return await this.page.getByRole('row').first().isVisible();
|
||||
}
|
||||
|
||||
async getRowFromTableByName(name: string): Promise<Locator | undefined> {
|
||||
if (await this.pageIsEmpty()) {
|
||||
return undefined;
|
||||
|
|
@ -94,4 +99,25 @@ export abstract class MainPage extends BasePage {
|
|||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async getRowsFromTableByStatus(status: string): Promise<Locator[]> {
|
||||
await waitUntil(async () => await this.rowsAreVisible(), { sendError: false });
|
||||
|
||||
const table = this.content.getByRole('table');
|
||||
const rows = await table.getByRole('row').all();
|
||||
const filteredRows = [];
|
||||
for (let rowNum = 1; rowNum < rows.length; rowNum++) {
|
||||
//skip header
|
||||
const statusCount = await rows[rowNum].getByRole('cell').nth(2).getByTitle(status, { exact: true }).count();
|
||||
if (statusCount > 0) filteredRows.push(rows[rowNum]);
|
||||
}
|
||||
return filteredRows;
|
||||
}
|
||||
|
||||
async countRowsFromTable(): Promise<number> {
|
||||
await waitUntil(async () => await this.rowsAreVisible(), { sendError: false });
|
||||
const table = this.content.getByRole('table');
|
||||
const rows = await table.getByRole('row').all();
|
||||
return rows.length > 1 ? rows.length - 1 : 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
import type { Locator, Page } from '@playwright/test';
|
||||
import { expect as playExpect } from '@playwright/test';
|
||||
|
||||
import { handleConfirmationDialog } from '../../utility/operations';
|
||||
import { waitUntil, waitWhile } from '../../utility/wait';
|
||||
import { VolumeState } from '../core/states';
|
||||
import { CreateVolumePage } from './create-volume-page';
|
||||
import { MainPage } from './main-page';
|
||||
import { VolumeDetailsPage } from './volume-details-page';
|
||||
|
|
@ -58,6 +60,19 @@ export class VolumesPage extends MainPage {
|
|||
return new VolumeDetailsPage(this.page, volumeName);
|
||||
}
|
||||
|
||||
async deleteVolume(volumeName: string): Promise<VolumesPage> {
|
||||
const volumeRow = await this.getVolumeRowByName(volumeName);
|
||||
if (volumeRow === undefined) {
|
||||
throw Error(`Volume: ${volumeName} does not exist`);
|
||||
}
|
||||
const containerRowDeleteButton = volumeRow.getByRole('button', { name: 'Delete Volume' });
|
||||
await playExpect(containerRowDeleteButton).toBeEnabled();
|
||||
await containerRowDeleteButton.click();
|
||||
await handleConfirmationDialog(this.page);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async getVolumeRowByName(name: string): Promise<Locator | undefined> {
|
||||
return this.getRowFromTableByName(name);
|
||||
}
|
||||
|
|
@ -67,6 +82,14 @@ export class VolumesPage extends MainPage {
|
|||
return result !== undefined;
|
||||
}
|
||||
|
||||
async countVolumesFromTable(): Promise<number> {
|
||||
return this.countRowsFromTable();
|
||||
}
|
||||
|
||||
async countUsedVolumesFromTable(): Promise<number> {
|
||||
return (await this.getRowsFromTableByStatus(VolumeState.Used)).length;
|
||||
}
|
||||
|
||||
async waitForVolumeExists(name: string): Promise<boolean> {
|
||||
await waitUntil(async () => await this.volumeExists(name));
|
||||
return true;
|
||||
|
|
@ -76,4 +99,11 @@ export class VolumesPage extends MainPage {
|
|||
await waitWhile(async () => await this.volumeExists(name));
|
||||
return true;
|
||||
}
|
||||
|
||||
async pruneVolumes(): Promise<VolumesPage> {
|
||||
await playExpect(this.pruneVolumesButton).toBeEnabled();
|
||||
await this.pruneVolumesButton.click();
|
||||
await handleConfirmationDialog(this.page, 'Prune');
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import type { Page } from '@playwright/test';
|
|||
import { expect as playExpect } from '@playwright/test';
|
||||
import { afterAll, beforeAll, beforeEach, describe, test } from 'vitest';
|
||||
|
||||
import { ContainerState, VolumeState } from '../model/core/states';
|
||||
import type { ContainerInteractiveParams } from '../model/core/types';
|
||||
import { WelcomePage } from '../model/pages/welcome-page';
|
||||
import { NavigationBar } from '../model/workbench/navigation';
|
||||
import { PodmanDesktopRunner } from '../runner/podman-desktop-runner';
|
||||
|
|
@ -30,6 +32,11 @@ let pdRunner: PodmanDesktopRunner;
|
|||
let page: Page;
|
||||
let navBar: NavigationBar;
|
||||
|
||||
const imageToPull = 'quay.io/centos-bootc/bootc-image-builder';
|
||||
const imageTag = 'latest';
|
||||
const containerToRun = 'bootc-image-builder';
|
||||
const containerStartParams: ContainerInteractiveParams = { attachTerminal: false };
|
||||
|
||||
beforeAll(async () => {
|
||||
pdRunner = new PodmanDesktopRunner();
|
||||
page = await pdRunner.start();
|
||||
|
|
@ -55,12 +62,11 @@ describe('Volume workflow verification', async () => {
|
|||
test('Create new Volume', async () => {
|
||||
let volumesPage = await navBar.openVolumes();
|
||||
await playExpect(volumesPage.heading).toBeVisible();
|
||||
|
||||
const createVolumePage = await volumesPage.openCreateVolumePage(volumeName);
|
||||
volumesPage = await createVolumePage.createVolume(volumeName);
|
||||
|
||||
await playExpect.poll(async () => await volumesPage.waitForVolumeExists(volumeName)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Test navigation between pages', async () => {
|
||||
const volumesPage = await navBar.openVolumes();
|
||||
await playExpect(volumesPage.heading).toBeVisible();
|
||||
|
|
@ -77,10 +83,29 @@ describe('Volume workflow verification', async () => {
|
|||
await volumeDetails.closeButton.click();
|
||||
await playExpect(volumesPage.heading).toBeVisible();
|
||||
});
|
||||
|
||||
test('Delete volume from Volumes page', async () => {
|
||||
let volumesPage = await navBar.openVolumes();
|
||||
await playExpect(volumesPage.heading).toBeVisible();
|
||||
const volumeRow = await volumesPage.getVolumeRowByName(volumeName);
|
||||
playExpect(volumeRow).not.toBeUndefined();
|
||||
volumesPage = await volumesPage.deleteVolume(volumeName);
|
||||
await playExpect.poll(async () => await volumesPage.waitForVolumeDelete(volumeName)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Delete volume through details page', async () => {
|
||||
//re-create a new volume
|
||||
let volumesPage = await navBar.openVolumes();
|
||||
await playExpect(volumesPage.heading).toBeVisible();
|
||||
|
||||
const createVolumePage = await volumesPage.openCreateVolumePage(volumeName);
|
||||
volumesPage = await createVolumePage.createVolume(volumeName);
|
||||
|
||||
await playExpect.poll(async () => await volumesPage.waitForVolumeExists(volumeName)).toBeTruthy();
|
||||
|
||||
//delete it from the details page
|
||||
volumesPage = await navBar.openVolumes();
|
||||
await playExpect(volumesPage.heading).toBeVisible();
|
||||
const volumeRow = await volumesPage.getVolumeRowByName(volumeName);
|
||||
playExpect(volumeRow).not.toBeUndefined();
|
||||
|
||||
|
|
@ -89,4 +114,69 @@ describe('Volume workflow verification', async () => {
|
|||
|
||||
await playExpect.poll(async () => await volumesPage.waitForVolumeDelete(volumeName)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Create volumes from bootc-image-builder', async () => {
|
||||
//count the number of existing volumes
|
||||
const navigationBar = new NavigationBar(page);
|
||||
let volumesPage = await navigationBar.openVolumes();
|
||||
let previousVolumes = await volumesPage.countVolumesFromTable();
|
||||
|
||||
//if there are volumes, check how many are used
|
||||
if (previousVolumes > 0) {
|
||||
const usedVolumes = await volumesPage.countUsedVolumesFromTable();
|
||||
//if there are unused volumes, prune them
|
||||
if (previousVolumes - usedVolumes > 0) {
|
||||
volumesPage = await volumesPage.pruneVolumes();
|
||||
await playExpect
|
||||
.poll(async () => (await volumesPage.getRowsFromTableByStatus(VolumeState.Unused)).length, { timeout: 10000 })
|
||||
.toBe(0);
|
||||
previousVolumes = await volumesPage.countVolumesFromTable();
|
||||
}
|
||||
}
|
||||
|
||||
//pull image from quay.io/centos-bootc/bootc-image-builder
|
||||
let images = await navigationBar.openImages();
|
||||
const pullImagePage = await images.openPullImage();
|
||||
images = await pullImagePage.pullImage(imageToPull, imageTag);
|
||||
await playExpect.poll(async () => await images.waitForImageExists(imageToPull)).toBeTruthy();
|
||||
|
||||
//start a container from the image (generates 4 new volumes)
|
||||
const imageDetails = await images.openImageDetails(imageToPull);
|
||||
const runImage = await imageDetails.openRunImage();
|
||||
let containers = await runImage.startContainer(containerToRun, containerStartParams);
|
||||
await playExpect(containers.header).toBeVisible();
|
||||
await playExpect
|
||||
.poll(async () => await containers.containerExists(containerToRun), { timeout: 10000 })
|
||||
.toBeTruthy();
|
||||
await containers.startContainer(containerToRun);
|
||||
|
||||
//check that four volumes are created (in addition to the existing ones)
|
||||
volumesPage = await navigationBar.openVolumes();
|
||||
await playExpect(volumesPage.heading).toBeVisible();
|
||||
const newVolumes = await volumesPage.countVolumesFromTable();
|
||||
playExpect(newVolumes - previousVolumes).toBe(4);
|
||||
|
||||
//check the container is stopped and delete it
|
||||
containers = await navigationBar.openContainers();
|
||||
const containerDetails = await containers.openContainersDetails(containerToRun);
|
||||
await playExpect
|
||||
.poll(async () => containerDetails.getState(), { timeout: 20000 })
|
||||
.toContain(ContainerState.Exited.toLowerCase());
|
||||
await playExpect(await containerDetails.getStateLocator()).toHaveText(ContainerState.Exited.toLowerCase());
|
||||
containers = await navigationBar.openContainers();
|
||||
const containersPage = await containers.deleteContainer(containerToRun);
|
||||
await playExpect(containersPage.heading).toBeVisible();
|
||||
await playExpect
|
||||
.poll(async () => await containersPage.containerExists(containerToRun), { timeout: 15000 })
|
||||
.toBeFalsy();
|
||||
|
||||
//prune unused volumes
|
||||
volumesPage = await navigationBar.openVolumes();
|
||||
volumesPage = await volumesPage.pruneVolumes();
|
||||
await playExpect
|
||||
.poll(async () => (await volumesPage.getRowsFromTableByStatus(VolumeState.Unused)).length, { timeout: 10000 })
|
||||
.toBe(0);
|
||||
const finalVolumes = await volumesPage.countVolumesFromTable();
|
||||
playExpect(finalVolumes - previousVolumes).toBe(0);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue