Merge fb2ecfc65b into 7391652e17
This commit is contained in:
commit
57fdeba788
10 changed files with 540 additions and 2 deletions
90
.github/workflows/boot-playwright.yml
vendored
Normal file
90
.github/workflows/boot-playwright.yml
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
name: Playwright nightly boot tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize, labeled, unlabeled]
|
||||
workflow_dispatch:
|
||||
merge_group:
|
||||
|
||||
# this prevents multiple jobs from the same pr
|
||||
# running when new changes are pushed.
|
||||
concurrency:
|
||||
group: ${{github.workflow}}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
playwright-tests:
|
||||
runs-on:
|
||||
- codebuild-image-builder-frontend-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
- instance-size:large
|
||||
- buildspec-override:true
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Get current PR URL
|
||||
id: get-pr-url
|
||||
run: |
|
||||
# Extract the pull request URL from the event payload
|
||||
pr_url=$(jq -r '.pull_request.html_url' < "$GITHUB_EVENT_PATH")
|
||||
echo "Pull Request URL: $pr_url"
|
||||
# Set the PR URL as an output using the environment file
|
||||
echo "pr_url=$pr_url" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "npm"
|
||||
|
||||
- name: Install front-end dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install playwright
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
# This prevents an error related to minimum watchers when running the front-end and playwright
|
||||
- name: Increase file watchers limit
|
||||
run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
|
||||
- name: Update /etc/hosts
|
||||
run: sudo npm run patch:hosts
|
||||
|
||||
- name: Start front-end server
|
||||
run: |
|
||||
npm run start:federated &
|
||||
npx wait-on http://localhost:8003/apps/image-builder/
|
||||
|
||||
- name: Run testing proxy
|
||||
run: docker run -d --network=host -e HTTPS_PROXY=$RH_PROXY_URL -v "$(pwd)/config:/config:ro,Z" --name consoledot-testing-proxy quay.io/dvagner/consoledot-testing-proxy
|
||||
|
||||
- name: Run front-end Playwright tests
|
||||
env:
|
||||
BASE_URL: https://stage.foo.redhat.com:1337
|
||||
run: |
|
||||
export PLAYWRIGHT_USER=image-builder-playwright-$RANDOM
|
||||
export PLAYWRIGHT_PASSWORD=image-builder-playwright-$(uuidgen)
|
||||
# Step 1: Create a new empty account
|
||||
curl -k -X POST https://account-manager-stage.app.eng.rdu2.redhat.com/account/new -d "{\"username\": \"$PLAYWRIGHT_USER\", \"password\":\"$PLAYWRIGHT_PASSWORD\"}"
|
||||
# Step 2: Attach subscriptions to the new account
|
||||
curl -k -X POST https://account-manager-stage.app.eng.rdu2.redhat.com/account/attach \
|
||||
-d "{\"username\": \"$PLAYWRIGHT_USER\", \"password\":\"$PLAYWRIGHT_PASSWORD\", \"sku\":[\"RH00003\"],\"quantity\": 1}"
|
||||
# Step 3: Activate the new account by accepting Terms and Conditions
|
||||
curl -k -X POST https://account-manager-stage.app.eng.rdu2.redhat.com/account/activate -d "{\"username\": \"$PLAYWRIGHT_USER\", \"password\":\"$PLAYWRIGHT_PASSWORD\"}"
|
||||
# Step 4: Refresh account to update subscription pools
|
||||
curl -k -X POST https://account-manager-stage.app.eng.rdu2.redhat.com/account/refresh -d "{\"username\": \"$PLAYWRIGHT_USER\", \"password\":\"$PLAYWRIGHT_PASSWORD\"}"
|
||||
# Step 5: View account to check account status
|
||||
curl -k -X GET "https://account-manager-stage.app.eng.rdu2.redhat.com/account/get?username=$PLAYWRIGHT_USER&password=$PLAYWRIGHT_PASSWORD"
|
||||
|
||||
CURRENTS_PROJECT_ID=hIU6nO CURRENTS_RECORD_KEY=$CURRENTS_RECORD_KEY npx playwright test playwright/BootTests
|
||||
|
||||
- name: Store front-end Test report
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 10
|
||||
3
.github/workflows/playwright.yml
vendored
3
.github/workflows/playwright.yml
vendored
|
|
@ -14,6 +14,7 @@ concurrency:
|
|||
|
||||
jobs:
|
||||
playwright-tests:
|
||||
if: false # Temporarily disabled
|
||||
runs-on:
|
||||
- codebuild-image-builder-frontend-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
- instance-size:large
|
||||
|
|
@ -79,7 +80,7 @@ jobs:
|
|||
# Step 5: View account to check account status
|
||||
curl -k -X GET "https://account-manager-stage.app.eng.rdu2.redhat.com/account/get?username=$PLAYWRIGHT_USER&password=$PLAYWRIGHT_PASSWORD"
|
||||
|
||||
CURRENTS_PROJECT_ID=hIU6nO CURRENTS_RECORD_KEY=$CURRENTS_RECORD_KEY npx playwright test
|
||||
CURRENTS_PROJECT_ID=hIU6nO CURRENTS_RECORD_KEY=$CURRENTS_RECORD_KEY npx playwright test playwright/Customizations
|
||||
|
||||
- name: Store front-end Test report
|
||||
uses: actions/upload-artifact@v4
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -49,3 +49,4 @@ rpmbuild
|
|||
/playwright/.cache/
|
||||
.env
|
||||
.auth
|
||||
/image-downloads/
|
||||
|
|
|
|||
|
|
@ -8,10 +8,15 @@ phases:
|
|||
- echo Entered the install phase...
|
||||
- nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay2 &
|
||||
- timeout 15 sh -c "until docker info; do echo .; sleep 1; done"
|
||||
- apt-get install -y openssh-client python3
|
||||
- pip install python-openstackclient
|
||||
|
||||
pre_build:
|
||||
commands:
|
||||
- echo Entered the pre_build phase...
|
||||
- echo Adding SSH key to the agent...
|
||||
- eval "$(ssh-agent -s)"
|
||||
- echo "$OS_SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
|
||||
|
||||
build:
|
||||
commands:
|
||||
|
|
|
|||
|
|
@ -171,4 +171,14 @@ module.exports = defineConfig([
|
|||
]
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
files: [
|
||||
'playwright/BootTests/helpers/OpenStackWrapper.ts',
|
||||
'playwright/BootTests/helpers/imageBuilding.ts',
|
||||
],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ import {
|
|||
} from '@playwright/test';
|
||||
import 'dotenv/config';
|
||||
|
||||
const reporters: ReporterDescription[] = [['html'], ['list']];
|
||||
const reporters: ReporterDescription[] = [['html']];
|
||||
|
||||
if (!process.env.CI) {
|
||||
reporters.push(['list']);
|
||||
}
|
||||
|
||||
if (process.env.CURRENTS_PROJECT_ID && process.env.CURRENTS_RECORD_KEY) {
|
||||
reporters.push(['@currents/playwright']);
|
||||
|
|
|
|||
81
playwright/BootTests/Qcow2.spec.ts
Normal file
81
playwright/BootTests/Qcow2.spec.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import { expect } from '@playwright/test';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import {
|
||||
buildImage,
|
||||
constructFilePath,
|
||||
downloadImage,
|
||||
} from './helpers/imageBuilding';
|
||||
import { OpenStackWrapper } from './helpers/OpenStackWrapper';
|
||||
import { navigateToWizard, selectTarget } from './helpers/targetChooser';
|
||||
|
||||
import { test } from '../fixtures/customizations';
|
||||
import { isHosted } from '../helpers/helpers';
|
||||
import { ensureAuthenticated } from '../helpers/login';
|
||||
import { ibFrame, navigateToLandingPage } from '../helpers/navHelpers';
|
||||
import {
|
||||
createBlueprint,
|
||||
deleteBlueprint,
|
||||
fillInDetails,
|
||||
registerLater,
|
||||
} from '../helpers/wizardHelpers';
|
||||
|
||||
test('Boot qcow2 image and test hostname', async ({ page, cleanup }) => {
|
||||
test.setTimeout(120 * 60 * 1000); // 2 hours
|
||||
test.skip(
|
||||
!isHosted(),
|
||||
'Skipping test. Boot test run only on the hosted service.',
|
||||
);
|
||||
const blueprintName = 'boot-test-qcow-' + uuidv4();
|
||||
const filePath = constructFilePath(blueprintName, 'qcow2');
|
||||
|
||||
// Delete the blueprint and Openstack resources after the run
|
||||
await cleanup.add(() => deleteBlueprint(page, blueprintName));
|
||||
await cleanup.add(() => OpenStackWrapper.deleteImage(blueprintName));
|
||||
await cleanup.add(() => OpenStackWrapper.deleteInstance(blueprintName));
|
||||
|
||||
await ensureAuthenticated(page);
|
||||
|
||||
await navigateToLandingPage(page);
|
||||
const frame = await ibFrame(page);
|
||||
|
||||
await test.step('Select target', async () => {
|
||||
await navigateToWizard(frame);
|
||||
await selectTarget(frame, 'qcow2');
|
||||
});
|
||||
|
||||
await test.step('Register later', async () => {
|
||||
await registerLater(frame);
|
||||
await frame.getByRole('button', { name: 'Review and finish' }).click();
|
||||
});
|
||||
|
||||
await test.step('Fill the BP details', async () => {
|
||||
await fillInDetails(frame, blueprintName);
|
||||
});
|
||||
|
||||
await test.step('Create BP', async () => {
|
||||
await createBlueprint(frame, blueprintName);
|
||||
});
|
||||
|
||||
await test.step('Build the image', async () => {
|
||||
await buildImage(page);
|
||||
});
|
||||
|
||||
await test.step('Download the image', async () => {
|
||||
await downloadImage(page, filePath);
|
||||
});
|
||||
|
||||
// Initialize Openstack wrapper
|
||||
const image = new OpenStackWrapper(blueprintName, 'qcow2', filePath);
|
||||
|
||||
await test.step('Prepare Openstack instance', async () => {
|
||||
await image.createImage();
|
||||
await image.launchInstance();
|
||||
});
|
||||
|
||||
await test.step('Test if the image booted', async () => {
|
||||
const [exitCode, output] = await image.exec('echo "Hello World"');
|
||||
expect(exitCode).toBe(0);
|
||||
expect(output).toContain('Hello World');
|
||||
});
|
||||
});
|
||||
246
playwright/BootTests/helpers/OpenStackWrapper.ts
Normal file
246
playwright/BootTests/helpers/OpenStackWrapper.ts
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
import { exec } from 'child_process';
|
||||
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
export class OpenStackWrapper {
|
||||
private ipAddress: string;
|
||||
private imageName: string;
|
||||
private instanceName: string;
|
||||
private diskFormat: string;
|
||||
private imageFilePath: string;
|
||||
// Add an option to use environment variable for local debugging
|
||||
private keyName: string =
|
||||
process.env.OS_SSH_KEY_NAME ?? 'image-builder-frontend-ci';
|
||||
private canConnect: boolean;
|
||||
|
||||
/**
|
||||
* This class serves as a wrapper around the OpenStack CLI.
|
||||
* It provides methods to create an image, launch an instance, and execute commands on the instance.
|
||||
* @param imageName - The name of the image to create.
|
||||
* @param diskFormat - The disk format of the image.
|
||||
* @param imageFilePath - The path to the image file.
|
||||
* @param instanceName - The name of the instance to launch. If not provided, the image name is used.
|
||||
*/
|
||||
public constructor(
|
||||
imageName: string,
|
||||
diskFormat: string,
|
||||
imageFilePath: string,
|
||||
instanceName?: string,
|
||||
) {
|
||||
this.imageName = imageName;
|
||||
this.instanceName = instanceName ?? imageName;
|
||||
this.diskFormat = diskFormat;
|
||||
this.imageFilePath = imageFilePath;
|
||||
this.canConnect = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around exec so it can be simply called as await execCommand(...).
|
||||
* Executes a command and returns the output.
|
||||
* @param command - The command to execute.
|
||||
* @throws Error if command fails
|
||||
* @returns stdout
|
||||
*/
|
||||
private static async execCommand(command: string): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
exec(command, (error, stdout) => {
|
||||
if (error) {
|
||||
// Reject the promise with an error that includes stderr
|
||||
reject(new Error(`Command failed: ${error.message}`));
|
||||
return;
|
||||
}
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload an image to Openstack.
|
||||
*/
|
||||
public async createImage(): Promise<void> {
|
||||
const sleepTime = 10000; // 10 seconds
|
||||
const retries = 40; // 40 * 10 seconds = 4 minutes for image to be in 'active' state
|
||||
try {
|
||||
console.log(`Uploading image ${this.imageName} to Openstack`);
|
||||
await OpenStackWrapper.execCommand(
|
||||
`openstack image create -f json --disk-format="${this.diskFormat}" --file=${this.imageFilePath} ${this.imageName}`,
|
||||
);
|
||||
// Wait until the image is in 'active' state
|
||||
for (let i = 0; i < retries; i++) {
|
||||
const output = await OpenStackWrapper.execCommand(
|
||||
`openstack image show -f json ${this.imageName}`,
|
||||
);
|
||||
const image = JSON.parse(output);
|
||||
if (image.status === 'active') {
|
||||
console.log(
|
||||
`Image ${this.imageName} successfully uploaded to Openstack`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Wait before checking again
|
||||
await new Promise((resolve) => setTimeout(resolve, sleepTime));
|
||||
}
|
||||
// If the image is not ready after the retries, throw an error
|
||||
throw new OpenStackError(
|
||||
`Instance ${this.imageName} didn't launch after 10 minutes.`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(`Error creating image: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch an instance from the created image.
|
||||
*/
|
||||
public async launchInstance(): Promise<void> {
|
||||
const sleepTime = 30000; // 30 seconds
|
||||
const retries = 20; // 20 * 30 seconds = 10 minutes to launch the instance
|
||||
try {
|
||||
console.log(
|
||||
`Launching instance ${this.instanceName} from image ${this.imageName}`,
|
||||
);
|
||||
const output = await OpenStackWrapper.execCommand(
|
||||
`openstack server create -f json --image="${this.imageName}" --flavor="g.standard.small" --network="shared_net_1" --security-group="default" --key-name="${this.keyName}" ${this.instanceName}`,
|
||||
);
|
||||
const instance = JSON.parse(output);
|
||||
// Expect the instance started building
|
||||
if (instance.status !== 'BUILD') {
|
||||
throw new OpenStackError(
|
||||
`Instance ${this.instanceName} does not have expected status 'BUILD', but '${instance.status}'`,
|
||||
);
|
||||
}
|
||||
// Wait until the instance is running (in active state)
|
||||
for (let i = 0; i < retries; i++) {
|
||||
const output = await OpenStackWrapper.execCommand(
|
||||
`openstack server show -f json ${this.instanceName}`,
|
||||
);
|
||||
const instance = JSON.parse(output);
|
||||
if (instance.status === 'ACTIVE') {
|
||||
// Instance is running
|
||||
this.ipAddress = instance.addresses.shared_net_1[0];
|
||||
console.log(`Instance ${this.instanceName} launched successfully`);
|
||||
return;
|
||||
}
|
||||
// Wait before checking again
|
||||
await new Promise((resolve) => setTimeout(resolve, sleepTime));
|
||||
}
|
||||
// If the instance is not running after the retries, throw an error
|
||||
throw new OpenStackError(
|
||||
`Instance ${this.instanceName} didn't launch after 10 minutes.`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(`Error launching instance: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can connect to the instance. Raises an error if we can't.
|
||||
* @throws OpenStackError if we can't connect to the instance even after the retries.
|
||||
*/
|
||||
private async checkConnection(): Promise<void> {
|
||||
if (!this.canConnect) {
|
||||
const sleepTime = 15000; // 15 seconds
|
||||
const retries = 4; // 4 * 15 seconds = 1 minute to wait if we can connect to the instance
|
||||
for (let i = 0; i < retries; i++) {
|
||||
try {
|
||||
const output = await OpenStackWrapper.execCommand(
|
||||
`ssh -o StrictHostKeyChecking=accept-new cloud-user@${this.ipAddress} "echo 'Hello'"`,
|
||||
);
|
||||
if (output.includes('Hello')) {
|
||||
this.canConnect = true;
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
if (i < retries - 1) {
|
||||
await new Promise((resolve) => setTimeout(resolve, sleepTime));
|
||||
} else {
|
||||
throw new OpenStackError(
|
||||
`Failed to connect to instance ${this.imageName} after ${retries} attempts. Reason: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`Instance ${this.imageName} is ready to connect`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a command via SSH on the running instance and returns the exit code and output.
|
||||
* @param command - The command to execute.
|
||||
* @param user - The user to execute the command as. If not provided, defaults to 'cloud-user'.
|
||||
* @returns [exitCode, stdout]
|
||||
*/
|
||||
public async exec(command: string, user?: string): Promise<[number, string]> {
|
||||
await this.checkConnection();
|
||||
|
||||
// Execute the SSH command using the private wrapper
|
||||
try {
|
||||
const output = await OpenStackWrapper.execCommand(
|
||||
`ssh -o StrictHostKeyChecking=accept-new ${user ?? 'cloud-user'}@${this.ipAddress} ${command}`,
|
||||
);
|
||||
return [0, output];
|
||||
} catch (error) {
|
||||
return [error.code, error.message];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an image from Openstack.
|
||||
* @param imageName - The name of the image to delete.
|
||||
* @throws OpenStackError if the image is found but failed to delete.
|
||||
*/
|
||||
public static async deleteImage(imageName: string): Promise<void> {
|
||||
await test.step(
|
||||
'Delete the image on Openstack with name: ' + imageName,
|
||||
async () => {
|
||||
try {
|
||||
await this.execCommand(`openstack image delete ${imageName}`);
|
||||
console.log(`Image ${imageName} deleted`);
|
||||
} catch (error) {
|
||||
if (!error.message.includes('Multi Backend support not enabled.')) {
|
||||
throw new OpenStackError(
|
||||
`Image was found, but failed to delete. Reason: ${error.message}`,
|
||||
);
|
||||
}
|
||||
// Fail gracefully, no image to delete
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an instance from Openstack.
|
||||
* @param instanceName - The name of the instance to delete.
|
||||
* @throws OpenStackError if the instance is found but failed to delete.
|
||||
*/
|
||||
public static async deleteInstance(instanceName: string): Promise<void> {
|
||||
await test.step(
|
||||
'Delete the instance on Openstack with name: ' + instanceName,
|
||||
async () => {
|
||||
try {
|
||||
await this.execCommand(`openstack server delete ${instanceName}`);
|
||||
console.log(`Instance ${instanceName} deleted`);
|
||||
} catch (error) {
|
||||
if (!error.message.includes('No Server found')) {
|
||||
throw new OpenStackError(
|
||||
`Instance was found, but failed to delete. Reason: ${error.message}`,
|
||||
);
|
||||
}
|
||||
// Fail gracefully, no instance to delete
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom error class for OpenStack errors.
|
||||
*/
|
||||
class OpenStackError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'OpenStackError';
|
||||
}
|
||||
}
|
||||
56
playwright/BootTests/helpers/imageBuilding.ts
Normal file
56
playwright/BootTests/helpers/imageBuilding.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { Page } from '@playwright/test';
|
||||
|
||||
export const buildImage = async (page: Page) => {
|
||||
/**
|
||||
* Build the image and wait for it to be ready.
|
||||
* @param page - the page object
|
||||
*/
|
||||
await page.getByRole('button', { name: 'Build images' }).click();
|
||||
let timeSpentBuilding = 0;
|
||||
console.log('Starting the build');
|
||||
// eslint-disable-next-line disable-autofix/@typescript-eslint/no-unnecessary-condition
|
||||
while (true) {
|
||||
if (
|
||||
(await page.getByText('Ready').isVisible()) ||
|
||||
(await page.getByText('Expires in').isVisible())
|
||||
) {
|
||||
console.log(`Image is ready (Time spent: ${timeSpentBuilding / 60000}m)`);
|
||||
break;
|
||||
} else if (await page.getByText('Image build failed').isVisible()) {
|
||||
throw new Error('Image build failed');
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 30000));
|
||||
// Show how much time passed since the build started
|
||||
timeSpentBuilding += 30000;
|
||||
if (timeSpentBuilding % (60000 * 5) === 0) {
|
||||
// Log only every 5 minutes
|
||||
console.log(
|
||||
`Waiting for image to be ready (Time spent: ${timeSpentBuilding / 60000}m)`,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const downloadImage = async (page: Page, filePath: string) => {
|
||||
/**
|
||||
* Download the image and save it to the specified path.
|
||||
* @param page - the page object
|
||||
* @param filePath - the path to save the image
|
||||
*/
|
||||
// Start waiting for download before clicking. Note no await.
|
||||
console.log('Downloading image');
|
||||
const downloadPromise = page.waitForEvent('download');
|
||||
await page.getByText('Download').first().click();
|
||||
const download = await downloadPromise;
|
||||
await download.saveAs(filePath);
|
||||
console.log(`Downloaded file: ${filePath}`);
|
||||
};
|
||||
|
||||
export const constructFilePath = (blueprintName: string, extension: string) => {
|
||||
/**
|
||||
* Construct the file path for the image.
|
||||
* @param blueprintName - the name of the blueprint
|
||||
* @param extension - the extension of the image
|
||||
*/
|
||||
return `./image-downloads/${blueprintName}.${extension}`;
|
||||
};
|
||||
44
playwright/BootTests/helpers/targetChooser.ts
Normal file
44
playwright/BootTests/helpers/targetChooser.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { FrameLocator, Page } from '@playwright/test';
|
||||
|
||||
export const navigateToWizard = async (page: Page | FrameLocator) => {
|
||||
/**
|
||||
* Open the wizard.
|
||||
* @param page - the page object
|
||||
*/
|
||||
await page.getByRole('button', { name: 'Create image blueprint' }).click();
|
||||
};
|
||||
|
||||
export const selectTarget = async (
|
||||
page: Page | FrameLocator,
|
||||
target: 'qcow2' | 'iso' | 'wsl' | 'ova' | 'vmdk',
|
||||
) => {
|
||||
/**
|
||||
* Select the target.
|
||||
* @param page - the page object
|
||||
* @param target - the target to select (qcow2, iso, wsl, ova, vmdk)
|
||||
*/
|
||||
switch (target) {
|
||||
case 'qcow2':
|
||||
await page.getByRole('checkbox', { name: 'Virtualization' }).click();
|
||||
break;
|
||||
case 'iso':
|
||||
await page.getByRole('checkbox', { name: 'Bare metal' }).click();
|
||||
break;
|
||||
case 'wsl':
|
||||
await page.getByRole('checkbox', { name: 'WSL' }).click();
|
||||
break;
|
||||
case 'ova':
|
||||
await page
|
||||
.getByRole('checkbox', {
|
||||
name: 'VMware vSphere - Open virtualization format',
|
||||
})
|
||||
.click();
|
||||
break;
|
||||
case 'vmdk':
|
||||
await page
|
||||
.getByRole('checkbox', { name: 'VMware vSphere - Virtual disk' })
|
||||
.click();
|
||||
break;
|
||||
}
|
||||
await page.getByRole('button', { name: 'Next' }).click();
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue