debian-image-builder-frontend/playwright/test.spec.ts
Gianluca Zuccarelli 3a83a14720 BlueprintCard: fix name truncation
This might be an issue with the pf6 truncate component. Since we're not
really using the popover, we can just use vanilla js to truncate the
string rather than use the Truncate component. We can match the
behaviour of the component by also splitting on 24 characters.

https://github.com/patternfly/patternfly-react/issues/11964
2025-08-21 13:44:58 +00:00

314 lines
12 KiB
TypeScript

import { readFileSync } from 'node:fs';
import TOML from '@ltd/j-toml';
import { expect, test } from '@playwright/test';
import { v4 as uuidv4 } from 'uuid';
import { closePopupsIfExist, isHosted } from './helpers/helpers';
import { ensureAuthenticated } from './helpers/login';
import { ibFrame, navigateToLandingPage } from './helpers/navHelpers';
test.describe.serial('test', () => {
const blueprintName = uuidv4();
test('create blueprint', async ({ page }) => {
await ensureAuthenticated(page);
await closePopupsIfExist(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);
frame.getByRole('heading', { name: 'Images About image builder' });
frame.getByRole('heading', { name: 'Blueprints' });
await frame.getByTestId('blueprints-create-button').click();
frame.getByRole('heading', { name: 'Image output' });
await frame
.getByRole('checkbox', { name: /Virtualization guest image/i })
.click();
await frame.getByRole('button', { name: 'Next', exact: true }).click();
if (isHosted()) {
frame.getByRole('heading', {
name: 'Register systems using this image',
});
await page.getByRole('radio', { name: /Register later/i }).click();
await frame.getByRole('button', { name: 'Next', exact: true }).click();
}
frame.getByRole('heading', { name: 'Compliance' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
frame.getByRole('heading', { name: 'File system configuration' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
if (isHosted()) {
frame.getByRole('heading', { name: 'Repository snapshot' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
frame.getByRole('heading', { name: 'Custom repositories' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
}
frame.getByRole('heading', { name: 'Additional packages' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
frame.getByRole('heading', { name: 'Users' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
frame.getByRole('heading', { name: 'Timezone' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
frame.getByRole('heading', { name: 'Locale' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
frame.getByRole('heading', { name: 'Hostname' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
frame.getByRole('heading', { name: 'Kernel' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
frame.getByRole('heading', { name: 'Firewall' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
frame.getByRole('heading', { name: 'Systemd services' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
if (isHosted()) {
frame.getByRole('heading', { name: 'Ansible Automation Platform' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
}
if (isHosted()) {
frame.getByRole('heading', { name: 'First boot configuration' });
await frame.getByRole('button', { name: 'Next', exact: true }).click();
}
frame.getByRole('heading', { name: 'Details' });
await frame.getByTestId('blueprint').fill(blueprintName);
await expect(frame.getByTestId('blueprint')).toHaveValue(blueprintName);
await frame.getByRole('button', { name: 'Next', exact: true }).click();
await frame.getByRole('button', { name: 'Create blueprint' }).click();
await frame.getByTestId('close-button-saveandbuild-modal').click();
await frame.getByRole('button', { name: 'Create blueprint' }).click();
await expect(
frame.locator('.pf-v6-c-card__title-text').getByText(
// if the name is too long, the blueprint card will have a truncated name.
blueprintName.length > 24
? blueprintName.slice(0, 24) + '...'
: blueprintName,
),
).toBeVisible();
});
test('edit blueprint', async ({ page }) => {
await ensureAuthenticated(page);
await closePopupsIfExist(page);
// package searching is really slow the first time in cockpit
if (!isHosted()) {
test.setTimeout(300000);
}
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);
await frame
.getByRole('textbox', { name: 'Search input' })
.fill(blueprintName);
// the clickable blueprint cards are a bit awkward, so use the
// button's id instead
await frame.locator(`button[id="${blueprintName}"]`).click();
await frame.getByRole('button', { name: 'Edit blueprint' }).click();
await frame.getByRole('button', { name: 'Additional packages' }).click();
await frame
.getByTestId('packages-search-input')
.locator('input')
.fill('osbuild-composer');
frame.getByTestId('packages-table').getByText('Searching');
frame.getByRole('gridcell', { name: 'osbuild-composer' }).first();
await frame.getByRole('checkbox', { name: 'Select row 0' }).check();
await frame.getByRole('button', { name: 'Review and finish' }).click();
await frame.getByRole('button', { name: 'About packages' }).click();
frame.getByRole('gridcell', { name: 'osbuild-composer' });
await frame.getByRole('button', { name: 'Close', exact: true }).click();
await frame
.getByRole('button', { name: 'Save changes to blueprint' })
.click();
await frame.getByRole('button', { name: 'Edit blueprint' }).click();
await frame.getByRole('button', { name: 'About packages' }).click();
frame.getByRole('gridcell', { name: 'osbuild-composer' });
await frame.getByRole('button', { name: 'Close', exact: true }).click();
await frame.getByRole('button', { name: 'Cancel', exact: true }).click();
frame.getByRole('heading', { name: 'All images' });
});
test('build blueprint', async ({ page }) => {
await ensureAuthenticated(page);
await closePopupsIfExist(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);
await frame
.getByRole('textbox', { name: 'Search input' })
.fill(blueprintName);
// the clickable blueprint cards are a bit awkward, so use the
// button's id instead
await frame.locator(`button[id="${blueprintName}"]`).click();
await frame.getByTestId('blueprint-build-image-menu-option').click();
// make sure the image is present
await frame
.getByTestId('images-table')
.getByRole('button', { name: 'Details' })
.click();
frame.getByText('Build Information');
});
test('delete blueprint', async ({ page }) => {
await ensureAuthenticated(page);
await closePopupsIfExist(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);
await frame
.getByRole('textbox', { name: 'Search input' })
.fill(blueprintName);
// the clickable blueprint cards are a bit awkward, so use the
// button's id instead
await frame.locator(`button[id="${blueprintName}"]`).click();
await frame.getByRole('button', { name: /blueprint menu toggle/i }).click();
await frame.getByRole('menuitem', { name: 'Delete blueprint' }).click();
await frame.getByRole('button', { name: 'Delete' }).click();
});
test('cockpit worker config', async ({ page }) => {
if (isHosted()) {
return;
}
await ensureAuthenticated(page);
await closePopupsIfExist(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
await page.goto('/cockpit-image-builder');
const frame = ibFrame(page);
const header = frame.getByText('Configure AWS Uploads');
if (!(await header.isVisible())) {
await frame
.getByRole('button', { name: 'Configure Cloud Providers' })
.click();
await expect(header).toBeVisible();
}
const bucket = 'cockpit-ib-playwright-bucket';
const credentials = '/test/credentials';
const switchInput = frame.locator('#aws-config-switch');
await expect(switchInput).toBeVisible();
// introduce a wait time, since it takes some time to load the
// worker config file.
await page.waitForTimeout(1000);
// If this test fails for any reason, the config should already be loaded
// and visible on the retury. If it is go back to the landing page
if (await switchInput.isChecked()) {
await frame.getByRole('button', { name: 'Cancel' }).click();
await expect(
frame.getByRole('heading', { name: 'All images' }),
).toBeVisible();
} else {
const switchToggle = frame.locator('.pf-v6-c-switch');
await switchToggle.click();
await frame
.getByPlaceholder('AWS bucket')
// this doesn't need to exist, we're just testing that
// the form works as expected
.fill(bucket);
await frame.getByPlaceholder('Path to AWS credentials').fill(credentials);
await frame.getByRole('button', { name: 'Submit' }).click();
await expect(
frame.getByRole('heading', { name: 'All images' }),
).toBeVisible();
}
await frame
.getByRole('button', { name: 'Configure Cloud Providers' })
.click();
await expect(header).toBeVisible();
// introduce a wait time, since it takes some time to load the
// worker config file.
await page.waitForTimeout(1500);
await expect(frame.locator('#aws-config-switch')).toBeChecked();
await expect(frame.getByPlaceholder('AWS bucket')).toHaveValue(bucket);
await expect(frame.getByPlaceholder('Path to AWS credentials')).toHaveValue(
credentials,
);
await frame.getByRole('button', { name: 'Cancel' }).click();
const config = readFileSync('/etc/osbuild-worker/osbuild-worker.toml');
// this is for testing, the field `aws` should exist
// eslint-disable-next-line
const parsed = TOML.parse(config) as any;
expect(parsed.aws?.bucket).toBe(bucket);
expect(parsed.aws?.credentials).toBe(credentials);
});
const cockpitBlueprintname = uuidv4();
test('cockpit cloud upload', async ({ page }) => {
if (isHosted()) {
return;
}
await ensureAuthenticated(page);
await closePopupsIfExist(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
await page.goto('/cockpit-image-builder');
const frame = ibFrame(page);
frame.getByRole('heading', { name: 'Images About image builder' });
frame.getByRole('heading', { name: 'Blueprints' });
await frame.getByTestId('blueprints-create-button').click();
frame.getByRole('heading', { name: 'Image output' });
// the first card should be the AWS card
await frame.locator('.pf-v6-c-card').first().click();
await frame.getByRole('button', { name: 'Next', exact: true }).click();
await frame.getByRole('button', { name: 'Next', exact: true }).click();
await frame.getByRole('button', { name: 'Review and finish' }).click();
await frame.getByRole('button', { name: 'Back', exact: true }).click();
frame.getByRole('heading', { name: 'Details' });
await frame.getByTestId('blueprint').fill(cockpitBlueprintname);
await expect(frame.getByTestId('blueprint')).toHaveValue(
cockpitBlueprintname,
);
await frame.getByRole('button', { name: 'Next', exact: true }).click();
await frame.getByRole('button', { name: 'Create blueprint' }).click();
await frame.getByTestId('close-button-saveandbuild-modal').click();
await frame.getByRole('button', { name: 'Create blueprint' }).click();
await frame
.getByRole('textbox', { name: 'Search input' })
.fill(cockpitBlueprintname);
// the clickable blueprint cards are a bit awkward, so use the
// button's id instead
await frame.locator(`button[id="${cockpitBlueprintname}"]`).click();
await frame.getByTestId('blueprint-build-image-menu-option').click();
// make sure the image is present
await frame
.getByTestId('images-table')
.getByRole('button', { name: 'Details' })
.click();
frame.getByText('Build Information');
});
});