Skip to content

Commit

Permalink
initial version of volume page e2e tests (podman-desktop#6742)
Browse files Browse the repository at this point in the history
* test: volume page e2e tests
  • Loading branch information
cbr7 authored Apr 10, 2024
1 parent abd22dc commit dfbf936
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 0 deletions.
51 changes: 51 additions & 0 deletions tests/playwright/src/model/pages/create-volume-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import type { Locator, Page } from '@playwright/test';
import { expect as playExpect } from '@playwright/test';

import { BasePage } from './base-page';
import { VolumesPage } from './volumes-page';

export class CreateVolumePage extends BasePage {
readonly heading: Locator;
readonly closeLink: Locator;
readonly volumeNameBox: Locator;
readonly doneButton: Locator;
readonly closeButton: Locator;
readonly createVolumeButton: Locator;

constructor(page: Page) {
super(page);
this.heading = this.page.getByRole('heading', { name: 'Create a volume' });
this.closeLink = this.page.getByRole('link', { name: 'Close' });
this.volumeNameBox = this.page.getByRole('textbox', { name: 'Volume name' });
this.doneButton = this.page.getByRole('button', { name: 'Done' });
this.closeButton = this.page.getByRole('button', { name: 'Close' });
this.createVolumeButton = this.page.getByRole('button', { name: 'Create' });
}

async createVolume(name: string): Promise<VolumesPage> {
await this.volumeNameBox.fill(name);
await playExpect(this.createVolumeButton).toBeEnabled();
await this.createVolumeButton.click();
await playExpect(this.doneButton).toBeEnabled({ timeout: 30000 });
await this.doneButton.click();
return new VolumesPage(this.page);
}
}
68 changes: 68 additions & 0 deletions tests/playwright/src/model/pages/volume-details-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import type { Locator, Page } from '@playwright/test';
import { expect as playExpect } from '@playwright/test';

import { handleConfirmationDialog } from '../../utility/operations';
import { BasePage } from './base-page';
import { VolumesPage } from './volumes-page';

export class VolumeDetailsPage extends BasePage {
readonly labelName: Locator;
readonly heading: Locator;
readonly closeLink: Locator;
readonly backToVolumesLink: Locator;
readonly volumeName: string;
readonly deleteButton: Locator;

static readonly SUMMARY_TAB = 'Summary';

constructor(page: Page, name: string) {
super(page);
this.volumeName = name;
this.labelName = page.getByLabel('name').and(page.getByText('Volume Details'));
this.heading = page.getByRole('heading', { name: this.volumeName });
this.closeLink = page.getByRole('link', { name: 'Close Details' });
this.backToVolumesLink = page.getByRole('link', { name: 'Go back to Volumes' });
this.deleteButton = page.getByRole('button', { name: 'Delete Volume' });
}

async activateTab(tabName: string): Promise<this> {
const tabItem = this.page.getByRole('link', { name: tabName, exact: true });
await tabItem.waitFor({ state: 'visible', timeout: 2000 });
await tabItem.click();
return this;
}

async getStateLocator(): Promise<Locator> {
await this.activateTab(VolumeDetailsPage.SUMMARY_TAB);
const summaryTable = this.getPage().getByRole('table');
const stateRow = summaryTable.locator('tr:has-text("State")');
const stateCell = stateRow.getByRole('cell').nth(1);
await stateCell.waitFor({ state: 'visible', timeout: 500 });
return stateCell;
}

async deleteVolume(): Promise<VolumesPage> {
await playExpect(this.deleteButton).toBeEnabled();
await this.deleteButton.click();
await handleConfirmationDialog(this.page);
return new VolumesPage(this.page);
}
}
96 changes: 96 additions & 0 deletions tests/playwright/src/model/pages/volumes-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import type { Locator, Page } from '@playwright/test';
import { expect as playExpect } from '@playwright/test';

import { waitUntil, waitWhile } from '../../utility/wait';
import { CreateVolumePage } from './create-volume-page';
import { MainPage } from './main-page';
import { VolumeDetailsPage } from './volume-details-page';

export class VolumesPage extends MainPage {
readonly createVolumeButton: Locator;
readonly pruneVolumesButton: Locator;
readonly collectUsageDataButton: Locator;

constructor(page: Page) {
super(page, 'volumes');
this.createVolumeButton = this.additionalActions.getByRole('button', { name: 'Create' });
this.pruneVolumesButton = this.additionalActions.getByRole('button', { name: 'Prune' });
this.collectUsageDataButton = this.additionalActions.getByRole('button', { name: 'Collect usage data' });
}

async openCreateVolumePage(volumeName: string): Promise<CreateVolumePage> {
const row = await this.getVolumeRowByName(volumeName);
if (row !== undefined) {
throw Error('Volume is already created');
}

await playExpect(this.createVolumeButton).toBeEnabled();
await this.createVolumeButton.click();
return new CreateVolumePage(this.page);
}

async openVolumeDetails(volumeName: string): Promise<VolumeDetailsPage> {
const volumeRow = await this.getVolumeRowByName(volumeName);
if (volumeRow === undefined) {
throw Error(`Volume: ${volumeName} does not exist`);
}
const containerRowName = volumeRow.getByRole('cell').nth(3);
await containerRowName.click();

return new VolumeDetailsPage(this.page, volumeName);
}

async getVolumeRowByName(name: string): Promise<Locator | undefined> {
if (await this.pageIsEmpty()) {
return undefined;
}

try {
const table = await this.getTable();
const rows = await table.getByRole('row').all();

for (let i = rows.length - 1; i >= 0; i--) {
const thirdCell = await rows[i].getByRole('cell').nth(3).getByText(name, { exact: true }).count();
if (thirdCell) {
return rows[i];
}
}
} catch (err) {
console.log(`Exception caught on volumes page with message: ${err}`);
}
return undefined;
}

protected async volumeExists(name: string): Promise<boolean> {
const result = await this.getVolumeRowByName(name);
return result !== undefined;
}

async waitForVolumeExists(name: string): Promise<boolean> {
await waitUntil(async () => await this.volumeExists(name), 3000, 900);
return true;
}

async waitForVolumeDelete(name: string): Promise<boolean> {
await waitWhile(async () => await this.volumeExists(name), 3000, 900);
return true;
}
}
7 changes: 7 additions & 0 deletions tests/playwright/src/model/workbench/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { DashboardPage } from '../pages/dashboard-page';
import { ImagesPage } from '../pages/images-page';
import { PodsPage } from '../pages/pods-page';
import { SettingsBar } from '../pages/settings-bar';
import { VolumesPage } from '../pages/volumes-page';

export class NavigationBar {
readonly page: Page;
Expand Down Expand Up @@ -77,4 +78,10 @@ export class NavigationBar {
}
return settingsBar;
}

async openVolumes(): Promise<VolumesPage> {
await this.volumesLink.waitFor({ state: 'visible', timeout: 3000 });
await this.volumesLink.click({ timeout: 5000 });
return new VolumesPage(this.page);
}
}
75 changes: 75 additions & 0 deletions tests/playwright/src/specs/volume-smoke.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import type { Page } from '@playwright/test';
import { expect as playExpect } from '@playwright/test';
import { afterAll, beforeAll, beforeEach, describe, test } from 'vitest';

import { WelcomePage } from '../model/pages/welcome-page';
import { NavigationBar } from '../model/workbench/navigation';
import { PodmanDesktopRunner } from '../runner/podman-desktop-runner';
import type { RunnerTestContext } from '../testContext/runner-test-context';

let pdRunner: PodmanDesktopRunner;
let page: Page;
let navBar: NavigationBar;

beforeAll(async () => {
pdRunner = new PodmanDesktopRunner();
page = await pdRunner.start();
pdRunner.setVideoAndTraceName('volume-e2e');

const welcomePage = new WelcomePage(page);
await welcomePage.handleWelcomePage(true);
navBar = new NavigationBar(page);
});

afterAll(async () => {
await pdRunner.close();
});

beforeEach<RunnerTestContext>(async ctx => {
ctx.pdRunner = pdRunner;
});

const volumeName = 'e2eVolume';

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('Delete volume through details page', async () => {
let volumesPage = await navBar.openVolumes();
await playExpect(volumesPage.heading).toBeVisible();

const volumeRow = await volumesPage.getVolumeRowByName(volumeName);
playExpect(volumeRow).not.toBeUndefined();

const volumeDetails = await volumesPage.openVolumeDetails(volumeName);
volumesPage = await volumeDetails.deleteVolume();

await playExpect.poll(async () => await volumesPage.waitForVolumeDelete(volumeName)).toBeTruthy();
});
});

0 comments on commit dfbf936

Please sign in to comment.