Skip to content

Commit

Permalink
chore(tests): refactor bootc tests
Browse files Browse the repository at this point in the history
Signed-off-by: Vladimir Lazar <[email protected]>
  • Loading branch information
cbr7 committed May 17, 2024
1 parent c505e5e commit 5032ae2
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 84 deletions.
77 changes: 41 additions & 36 deletions tests/playwright/src/bootc-extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ import { RunnerTestContext } from '@podman-desktop/tests-playwright';
import * as path from 'node:path';
import * as os from 'node:os';
import { BootcPage } from './model/bootc-page';
import { ArchitectureType } from '@podman-desktop/tests-playwright';

let pdRunner: PodmanDesktopRunner;
let page: Page;
let webview: Page;
let navBar: NavigationBar;
let extensionInstalled = false;
const imageName = 'quay.io/centos-bootc/centos-bootc';
const imageTag = 'stream9';
const extensionName = 'bootc';
const extensionLabel = 'redhat.bootc';
const containerFilePath = path.resolve(__dirname, '..', 'resources', 'bootable-containerfile');
Expand All @@ -61,6 +63,8 @@ beforeAll(async () => {
afterAll(async () => {
try {
await deleteImage(page, imageName);
} catch (error) {
console.log(`Error deleting image: ${error}`);
} finally {
await pdRunner.close();
}
Expand Down Expand Up @@ -94,42 +98,43 @@ describe('BootC Extension', async () => {
200000,
);

test('Build bootc image from containerfile', async () => {
let imagesPage = await navBar.openImages();
await playExpect(imagesPage.heading).toBeVisible();

const buildImagePage = await imagesPage.openBuildImage();
await playExpect(buildImagePage.heading).toBeVisible();

imagesPage = await buildImagePage.buildImage(`${imageName}:stream9`, containerFilePath, contextDirectory);
await playExpect.poll(async () => await imagesPage.waitForImageExists(imageName)).toBeTruthy();
}, 150000);

test.skipIf(isLinux).each([
['QCOW2', 'ARM64'],
['QCOW2', 'AMD64'],
['AMI', 'ARM64'],
['AMI', 'AMD64'],
['RAW', 'ARM64'],
['RAW', 'AMD64'],
['ISO', 'ARM64'],
['ISO', 'AMD64'],
])(
'Building bootable image type: %s for architecture: %s',
async (type, architecture) => {
const imagesPage = await navBar.openImages();
await playExpect(imagesPage.heading).toBeVisible();

const imageDetailPage = await imagesPage.openImageDetails(imageName);
await playExpect(imageDetailPage.heading).toBeVisible();

const pathToStore = path.resolve(__dirname, '..', 'output', 'images', `${type}-${architecture}`);
[page, webview] = await handleWebview(imageDetailPage);
const bootcPAge = new BootcPage(page, webview);
const result = await bootcPAge.buildDiskImage(`${imageName}:stream9`, pathToStore, type, architecture);
playExpect(result).toBeTruthy();
describe.each([ArchitectureType.ARM64, ArchitectureType.AMD64])(
'Bootc images for architecture: %s',
async architecture => {
test('Build bootc image from containerfile', async () => {
let imagesPage = await navBar.openImages();
await playExpect(imagesPage.heading).toBeVisible();

const buildImagePage = await imagesPage.openBuildImage();
await playExpect(buildImagePage.heading).toBeVisible();

imagesPage = await buildImagePage.buildImage(
`${imageName}:${imageTag}`,
containerFilePath,
contextDirectory,
architecture,
);
await playExpect.poll(async () => await imagesPage.waitForImageExists(imageName)).toBeTruthy();
}, 150000);

test.skipIf(isLinux).each(['QCOW2', 'AMI', 'RAW', 'VMDK', 'ISO'])(
`Building bootable image type: %s`,
async type => {
const imagesPage = await navBar.openImages();
await playExpect(imagesPage.heading).toBeVisible();

const imageDetailPage = await imagesPage.openImageDetails(imageName);
await playExpect(imageDetailPage.heading).toBeVisible();

const pathToStore = path.resolve(__dirname, '..', 'tests', 'output', 'images', `${type}-${architecture}`);
[page, webview] = await handleWebview(imageDetailPage);
const bootcPAge = new BootcPage(page, webview);
const result = await bootcPAge.buildDiskImage(`${imageName}:${imageTag}`, pathToStore, type, architecture);
playExpect(result).toBeTruthy();
},
350000,
);
},
350000,
);

test('Remove bootc extension through Settings', async () => {
Expand All @@ -154,7 +159,7 @@ async function handleWebview(imageDetailsPage: ImageDetailsPage): Promise<[Page,
await imageDetailsPage.actionsButton.click();
await playExpect(imageDetailsPage.buildDiskImageButton).toBeEnabled();
await imageDetailsPage.buildDiskImageButton.click();
await page.waitForTimeout(5000);
await page.waitForTimeout(2000);

const webView = page.getByRole('document', { name: 'Bootable Containers' });
await playExpect(webView).toBeVisible();
Expand Down
130 changes: 82 additions & 48 deletions tests/playwright/src/model/bootc-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import type { Locator, Page } from '@playwright/test';
import { expect as playExpect } from '@playwright/test';
import { waitUntil, waitWhile } from '@podman-desktop/tests-playwright';
import { ArchitectureType } from '@podman-desktop/tests-playwright';

export class BootcPage {
readonly page: Page;
Expand All @@ -33,6 +35,10 @@ export class BootcPage {
readonly arm64Button: Locator;
readonly buildButton: Locator;
readonly imageSelect: Locator;
readonly goBackButton: Locator;
readonly rowGroup: Locator;
readonly latestBuiltImage: Locator;
readonly getCurrentStatusOfLatestBuildImage: Locator;

constructor(page: Page, webview: Page) {
this.page = page;
Expand All @@ -48,9 +54,18 @@ export class BootcPage {
this.amd64Button = webview.locator('label[for="amd64"]');
this.arm64Button = webview.locator('label[for="arm64"]');
this.buildButton = webview.getByRole('button', { name: 'Build' });
this.goBackButton = webview.getByRole('button', { name: 'Go Back' });
this.rowGroup = webview.getByRole('rowgroup').nth(1);
this.latestBuiltImage = this.rowGroup.getByRole('row').first();
this.getCurrentStatusOfLatestBuildImage = this.latestBuiltImage.getByRole('status');
}

async buildDiskImage(imageName: string, pathToStore: string, type: string, architecture: string): Promise<boolean> {
async buildDiskImage(
imageName: string,
pathToStore: string,
type: string,
architecture: ArchitectureType,
): Promise<boolean> {
let result = false;

if (await this.buildButton.isEnabled()) {
Expand All @@ -60,57 +75,57 @@ export class BootcPage {
await playExpect(this.buildButton).toBeDisabled();
await this.imageSelect.selectOption({ label: imageName });

try {
await this.outputFolderPath.fill(pathToStore);
await this.uncheckedAllCheckboxes();

switch (type.toLocaleLowerCase()) {
case 'raw':
await this.rawCheckbox.check();
break;
case 'qcow2':
await this.qcow2Checkbox.check();
break;
case 'iso':
await this.isoCheckbox.check();
break;
case 'vmdk':
await this.vmdkCheckbox.check();
break;
case 'ami':
await this.amiCheckbox.check();
break;
default:
throw new Error(`Unknown type: ${type}`);
}

switch (architecture.toLocaleLowerCase()) {
case 'amd64':
await playExpect(this.amd64Button).toBeEnabled();
await this.amd64Button.click();
break;
case 'arm64':
await playExpect(this.arm64Button).toBeEnabled();
await this.arm64Button.click();
break;
default:
throw new Error(`Unknown architecture: ${architecture}`);
}

await playExpect(this.buildButton).toBeEnabled();
await this.buildButton.click();
await this.outputFolderPath.fill(pathToStore);
await this.uncheckedAllCheckboxes();

const dialogLocator = this.page.getByRole('dialog', { name: 'Bootable Container', exact: true });
await playExpect.poll(async () => (await dialogLocator.count()) > 0, { timeout: 340000 }).toBeTruthy();
switch (type.toLocaleLowerCase()) {
case 'raw':
await this.rawCheckbox.check();
break;
case 'qcow2':
await this.qcow2Checkbox.check();
break;
case 'iso':
await this.isoCheckbox.check();
break;
case 'vmdk':
await this.vmdkCheckbox.check();
break;
case 'ami':
await this.amiCheckbox.check();
break;
default:
throw new Error(`Unknown type: ${type}`);
}

const dialogMessageLocator = this.page.getByLabel('Dialog Message');
result = (await dialogMessageLocator.innerText()).includes('Success!');
} finally {
const okButtonLocator = this.page.getByRole('button', { name: 'OK' });
await playExpect(okButtonLocator).toBeEnabled();
await okButtonLocator.click();
switch (architecture) {
case ArchitectureType.AMD64:
await playExpect(this.amd64Button).toBeEnabled();
await this.amd64Button.click();
break;
case ArchitectureType.ARM64:
await playExpect(this.arm64Button).toBeEnabled();
await this.arm64Button.click();
break;
default:
throw new Error(`Unknown architecture: ${architecture}`);
}

await playExpect(this.buildButton).toBeEnabled();
await this.buildButton.click();

await playExpect(this.goBackButton).toBeEnabled();
await this.goBackButton.click();

await this.waitUntilCurrentBuildIsFinished();
if ((await this.getCurrentStatusOfLatestEntry()) === 'error') return false;

const dialogMessageLocator = this.page.getByLabel('Dialog Message');
result = (await dialogMessageLocator.innerText()).includes('Success!');
const okButtonLocator = this.page.getByRole('button', { name: 'OK' });
await playExpect(okButtonLocator).toBeEnabled();
await okButtonLocator.click();

return result;
}

Expand All @@ -121,4 +136,23 @@ export class BootcPage {
await this.vmdkCheckbox.uncheck();
await this.amiCheckbox.uncheck();
}

async getCurrentStatusOfLatestEntry(): Promise<string> {
const status = await this.getCurrentStatusOfLatestBuildImage.getAttribute('title');

if (status) return status;
return '';
}

async waitUntilCurrentBuildIsFinished(): Promise<void> {
await waitUntil(
async () =>
(await this.getCurrentStatusOfLatestEntry()).toLocaleLowerCase() === 'error' ||
(await this.getCurrentStatusOfLatestEntry()).toLocaleLowerCase() === 'success',
340000,
2500,
true,
`Build didn't finish before timeout!`,
);
}
}

0 comments on commit 5032ae2

Please sign in to comment.