mirror of
https://github.com/podman-desktop/podman-desktop
synced 2026-05-24 10:18:53 +00:00
chore(test): enhance Detail Page Locators, ARIA Labels, and Add Navigation Tests (#8196)
* refactor: add aria labels and roles Signed-off-by: Anton Misskii <amisskii@redhat.com> * chore: change resources details pages Signed-off-by: Anton Misskii <amisskii@redhat.com> * chore(test): add navigation tests Signed-off-by: Anton Misskii <amisskii@redhat.com> * refactor: unit tests refactoring Signed-off-by: Anton Misskii <amisskii@redhat.com> * chore: use playExpect instead of waitFor Signed-off-by: Anton Misskii <amisskii@redhat.com>
This commit is contained in:
parent
596e1c9d0c
commit
528b2c7933
17 changed files with 178 additions and 131 deletions
|
|
@ -51,14 +51,14 @@ test('Expect title is defined', async () => {
|
|||
expect(titleElement).toHaveTextContent(title);
|
||||
});
|
||||
|
||||
test('Expect name is defined', async () => {
|
||||
test('Expect page name is defined', async () => {
|
||||
const name = 'My Dummy Name';
|
||||
currentPage.set({ name: name, path: '/' } as TinroBreadcrumb);
|
||||
render(DetailsPage, {
|
||||
title: 'No Title',
|
||||
});
|
||||
|
||||
const nameElement = screen.getByLabelText('name');
|
||||
const nameElement = screen.getByLabelText('Page Name');
|
||||
expect(nameElement).toBeInTheDocument();
|
||||
expect(nameElement).toHaveTextContent(name);
|
||||
});
|
||||
|
|
@ -71,7 +71,7 @@ test('Expect backlink is defined', async () => {
|
|||
title: 'No Title',
|
||||
});
|
||||
|
||||
const backElement = screen.getByLabelText('back');
|
||||
const backElement = screen.getByLabelText('Back');
|
||||
expect(backElement).toBeInTheDocument();
|
||||
expect(backElement).toHaveTextContent(backName);
|
||||
|
||||
|
|
|
|||
|
|
@ -52,14 +52,14 @@ test('Expect to see empty screen', async () => {
|
|||
expect(titleElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Expect name is defined', async () => {
|
||||
test('Expect page name is defined', async () => {
|
||||
const name = 'My Dummy Name';
|
||||
currentPage.set({ name: name, path: '/' } as TinroBreadcrumb);
|
||||
render(EngineFormPage, {
|
||||
title: 'No Title',
|
||||
});
|
||||
|
||||
const nameElement = screen.getByLabelText('name');
|
||||
const nameElement = screen.getByLabelText('Page Name');
|
||||
expect(nameElement).toBeInTheDocument();
|
||||
expect(nameElement).toHaveTextContent(name);
|
||||
});
|
||||
|
|
@ -72,7 +72,7 @@ test('Expect backlink is defined', async () => {
|
|||
title: 'No Title',
|
||||
});
|
||||
|
||||
const backElement = screen.getByLabelText('back');
|
||||
const backElement = screen.getByLabelText('Back');
|
||||
expect(backElement).toBeInTheDocument();
|
||||
expect(backElement).toHaveTextContent(backName);
|
||||
|
||||
|
|
|
|||
|
|
@ -51,14 +51,14 @@ test('Expect title is defined', async () => {
|
|||
expect(titleElement).toHaveTextContent(title);
|
||||
});
|
||||
|
||||
test('Expect name is defined', async () => {
|
||||
test('Expect page name is defined', async () => {
|
||||
const name = 'My Dummy Name';
|
||||
currentPage.set({ name: name, path: '/' } as TinroBreadcrumb);
|
||||
render(FormPage, {
|
||||
title: 'No Title',
|
||||
});
|
||||
|
||||
const nameElement = screen.getByLabelText('name');
|
||||
const nameElement = screen.getByLabelText('Page Name');
|
||||
expect(nameElement).toBeInTheDocument();
|
||||
expect(nameElement).toHaveTextContent(name);
|
||||
});
|
||||
|
|
@ -71,7 +71,7 @@ test('Expect backlink is defined', async () => {
|
|||
title: 'No Title',
|
||||
});
|
||||
|
||||
const backElement = screen.getByLabelText('back');
|
||||
const backElement = screen.getByLabelText('Back');
|
||||
expect(backElement).toBeInTheDocument();
|
||||
expect(backElement).toHaveTextContent(backName);
|
||||
|
||||
|
|
|
|||
|
|
@ -39,14 +39,14 @@ test('Expect title is defined', async () => {
|
|||
expect(titleElement).toHaveTextContent(title);
|
||||
});
|
||||
|
||||
test('Expect name is defined', async () => {
|
||||
test('Expect page name is defined', async () => {
|
||||
const name = 'My Dummy Name';
|
||||
render(DetailsPage, {
|
||||
title: 'No Title',
|
||||
breadcrumbRightPart: name,
|
||||
});
|
||||
|
||||
const nameElement = screen.getByLabelText('name');
|
||||
const nameElement = screen.getByLabelText('Page Name');
|
||||
expect(nameElement).toBeInTheDocument();
|
||||
expect(nameElement).toHaveTextContent(name);
|
||||
});
|
||||
|
|
@ -60,7 +60,7 @@ test('Expect backlink is defined', async () => {
|
|||
onbreadcrumbClick: breadcrumbClickMock,
|
||||
});
|
||||
|
||||
const backElement = screen.getByLabelText('back');
|
||||
const backElement = screen.getByLabelText('Back');
|
||||
expect(backElement).toBeInTheDocument();
|
||||
expect(backElement).toHaveTextContent(backName);
|
||||
|
||||
|
|
|
|||
|
|
@ -44,21 +44,21 @@ test('Expect no backlink or close is defined', async () => {
|
|||
title: 'No Title',
|
||||
});
|
||||
|
||||
const backElement = screen.queryByLabelText('back');
|
||||
const backElement = screen.queryByLabelText('Back');
|
||||
expect(backElement).not.toBeInTheDocument();
|
||||
|
||||
const closeElement = screen.queryByTitle('Close');
|
||||
expect(closeElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Expect name is defined', async () => {
|
||||
test('Expect page name is defined', async () => {
|
||||
const name = 'My Dummy Name';
|
||||
render(FormPage, {
|
||||
title: 'No Title',
|
||||
breadcrumbRightPart: name,
|
||||
});
|
||||
|
||||
const nameElement = screen.getByLabelText('name');
|
||||
const nameElement = screen.getByLabelText('Page Name');
|
||||
expect(nameElement).toBeInTheDocument();
|
||||
expect(nameElement).toHaveTextContent(name);
|
||||
});
|
||||
|
|
@ -74,7 +74,7 @@ test('Expect backlink is defined', async () => {
|
|||
onbreadcrumbClick: breadcrumbClickMock,
|
||||
});
|
||||
|
||||
const backElement = screen.getByLabelText('back');
|
||||
const backElement = screen.getByLabelText('Back');
|
||||
expect(backElement).toBeInTheDocument();
|
||||
expect(backElement).toHaveTextContent(backName);
|
||||
|
||||
|
|
|
|||
|
|
@ -39,38 +39,26 @@ test('Expect title is defined', async () => {
|
|||
expect(titleElement).toHaveTextContent(title);
|
||||
});
|
||||
|
||||
test('Expect name is defined', async () => {
|
||||
const name = 'My Dummy Name';
|
||||
render(Page, {
|
||||
title: 'No Title',
|
||||
breadcrumbRightPart: name,
|
||||
});
|
||||
|
||||
const nameElement = screen.getByLabelText('name');
|
||||
expect(nameElement).toBeInTheDocument();
|
||||
expect(nameElement).toHaveTextContent(name);
|
||||
});
|
||||
|
||||
test('Expect no backlink or close is defined', async () => {
|
||||
render(Page, {
|
||||
title: 'No Title',
|
||||
});
|
||||
|
||||
const backElement = screen.queryByLabelText('back');
|
||||
const backElement = screen.queryByLabelText('Back');
|
||||
expect(backElement).not.toBeInTheDocument();
|
||||
|
||||
const closeElement = screen.queryByTitle('Close');
|
||||
expect(closeElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Expect name is defined', async () => {
|
||||
test('Expect page name is defined', async () => {
|
||||
const name = 'My Dummy Name';
|
||||
render(Page, {
|
||||
title: 'No Title',
|
||||
breadcrumbRightPart: name,
|
||||
});
|
||||
|
||||
const nameElement = screen.getByLabelText('name');
|
||||
const nameElement = screen.getByLabelText('Page Name');
|
||||
expect(nameElement).toBeInTheDocument();
|
||||
expect(nameElement).toHaveTextContent(name);
|
||||
});
|
||||
|
|
@ -86,7 +74,7 @@ test('Expect backlink is defined', async () => {
|
|||
onbreadcrumbClick: breadcrumbClickMock,
|
||||
});
|
||||
|
||||
const backElement = screen.getByLabelText('back');
|
||||
const backElement = screen.getByLabelText('Back');
|
||||
expect(backElement).toBeInTheDocument();
|
||||
expect(backElement).toHaveTextContent(backName);
|
||||
|
||||
|
|
|
|||
|
|
@ -37,17 +37,20 @@ function handleKeydown(e: KeyboardEvent): void {
|
|||
<svelte:window on:keydown={handleKeydown} />
|
||||
|
||||
<div class="flex flex-col w-full h-full shadow-pageheader bg-[var(--pd-content-bg)]">
|
||||
<div class="flex flex-row w-full h-fit px-5 pt-4 pb-2">
|
||||
<div class="flex flex-row w-full h-fit px-5 pt-4 pb-2" aria-label="Header" role="region">
|
||||
<div class="flex flex-col w-full h-fit">
|
||||
{#if showBreadcrumb}
|
||||
<div class="flex flew-row items-center text-sm text-[var(--pd-content-breadcrumb)]">
|
||||
<div
|
||||
class="flex flew-row items-center text-sm text-[var(--pd-content-breadcrumb)]"
|
||||
role="navigation"
|
||||
aria-label="Breadcrumb">
|
||||
{#if breadcrumbLeftPart}
|
||||
<Link class="text-sm" aria-label="back" on:click={onbreadcrumbClick} title={breadcrumbTitle}
|
||||
<Link class="text-sm" aria-label="Back" on:click={onbreadcrumbClick} title={breadcrumbTitle}
|
||||
>{breadcrumbLeftPart}</Link>
|
||||
{/if}
|
||||
{#if breadcrumbRightPart}
|
||||
<div class="mx-2">></div>
|
||||
<div class="grow font-extralight" aria-label="name">{breadcrumbRightPart}</div>
|
||||
<div class="grow font-extralight" aria-label="Page Name">{breadcrumbRightPart}</div>
|
||||
{/if}
|
||||
{#if hasClose}
|
||||
<CloseButton class="justify-self-end text-lg" on:click={onclose} />
|
||||
|
|
@ -76,7 +79,7 @@ function handleKeydown(e: KeyboardEvent): void {
|
|||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-nowrap justify-self-end pl-3 space-x-2">
|
||||
<div class="flex flex-nowrap justify-self-end pl-3 space-x-2" aria-label="Control Actions" role="group">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
<div class="relative">
|
||||
|
|
@ -94,8 +97,10 @@ function handleKeydown(e: KeyboardEvent): void {
|
|||
{#if inProgress}
|
||||
<LinearProgress />
|
||||
{/if}
|
||||
<slot name="tabs" />
|
||||
<div class="h-full min-h-0">
|
||||
<div class="flex flex-row px-2 border-b border-[var(--pd-content-divider)]" aria-label="Tabs" role="region">
|
||||
<slot name="tabs" />
|
||||
</div>
|
||||
<div class="h-full min-h-0" aria-label="Tab Content" role="region">
|
||||
<slot name="content" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export * from './model/pages/container-details-page';
|
|||
export * from './model/pages/containers-page';
|
||||
export * from './model/pages/create-pod-page';
|
||||
export * from './model/pages/dashboard-page';
|
||||
export * from './model/pages/details-page';
|
||||
export * from './model/pages/extension-card-page';
|
||||
export * from './model/pages/extension-catalog-card-page';
|
||||
export * from './model/pages/extension-details-page';
|
||||
|
|
|
|||
|
|
@ -21,15 +21,10 @@ import { expect as playExpect } from '@playwright/test';
|
|||
|
||||
import { handleConfirmationDialog } from '../../utility/operations';
|
||||
import { ContainerState } from '../core/states';
|
||||
import { BasePage } from './base-page';
|
||||
import { ContainersPage } from './containers-page';
|
||||
import { DetailsPage } from './details-page';
|
||||
|
||||
export class ContainerDetailsPage extends BasePage {
|
||||
readonly labelName: Locator;
|
||||
readonly heading: Locator;
|
||||
readonly closeLink: Locator;
|
||||
readonly backToContainersLink: Locator;
|
||||
readonly containerName: string;
|
||||
export class ContainerDetailsPage extends DetailsPage {
|
||||
readonly stopButton: Locator;
|
||||
readonly deleteButton: Locator;
|
||||
|
||||
|
|
@ -40,25 +35,14 @@ export class ContainerDetailsPage extends BasePage {
|
|||
static readonly INSPECT_TAB = 'Inspect';
|
||||
|
||||
constructor(page: Page, name: string) {
|
||||
super(page);
|
||||
this.containerName = name;
|
||||
this.labelName = page.getByLabel('name').and(page.getByText('Container Details'));
|
||||
this.heading = page.getByRole('heading', { name: this.containerName });
|
||||
this.closeLink = page.getByRole('link', { name: 'Close Details' });
|
||||
this.backToContainersLink = page.getByRole('link', { name: 'Go back to Containers' });
|
||||
this.stopButton = this.page.getByRole('button').and(this.page.getByLabel('Stop Container'));
|
||||
this.deleteButton = this.page.getByRole('button').and(this.page.getByLabel('Delete Container'));
|
||||
}
|
||||
|
||||
async activateTab(tabName: string): Promise<void> {
|
||||
const tabItem = this.page.getByRole('link', { name: tabName, exact: true });
|
||||
await playExpect(tabItem).toBeVisible();
|
||||
await tabItem.click();
|
||||
super(page, name);
|
||||
this.stopButton = this.controlActions.getByRole('button').and(this.page.getByLabel('Stop Container'));
|
||||
this.deleteButton = this.controlActions.getByRole('button').and(this.page.getByLabel('Delete Container'));
|
||||
}
|
||||
|
||||
async getStateLocator(): Promise<Locator> {
|
||||
await this.activateTab(ContainerDetailsPage.SUMMARY_TAB);
|
||||
const summaryTable = this.getPage().getByRole('table');
|
||||
const summaryTable = this.tabContent.getByRole('table');
|
||||
const stateRow = summaryTable.locator('tr:has-text("State")');
|
||||
const stateCell = stateRow.getByRole('cell').nth(1);
|
||||
await playExpect(stateCell).toBeVisible();
|
||||
|
|
@ -93,7 +77,7 @@ export class ContainerDetailsPage extends BasePage {
|
|||
|
||||
async getContainerPort(): Promise<string> {
|
||||
await this.activateTab(ContainerDetailsPage.SUMMARY_TAB);
|
||||
const summaryTable = this.getPage().getByRole('table');
|
||||
const summaryTable = this.tabContent.getByRole('table');
|
||||
const portsRow = summaryTable.locator('tr:has-text("Ports")');
|
||||
const portsCell = portsRow.getByRole('cell').nth(1);
|
||||
await playExpect(portsCell).toBeVisible();
|
||||
|
|
|
|||
56
tests/playwright/src/model/pages/details-page.ts
Normal file
56
tests/playwright/src/model/pages/details-page.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**********************************************************************
|
||||
* 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 { expect as playExpect, type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { BasePage } from './base-page';
|
||||
|
||||
export abstract class DetailsPage extends BasePage {
|
||||
readonly header: Locator;
|
||||
readonly tabs: Locator;
|
||||
readonly tabContent: Locator;
|
||||
readonly controlActions: Locator;
|
||||
readonly closeButton: Locator;
|
||||
readonly backLink: Locator;
|
||||
readonly pageName: Locator;
|
||||
readonly resourceName: string;
|
||||
readonly heading: Locator;
|
||||
readonly breadcrumb: Locator;
|
||||
|
||||
constructor(page: Page, resourceName: string) {
|
||||
super(page);
|
||||
this.resourceName = resourceName;
|
||||
|
||||
this.tabContent = page.getByRole('region', { name: 'Tab Content' });
|
||||
this.header = page.getByRole('region', { name: 'Header' });
|
||||
this.tabs = page.getByRole('region', { name: 'Tabs' });
|
||||
this.breadcrumb = this.header.getByRole('navigation', { name: 'Breadcrumb' });
|
||||
this.controlActions = this.header.getByRole('group', { name: 'Control Actions' });
|
||||
this.heading = this.header.getByRole('heading', { name: this.resourceName });
|
||||
this.closeButton = this.breadcrumb.getByRole('button', { name: 'Close' });
|
||||
this.backLink = this.breadcrumb.getByRole('link', { name: 'Back' });
|
||||
this.pageName = this.breadcrumb.getByRole('region', { name: 'Page Name' });
|
||||
}
|
||||
|
||||
async activateTab(tabName: string): Promise<this> {
|
||||
const tabItem = this.tabs.getByRole('link', { name: tabName, exact: true });
|
||||
await playExpect(tabItem).toBeVisible();
|
||||
await tabItem.click();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -21,23 +21,18 @@ import { expect as playExpect } from '@playwright/test';
|
|||
|
||||
import type { PodmanDesktopRunner } from '../../runner/podman-desktop-runner';
|
||||
import { handleConfirmationDialog } from '../../utility/operations';
|
||||
import { BasePage } from './base-page';
|
||||
import { DetailsPage } from './details-page';
|
||||
import { ImageEditPage } from './image-edit-page';
|
||||
import { ImagesPage } from './images-page';
|
||||
import { RunImagePage } from './run-image-page';
|
||||
|
||||
export class ImageDetailsPage extends BasePage {
|
||||
readonly name: Locator;
|
||||
readonly imageName: string;
|
||||
readonly heading: Locator;
|
||||
export class ImageDetailsPage extends DetailsPage {
|
||||
readonly runImageButton: Locator;
|
||||
readonly deleteButton: Locator;
|
||||
readonly editButton: Locator;
|
||||
readonly summaryTab: Locator;
|
||||
readonly historyTab: Locator;
|
||||
readonly inspectTab: Locator;
|
||||
readonly closeLink: Locator;
|
||||
readonly backToImagesLink: Locator;
|
||||
readonly actionsButton: Locator;
|
||||
readonly buildDiskImageButton: Locator;
|
||||
readonly saveImagebutton: Locator;
|
||||
|
|
@ -46,21 +41,16 @@ export class ImageDetailsPage extends BasePage {
|
|||
readonly browseButton: Locator;
|
||||
|
||||
constructor(page: Page, name: string) {
|
||||
super(page);
|
||||
this.name = page.getByLabel('name').and(page.getByText('Image Details'));
|
||||
this.imageName = name;
|
||||
this.heading = page.getByRole('heading', { name: this.imageName });
|
||||
this.runImageButton = page.getByRole('button', { name: 'Run Image' });
|
||||
this.deleteButton = page.getByRole('button', { name: 'Delete Image' });
|
||||
this.editButton = page.getByRole('button', { name: 'Edit Image' });
|
||||
this.summaryTab = page.getByText('Summary');
|
||||
this.historyTab = page.getByText('History');
|
||||
this.inspectTab = page.getByText('Inspect');
|
||||
this.closeLink = page.getByRole('link', { name: 'Close Details' });
|
||||
this.backToImagesLink = page.getByRole('link', { name: 'Go back to Images' });
|
||||
super(page, name);
|
||||
this.runImageButton = this.controlActions.getByRole('button', { name: 'Run Image' });
|
||||
this.deleteButton = this.controlActions.getByRole('button', { name: 'Delete Image' });
|
||||
this.editButton = this.controlActions.getByRole('button', { name: 'Edit Image' });
|
||||
this.summaryTab = this.tabs.getByText('Summary');
|
||||
this.historyTab = this.tabs.getByText('History');
|
||||
this.inspectTab = this.tabs.getByText('Inspect');
|
||||
this.actionsButton = page.getByRole('button', { name: 'kebab menu' });
|
||||
this.buildDiskImageButton = page.getByTitle('Build Disk Image');
|
||||
this.saveImagebutton = page.getByRole('button', { name: 'Save Image', exact: true });
|
||||
this.saveImagebutton = this.controlActions.getByRole('button', { name: 'Save Image', exact: true });
|
||||
this.saveImageInput = page.locator('#input-output-directory');
|
||||
this.confirmSaveImages = page.getByLabel('Save images', { exact: true });
|
||||
this.browseButton = page.getByLabel('Select output folder');
|
||||
|
|
@ -69,13 +59,13 @@ export class ImageDetailsPage extends BasePage {
|
|||
async openRunImage(): Promise<RunImagePage> {
|
||||
await playExpect(this.runImageButton).toBeEnabled({ timeout: 30000 });
|
||||
await this.runImageButton.click();
|
||||
return new RunImagePage(this.page, this.imageName);
|
||||
return new RunImagePage(this.page, this.resourceName);
|
||||
}
|
||||
|
||||
async openEditImage(): Promise<ImageEditPage> {
|
||||
await playExpect(this.editButton).toBeEnabled({ timeout: 30000 });
|
||||
await this.editButton.click();
|
||||
return new ImageEditPage(this.page, this.imageName);
|
||||
return new ImageEditPage(this.page, this.resourceName);
|
||||
}
|
||||
|
||||
async deleteImage(): Promise<ImagesPage> {
|
||||
|
|
|
|||
|
|
@ -22,15 +22,10 @@ import { expect as playExpect } from '@playwright/test';
|
|||
import { handleConfirmationDialog } from '../../utility/operations';
|
||||
import { PodState } from '../core/states';
|
||||
import { NavigationBar } from '../workbench/navigation';
|
||||
import { BasePage } from './base-page';
|
||||
import { DetailsPage } from './details-page';
|
||||
import { PodsPage } from './pods-page';
|
||||
|
||||
export class PodDetailsPage extends BasePage {
|
||||
readonly labelName: Locator;
|
||||
readonly heading: Locator;
|
||||
readonly closeLink: Locator;
|
||||
readonly backToPodsLink: Locator;
|
||||
readonly podName: string;
|
||||
export class PodDetailsPage extends DetailsPage {
|
||||
readonly startButton: Locator;
|
||||
readonly stopButton: Locator;
|
||||
readonly restartButton: Locator;
|
||||
|
|
@ -42,26 +37,19 @@ export class PodDetailsPage extends BasePage {
|
|||
static readonly KUBE_TAB = 'Kube';
|
||||
|
||||
constructor(page: Page, name: string) {
|
||||
super(page);
|
||||
this.podName = name;
|
||||
this.labelName = page.getByLabel('name').and(page.getByText('Pod Details'));
|
||||
this.heading = page.getByRole('heading', { name: this.podName });
|
||||
this.closeLink = page.getByRole('link', { name: 'Close Details' });
|
||||
this.backToPodsLink = page.getByRole('link', { name: 'Go back to Pods' });
|
||||
this.startButton = this.page.getByRole('button').and(this.page.getByLabel('Start Pod', { exact: true }));
|
||||
this.stopButton = this.page.getByRole('button').and(this.page.getByLabel('Stop Pod', { exact: true }));
|
||||
this.restartButton = this.page.getByRole('button').and(this.page.getByLabel('Restart Pod', { exact: true }));
|
||||
this.deleteButton = this.page.getByRole('button').and(this.page.getByLabel('Delete Pod', { exact: true }));
|
||||
}
|
||||
|
||||
async activateTab(tabName: string): Promise<void> {
|
||||
const tabItem = this.page.getByRole('link', { name: tabName });
|
||||
await playExpect(tabItem).toBeEnabled();
|
||||
await tabItem.click();
|
||||
super(page, name);
|
||||
this.startButton = this.controlActions.getByRole('button').and(this.page.getByLabel('Start Pod', { exact: true }));
|
||||
this.stopButton = this.controlActions.getByRole('button').and(this.page.getByLabel('Stop Pod', { exact: true }));
|
||||
this.restartButton = this.controlActions
|
||||
.getByRole('button')
|
||||
.and(this.page.getByLabel('Restart Pod', { exact: true }));
|
||||
this.deleteButton = this.controlActions
|
||||
.getByRole('button')
|
||||
.and(this.page.getByLabel('Delete Pod', { exact: true }));
|
||||
}
|
||||
|
||||
async getState(): Promise<string> {
|
||||
const currentState = await this.page.getByRole('status').getAttribute('title');
|
||||
const currentState = await this.header.getByRole('status').getAttribute('title');
|
||||
for (const state of Object.values(PodState)) {
|
||||
if (currentState === state) return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,39 +20,22 @@ 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 { DetailsPage } from './details-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;
|
||||
export class VolumeDetailsPage extends DetailsPage {
|
||||
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;
|
||||
super(page, name);
|
||||
this.deleteButton = this.controlActions.getByRole('button', { name: 'Delete Volume' });
|
||||
}
|
||||
|
||||
async getStateLocator(): Promise<Locator> {
|
||||
await this.activateTab(VolumeDetailsPage.SUMMARY_TAB);
|
||||
const summaryTable = this.getPage().getByRole('table');
|
||||
const summaryTable = this.tabContent.getByRole('table');
|
||||
const stateRow = summaryTable.locator('tr:has-text("State")');
|
||||
const stateCell = stateRow.getByRole('cell').nth(1);
|
||||
await stateCell.waitFor({ state: 'visible', timeout: 500 });
|
||||
|
|
|
|||
|
|
@ -113,7 +113,20 @@ describe('Verification of container creation workflow', async () => {
|
|||
images = await navigationBar.openImages();
|
||||
playExpect(await images.getCurrentStatusOfImage(imageToPull)).toBe('USED');
|
||||
});
|
||||
test('Test navigation between pages', async () => {
|
||||
const navigationBar = new NavigationBar(page);
|
||||
const containers = await navigationBar.openContainers();
|
||||
|
||||
const containersDetails = await containers.openContainersDetails(containerToRun);
|
||||
await playExpect(containersDetails.heading).toBeVisible();
|
||||
await containersDetails.backLink.click();
|
||||
await playExpect(containers.heading).toBeVisible();
|
||||
|
||||
await containers.openContainersDetails(containerToRun);
|
||||
await playExpect(containersDetails.heading).toBeVisible();
|
||||
await containersDetails.closeButton.click();
|
||||
await playExpect(containers.heading).toBeVisible();
|
||||
});
|
||||
test('Open a container details', async () => {
|
||||
const navigationBar = new NavigationBar(page);
|
||||
const containers = await navigationBar.openContainers();
|
||||
|
|
|
|||
|
|
@ -76,7 +76,18 @@ describe('Image workflow verification', async () => {
|
|||
playExpect(exists, `${helloContainer} image not present in the list of images`).toBeTruthy();
|
||||
playExpect(await updatedImages.getCurrentStatusOfImage(helloContainer)).toBe('UNUSED');
|
||||
});
|
||||
test('Test navigation between pages', async () => {
|
||||
const imagesPage = await navBar.openImages();
|
||||
const imageDetailPage = await imagesPage.openImageDetails(helloContainer);
|
||||
await playExpect(imageDetailPage.heading).toBeVisible();
|
||||
await imageDetailPage.backLink.click();
|
||||
await playExpect(imagesPage.heading).toBeVisible();
|
||||
|
||||
await imagesPage.openImageDetails(helloContainer);
|
||||
await playExpect(imageDetailPage.heading).toBeVisible();
|
||||
await imageDetailPage.closeButton.click();
|
||||
await playExpect(imagesPage.heading).toBeVisible();
|
||||
});
|
||||
test('Check image details', async () => {
|
||||
const imagesPage = await navBar.openImages();
|
||||
const imageDetailPage = await imagesPage.openImageDetails(helloContainer);
|
||||
|
|
|
|||
|
|
@ -145,7 +145,6 @@ describe.skipIf(process.env.GITHUB_ACTIONS && process.env.RUNNER_OS === 'Linux')
|
|||
.poll(async () => await containerDetails.getState(), { timeout: 15000 })
|
||||
.toBe(ContainerState.Running.toLowerCase());
|
||||
});
|
||||
|
||||
test('Podify containers', async () => {
|
||||
const navigationBar = new NavigationBar(page);
|
||||
const containers = await navigationBar.openContainers();
|
||||
|
|
@ -156,7 +155,21 @@ describe.skipIf(process.env.GITHUB_ACTIONS && process.env.RUNNER_OS === 'Linux')
|
|||
const podDetails = await pods.openPodDetails(podToRun);
|
||||
await playExpect.poll(async () => await podDetails.getState(), { timeout: 15000 }).toBe(PodState.Running);
|
||||
}, 90000);
|
||||
test('Test navigation between pages', async () => {
|
||||
const navigationBar = new NavigationBar(page);
|
||||
const pods = await navigationBar.openPods();
|
||||
await playExpect.poll(async () => await pods.podExists(podToRun), { timeout: 10000 }).toBeTruthy();
|
||||
|
||||
const podDetails = await pods.openPodDetails(podToRun);
|
||||
await playExpect(podDetails.heading).toBeVisible();
|
||||
await podDetails.backLink.click();
|
||||
await playExpect(pods.heading).toBeVisible();
|
||||
|
||||
await pods.openPodDetails(podToRun);
|
||||
await playExpect(podDetails.heading).toBeVisible();
|
||||
await podDetails.closeButton.click();
|
||||
await playExpect(pods.heading).toBeVisible();
|
||||
});
|
||||
test('Checking pod details', async () => {
|
||||
const navigationBar = new NavigationBar(page);
|
||||
const pods = await navigationBar.openPods();
|
||||
|
|
|
|||
|
|
@ -61,7 +61,22 @@ describe('Volume workflow verification', async () => {
|
|||
|
||||
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();
|
||||
const volumeRow = await volumesPage.getVolumeRowByName(volumeName);
|
||||
playExpect(volumeRow).not.toBeUndefined();
|
||||
|
||||
const volumeDetails = await volumesPage.openVolumeDetails(volumeName);
|
||||
await playExpect(volumeDetails.heading).toBeVisible();
|
||||
await volumeDetails.backLink.click();
|
||||
await playExpect(volumesPage.heading).toBeVisible();
|
||||
|
||||
await volumesPage.openVolumeDetails(volumeName);
|
||||
await playExpect(volumeDetails.heading).toBeVisible();
|
||||
await volumeDetails.closeButton.click();
|
||||
await playExpect(volumesPage.heading).toBeVisible();
|
||||
});
|
||||
test('Delete volume through details page', async () => {
|
||||
let volumesPage = await navBar.openVolumes();
|
||||
await playExpect(volumesPage.heading).toBeVisible();
|
||||
|
|
|
|||
Loading…
Reference in a new issue