tests/playwright: Add Hostname customization test
Adds a Hostname customization test that creates a BP, edits BP, exports and imports it back and verifies the BP content. This test also servers as a template for other customization tests. Includes a refactor of existing helper functions.
This commit is contained in:
parent
91ceaca760
commit
0bddb80e94
10 changed files with 341 additions and 49 deletions
|
|
@ -60,3 +60,6 @@ overrides:
|
||||||
extends: "plugin:testing-library/react"
|
extends: "plugin:testing-library/react"
|
||||||
- files: ["playwright/**/*.ts"]
|
- files: ["playwright/**/*.ts"]
|
||||||
extends: "plugin:playwright/recommended"
|
extends: "plugin:playwright/recommended"
|
||||||
|
rules:
|
||||||
|
playwright/no-conditional-in-test: off
|
||||||
|
playwright/no-conditional-expect: off
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export default defineConfig({
|
||||||
? process.env.BASE_URL
|
? process.env.BASE_URL
|
||||||
: 'http://127.0.0.1:9090',
|
: 'http://127.0.0.1:9090',
|
||||||
video: 'retain-on-failure',
|
video: 'retain-on-failure',
|
||||||
trace: 'on-first-retry',
|
trace: 'on',
|
||||||
|
|
||||||
ignoreHTTPSErrors: true,
|
ignoreHTTPSErrors: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
84
playwright/Customizations/Hostname.spec.ts
Normal file
84
playwright/Customizations/Hostname.spec.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { expect } from '@playwright/test';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import { test } from '../fixtures/cleanup';
|
||||||
|
import { isHosted } from '../helpers/helpers';
|
||||||
|
import { login } from '../helpers/login';
|
||||||
|
import { navigateToOptionalSteps, ibFrame } from '../helpers/navHelpers';
|
||||||
|
import {
|
||||||
|
registerLater,
|
||||||
|
fillInDetails,
|
||||||
|
createBlueprint,
|
||||||
|
fillInImageOutputGuest,
|
||||||
|
deleteBlueprint,
|
||||||
|
exportBlueprint,
|
||||||
|
importBlueprint,
|
||||||
|
} from '../helpers/wizardHelpers';
|
||||||
|
|
||||||
|
test('Create a blueprint with Hostname customization', async ({
|
||||||
|
page,
|
||||||
|
cleanup,
|
||||||
|
}) => {
|
||||||
|
const blueprintName = 'test-' + uuidv4();
|
||||||
|
const hostname = 'testsystem';
|
||||||
|
|
||||||
|
// Delete the blueprint after the run fixture
|
||||||
|
await cleanup.add(() => deleteBlueprint(page, blueprintName));
|
||||||
|
|
||||||
|
// Login, navigate to IB and get the frame
|
||||||
|
await login(page);
|
||||||
|
const frame = await ibFrame(page);
|
||||||
|
|
||||||
|
await test.step('Navigate to optional steps in Wizard', async () => {
|
||||||
|
await navigateToOptionalSteps(frame);
|
||||||
|
await registerLater(frame);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Select and fill the Hostname step', async () => {
|
||||||
|
await frame.getByRole('button', { name: 'Hostname' }).click();
|
||||||
|
await frame.getByRole('textbox', { name: 'hostname input' }).fill(hostname);
|
||||||
|
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('Edit BP', async () => {
|
||||||
|
await frame.getByRole('button', { name: 'Edit blueprint' }).click();
|
||||||
|
await frame.getByLabel('Revisit Hostname step').click();
|
||||||
|
await frame.getByRole('textbox', { name: 'hostname input' }).click();
|
||||||
|
await frame
|
||||||
|
.getByRole('textbox', { name: 'hostname input' })
|
||||||
|
.fill(hostname + 'edited');
|
||||||
|
await frame.getByRole('button', { name: 'Review and finish' }).click();
|
||||||
|
await frame
|
||||||
|
.getByRole('button', { name: 'Save changes to blueprint' })
|
||||||
|
.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
// This is for hosted service only as these features are not available in cockpit plugin
|
||||||
|
await test.step('Export BP', async (step) => {
|
||||||
|
step.skip(!isHosted(), 'Exporting is not available in the plugin');
|
||||||
|
await exportBlueprint(page, blueprintName);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Import BP', async (step) => {
|
||||||
|
step.skip(!isHosted(), 'Importing is not available in the plugin');
|
||||||
|
await importBlueprint(page, blueprintName);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Review imported BP', async (step) => {
|
||||||
|
step.skip(!isHosted(), 'Importing is not available in the plugin');
|
||||||
|
await fillInImageOutputGuest(page);
|
||||||
|
await page.getByRole('button', { name: 'Hostname' }).click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('textbox', { name: 'hostname input' })
|
||||||
|
).toHaveValue(hostname + 'edited');
|
||||||
|
await page.getByRole('button', { name: 'Cancel' }).click();
|
||||||
|
});
|
||||||
|
});
|
||||||
45
playwright/fixtures/cleanup.ts
Normal file
45
playwright/fixtures/cleanup.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { test as oldTest } from '@playwright/test';
|
||||||
|
|
||||||
|
type WithCleanup = {
|
||||||
|
cleanup: Cleanup;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Cleanup {
|
||||||
|
add: (cleanupFn: () => Promise<unknown>) => symbol;
|
||||||
|
runAndAdd: (cleanupFn: () => Promise<unknown>) => Promise<symbol>;
|
||||||
|
remove: (key: symbol) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const test = oldTest.extend<WithCleanup>({
|
||||||
|
// eslint-disable-next-line no-empty-pattern
|
||||||
|
cleanup: async ({}, use) => {
|
||||||
|
const cleanupFns: Map<symbol, () => Promise<unknown>> = new Map();
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
await use({
|
||||||
|
add: (cleanupFn) => {
|
||||||
|
const key = Symbol();
|
||||||
|
cleanupFns.set(key, cleanupFn);
|
||||||
|
return key;
|
||||||
|
},
|
||||||
|
runAndAdd: async (cleanupFn) => {
|
||||||
|
await cleanupFn();
|
||||||
|
|
||||||
|
const key = Symbol();
|
||||||
|
cleanupFns.set(key, cleanupFn);
|
||||||
|
return key;
|
||||||
|
},
|
||||||
|
remove: (key) => {
|
||||||
|
cleanupFns.delete(key);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step(
|
||||||
|
'Post-test cleanup',
|
||||||
|
async () => {
|
||||||
|
await Promise.all(Array.from(cleanupFns).map(([, fn]) => fn()));
|
||||||
|
},
|
||||||
|
{ box: true }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
35
playwright/helpers/helpers.ts
Normal file
35
playwright/helpers/helpers.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { type Page, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
export const togglePreview = async (page: Page) => {
|
||||||
|
const toggleSwitch = page.locator('#preview-toggle');
|
||||||
|
|
||||||
|
if (!(await toggleSwitch.isChecked())) {
|
||||||
|
await toggleSwitch.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
const turnOnButton = page.getByRole('button', { name: 'Turn on' });
|
||||||
|
if (await turnOnButton.isVisible()) {
|
||||||
|
await turnOnButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(toggleSwitch).toBeChecked();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isHosted = (): boolean => {
|
||||||
|
return process.env.BASE_URL?.includes('redhat.com') || false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const closePopupsIfExist = async (page: Page) => {
|
||||||
|
const locatorsToCheck = [
|
||||||
|
page.locator('.pf-v5-c-alert.notification-item button'), // This closes all toast pop-ups
|
||||||
|
page.locator(`button[id^="pendo-close-guide-"]`), // This closes the pendo guide pop-up
|
||||||
|
page.locator(`button[id="truste-consent-button"]`), // This closes the trusted consent pop-up
|
||||||
|
page.getByLabel('close-notification'), // This closes a one off info notification (May be covered by the toast above, needs recheck.)
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const locator of locatorsToCheck) {
|
||||||
|
await page.addLocatorHandler(locator, async () => {
|
||||||
|
await locator.first().click(); // There can be multiple toast pop-ups
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,29 +1,11 @@
|
||||||
import { type Page, type FrameLocator, expect } from '@playwright/test';
|
import { type Page, expect } from '@playwright/test';
|
||||||
|
|
||||||
export const ibFrame = (page: Page): FrameLocator | Page => {
|
import { closePopupsIfExist, isHosted, togglePreview } from './helpers';
|
||||||
if (isHosted()) {
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
return page
|
|
||||||
.locator('iframe[name="cockpit1\\:localhost\\/cockpit-image-builder"]')
|
|
||||||
.contentFrame();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const togglePreview = async (page: Page) => {
|
|
||||||
const toggleSwitch = page.locator('#preview-toggle');
|
|
||||||
|
|
||||||
if (!(await toggleSwitch.isChecked())) {
|
|
||||||
await toggleSwitch.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
const turnOnButton = page.getByRole('button', { name: 'Turn on' });
|
|
||||||
if (await turnOnButton.isVisible()) {
|
|
||||||
await turnOnButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
await expect(toggleSwitch).toBeChecked();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs in to either Cockpit or Console, will distinguish between them based on the environment
|
||||||
|
* @param page - the page object
|
||||||
|
*/
|
||||||
export const login = async (page: Page) => {
|
export const login = async (page: Page) => {
|
||||||
if (!process.env.PLAYWRIGHT_USER || !process.env.PLAYWRIGHT_PASSWORD) {
|
if (!process.env.PLAYWRIGHT_USER || !process.env.PLAYWRIGHT_PASSWORD) {
|
||||||
throw new Error('user or password not set in environment');
|
throw new Error('user or password not set in environment');
|
||||||
|
|
@ -38,10 +20,6 @@ export const login = async (page: Page) => {
|
||||||
return loginCockpit(page, user, password);
|
return loginCockpit(page, user, password);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isHosted = (): boolean => {
|
|
||||||
return process.env.BASE_URL?.includes('redhat.com') || false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const loginCockpit = async (page: Page, user: string, password: string) => {
|
const loginCockpit = async (page: Page, user: string, password: string) => {
|
||||||
await page.goto('/cockpit-image-builder');
|
await page.goto('/cockpit-image-builder');
|
||||||
|
|
||||||
|
|
@ -68,10 +46,13 @@ const loginCockpit = async (page: Page, user: string, password: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// expect to have administrative access
|
// expect to have administrative access
|
||||||
await page.getByRole('button', { name: 'Administrative access' });
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Administrative access' })
|
||||||
|
).toBeVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
const loginConsole = async (page: Page, user: string, password: string) => {
|
const loginConsole = async (page: Page, user: string, password: string) => {
|
||||||
|
await closePopupsIfExist(page);
|
||||||
await page.goto('/insights/image-builder/landing');
|
await page.goto('/insights/image-builder/landing');
|
||||||
await page
|
await page
|
||||||
.getByRole('textbox', { name: 'Red Hat login or email' })
|
.getByRole('textbox', { name: 'Red Hat login or email' })
|
||||||
|
|
@ -79,22 +60,6 @@ const loginConsole = async (page: Page, user: string, password: string) => {
|
||||||
await page.getByRole('button', { name: 'Next' }).click();
|
await page.getByRole('button', { name: 'Next' }).click();
|
||||||
await page.getByRole('textbox', { name: 'Password' }).fill(password);
|
await page.getByRole('textbox', { name: 'Password' }).fill(password);
|
||||||
await page.getByRole('button', { name: 'Log in' }).click();
|
await page.getByRole('button', { name: 'Log in' }).click();
|
||||||
await closePopupsIfExist(page);
|
|
||||||
await togglePreview(page);
|
await togglePreview(page);
|
||||||
await page.getByRole('heading', { name: 'All images' });
|
await expect(page.getByRole('heading', { name: 'All images' })).toBeVisible();
|
||||||
};
|
|
||||||
|
|
||||||
const closePopupsIfExist = async (page: Page) => {
|
|
||||||
const locatorsToCheck = [
|
|
||||||
page.locator('.pf-v5-c-alert.notification-item button'), // This closes all toast pop-ups
|
|
||||||
page.locator(`button[id^="pendo-close-guide-"]`), // This closes the pendo guide pop-up
|
|
||||||
page.locator(`button[id="truste-consent-button"]`), // This closes the trusted consent pop-up
|
|
||||||
page.getByLabel('close-notification'), // This closes a one off info notification (May be covered by the toast above, needs recheck.)
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const locator of locatorsToCheck) {
|
|
||||||
await page.addLocatorHandler(locator, async () => {
|
|
||||||
await locator.first().click(); // There can be multiple toast pop-ups
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
26
playwright/helpers/navHelpers.ts
Normal file
26
playwright/helpers/navHelpers.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import type { FrameLocator, Page } from '@playwright/test';
|
||||||
|
|
||||||
|
import { isHosted } from './helpers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the wizard, fills out the "Image Output" step, and navigates to the optional steps
|
||||||
|
* @param page - the page object
|
||||||
|
*/
|
||||||
|
export const navigateToOptionalSteps = async (page: Page | FrameLocator) => {
|
||||||
|
await page.getByRole('button', { name: 'Create blueprint' }).click();
|
||||||
|
await page.getByRole('checkbox', { name: 'Virtualization' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Next' }).click();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the FrameLocator object in case we are using cockpit plugin, else it returns the page object
|
||||||
|
* @param page - the page object
|
||||||
|
*/
|
||||||
|
export const ibFrame = (page: Page): FrameLocator | Page => {
|
||||||
|
if (isHosted()) {
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
return page
|
||||||
|
.locator('iframe[name="cockpit1\\:localhost\\/cockpit-image-builder"]')
|
||||||
|
.contentFrame();
|
||||||
|
};
|
||||||
129
playwright/helpers/wizardHelpers.ts
Normal file
129
playwright/helpers/wizardHelpers.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import { expect, FrameLocator, type Page, test } from '@playwright/test';
|
||||||
|
|
||||||
|
import { isHosted } from './helpers';
|
||||||
|
import { ibFrame } from './navHelpers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clicks the create button, handles the modal, clicks the button again and selecets the BP in the list
|
||||||
|
* @param page - the page object
|
||||||
|
* @param blueprintName - the name of the created blueprint
|
||||||
|
*/
|
||||||
|
export const createBlueprint = async (
|
||||||
|
page: Page | FrameLocator,
|
||||||
|
blueprintName: string
|
||||||
|
) => {
|
||||||
|
await page.getByRole('button', { name: 'Create blueprint' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Close' }).first().click();
|
||||||
|
await page.getByRole('button', { name: 'Create blueprint' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'Search input' }).fill(blueprintName);
|
||||||
|
await page.getByTestId('blueprint-card').getByText(blueprintName).click();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill in the "Details" step in the wizard
|
||||||
|
* This method assumes that the "Details" step is ENABLED!
|
||||||
|
* After filling the step, it will click the "Next" button
|
||||||
|
* Description defaults to "Testing blueprint"
|
||||||
|
* @param page - the page object
|
||||||
|
* @param blueprintName - the name of the blueprint to create
|
||||||
|
*/
|
||||||
|
export const fillInDetails = async (
|
||||||
|
page: Page | FrameLocator,
|
||||||
|
blueprintName: string
|
||||||
|
) => {
|
||||||
|
await page.getByRole('listitem').filter({ hasText: 'Details' }).click();
|
||||||
|
await page
|
||||||
|
.getByRole('textbox', { name: 'Blueprint name' })
|
||||||
|
.fill(blueprintName);
|
||||||
|
await page
|
||||||
|
.getByRole('textbox', { name: 'Blueprint description' })
|
||||||
|
.fill('Testing blueprint');
|
||||||
|
await page.getByRole('button', { name: 'Next' }).click();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select "Register later" option in the wizard
|
||||||
|
* This function executes only on the hosted service
|
||||||
|
* @param page - the page object
|
||||||
|
*/
|
||||||
|
export const registerLater = async (page: Page | FrameLocator) => {
|
||||||
|
if (isHosted()) {
|
||||||
|
await page.getByRole('button', { name: 'Register' }).click();
|
||||||
|
await page.getByRole('radio', { name: 'Register later' }).click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill in the image output step in the wizard by selecting the Guest Image
|
||||||
|
* @param page - the page object
|
||||||
|
*/
|
||||||
|
export const fillInImageOutputGuest = async (page: Page | FrameLocator) => {
|
||||||
|
await page.getByRole('checkbox', { name: 'Virtualization' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Next' }).click();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the blueprint with the given name
|
||||||
|
* Will locate to the Image Builder page and search for the blueprint first
|
||||||
|
* @param page - the page object
|
||||||
|
* @param blueprintName - the name of the blueprint to delete
|
||||||
|
*/
|
||||||
|
export const deleteBlueprint = async (page: Page, blueprintName: string) => {
|
||||||
|
await test.step(
|
||||||
|
'Delete the blueprint with name: ' + blueprintName,
|
||||||
|
async () => {
|
||||||
|
// Locate back to the Image Builder page every time because the test can fail at any stage
|
||||||
|
const frame = await ibFrame(page);
|
||||||
|
await frame
|
||||||
|
.getByRole('textbox', { name: 'Search input' })
|
||||||
|
.fill(blueprintName);
|
||||||
|
await frame
|
||||||
|
.getByTestId('blueprint-card')
|
||||||
|
.getByText(blueprintName)
|
||||||
|
.click();
|
||||||
|
await frame.getByRole('button', { name: 'Menu toggle' }).click();
|
||||||
|
await frame.getByRole('menuitem', { name: 'Delete blueprint' }).click();
|
||||||
|
await frame.getByRole('button', { name: 'Delete' }).click();
|
||||||
|
},
|
||||||
|
{ box: true }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export the blueprint
|
||||||
|
* This function executes only on the hosted service
|
||||||
|
* @param page - the page object
|
||||||
|
*/
|
||||||
|
export const exportBlueprint = async (page: Page, blueprintName: string) => {
|
||||||
|
if (isHosted()) {
|
||||||
|
await page.getByRole('button', { name: 'Menu toggle' }).click();
|
||||||
|
const downloadPromise = page.waitForEvent('download');
|
||||||
|
await page
|
||||||
|
.getByRole('menuitem', { name: 'Download blueprint (.json)' })
|
||||||
|
.click();
|
||||||
|
const download = await downloadPromise;
|
||||||
|
await download.saveAs('../../downloads/' + blueprintName + '.json');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import the blueprint
|
||||||
|
* This function executes only on the hosted service
|
||||||
|
* @param page - the page object
|
||||||
|
*/
|
||||||
|
export const importBlueprint = async (
|
||||||
|
page: Page | FrameLocator,
|
||||||
|
blueprintName: string
|
||||||
|
) => {
|
||||||
|
if (isHosted()) {
|
||||||
|
await page.getByRole('button', { name: 'Import' }).click();
|
||||||
|
const dragBoxSelector = page.getByRole('presentation').first();
|
||||||
|
await dragBoxSelector
|
||||||
|
.locator('input[type=file]')
|
||||||
|
.setInputFiles('../../downloads/' + blueprintName + '.json');
|
||||||
|
await expect(
|
||||||
|
page.getByRole('textbox', { name: 'File upload' })
|
||||||
|
).not.toBeEmpty();
|
||||||
|
await page.getByRole('button', { name: 'Review and Finish' }).click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { expect, test } from '@playwright/test';
|
import { expect, test } from '@playwright/test';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { login, ibFrame, isHosted } from './lib/lib';
|
import { isHosted } from './helpers/helpers';
|
||||||
|
import { login } from './helpers/login';
|
||||||
|
import { ibFrame } from './helpers/navHelpers';
|
||||||
|
|
||||||
test.describe.serial('test', () => {
|
test.describe.serial('test', () => {
|
||||||
const blueprintName = uuidv4();
|
const blueprintName = uuidv4();
|
||||||
|
|
@ -76,7 +78,9 @@ test.describe.serial('test', () => {
|
||||||
await frame.getByTestId('close-button-saveandbuild-modal').click();
|
await frame.getByTestId('close-button-saveandbuild-modal').click();
|
||||||
await frame.getByRole('button', { name: 'Create blueprint' }).click();
|
await frame.getByRole('button', { name: 'Create blueprint' }).click();
|
||||||
|
|
||||||
await frame.getByText(blueprintName);
|
await expect(
|
||||||
|
frame.locator('.pf-v5-c-card__title-text').getByText(blueprintName)
|
||||||
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('edit blueprint', async ({ page }) => {
|
test('edit blueprint', async ({ page }) => {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ const BlueprintCard = ({ blueprint }: blueprintProps) => {
|
||||||
<Card
|
<Card
|
||||||
isSelected={blueprint.id === selectedBlueprintId}
|
isSelected={blueprint.id === selectedBlueprintId}
|
||||||
ouiaId={`blueprint-card-${blueprint.id}`}
|
ouiaId={`blueprint-card-${blueprint.id}`}
|
||||||
|
data-testid={`blueprint-card`}
|
||||||
isCompact
|
isCompact
|
||||||
isClickable
|
isClickable
|
||||||
onClick={() => dispatch(setBlueprintId(blueprint.id))}
|
onClick={() => dispatch(setBlueprintId(blueprint.id))}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue