Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
12 changes: 6 additions & 6 deletions src/plans/create/steps/review/HooksReviewSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const HooksReviewSection: FC = () => {
<DescriptionListTerm>
{hooksFormFieldLabels[MigrationHookFieldId.HookRunnerImage]}
</DescriptionListTerm>
<DescriptionListDescription>
<DescriptionListDescription data-testid="review-pre-migration-hook-runner-image">
{preMigration[MigrationHookFieldId.HookRunnerImage]}
</DescriptionListDescription>
</DescriptionListGroup>
Expand All @@ -63,7 +63,7 @@ const HooksReviewSection: FC = () => {
<DescriptionListTerm>
{hooksFormFieldLabels[MigrationHookFieldId.ServiceAccount]}
</DescriptionListTerm>
<DescriptionListDescription>
<DescriptionListDescription data-testid="review-pre-migration-hook-service-account">
{preMigration[MigrationHookFieldId.ServiceAccount] ?? t('None')}
</DescriptionListDescription>
</DescriptionListGroup>
Expand All @@ -72,7 +72,7 @@ const HooksReviewSection: FC = () => {
<DescriptionListTerm>
{hooksFormFieldLabels[MigrationHookFieldId.AnsiblePlaybook]}
</DescriptionListTerm>
<DescriptionListDescription>
<DescriptionListDescription data-testid="review-pre-migration-hook-ansible-playbook">
{preMigration[MigrationHookFieldId.AnsiblePlaybook] ?? t('None')}
</DescriptionListDescription>
</DescriptionListGroup>
Expand Down Expand Up @@ -105,7 +105,7 @@ const HooksReviewSection: FC = () => {
<DescriptionListTerm>
{hooksFormFieldLabels[MigrationHookFieldId.HookRunnerImage]}
</DescriptionListTerm>
<DescriptionListDescription>
<DescriptionListDescription data-testid="review-post-migration-hook-runner-image">
{postMigration[MigrationHookFieldId.HookRunnerImage]}
</DescriptionListDescription>
</DescriptionListGroup>
Expand All @@ -114,7 +114,7 @@ const HooksReviewSection: FC = () => {
<DescriptionListTerm>
{hooksFormFieldLabels[MigrationHookFieldId.ServiceAccount]}
</DescriptionListTerm>
<DescriptionListDescription>
<DescriptionListDescription data-testid="review-post-migration-hook-service-account">
{postMigration[MigrationHookFieldId.ServiceAccount] ?? t('None')}
</DescriptionListDescription>
</DescriptionListGroup>
Expand All @@ -123,7 +123,7 @@ const HooksReviewSection: FC = () => {
<DescriptionListTerm>
{hooksFormFieldLabels[MigrationHookFieldId.AnsiblePlaybook]}
</DescriptionListTerm>
<DescriptionListDescription>
<DescriptionListDescription data-testid="review-post-migration-hook-ansible-playbook">
{postMigration[MigrationHookFieldId.AnsiblePlaybook] ?? t('None')}
</DescriptionListDescription>
</DescriptionListGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const OffloadPluginField: FC<OffloadPluginFieldProps> = ({ fieldId }) => {
<Select
ref={field.ref}
id={fieldId}
testId={fieldId}
isDisabled={isSubmitting || loading}
value={field.value}
options={options}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const StorageProductField: FC<StorageProductFieldProps> = ({ fieldId }) => {
<Select
ref={field.ref}
id={fieldId}
testId={fieldId}
isDisabled={isSubmitting || loading}
value={field.value}
options={options}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const StorageSecretField: FC<StorageSecretFieldProps> = ({ fieldId, sourceProvid
<Select
ref={field.ref}
id={fieldId}
testId={fieldId}
isDisabled={isSubmitting}
value={field.value}
onSelect={(_e, value) => {
Expand Down
1 change: 1 addition & 0 deletions src/storageMaps/create/CreateStorageMapForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ const CreateStorageMapForm: React.FC = () => {

<Split hasGutter>
<Button
data-testid="create-storage-map-button"
onClick={handleSubmit(onSubmit)}
isDisabled={!isValid || isSubmitting}
isLoading={isSubmitting}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ const CreateStorageMapFieldTable: FC = () => {
<TargetStorageField
fieldId={getStorageMapFieldId(StorageMapFieldId.TargetStorage, index)}
targetStorages={targetStorages}
testId={`target-storage-${getStorageMapFieldId(StorageMapFieldId.TargetStorage, index)}`}
/>,
],
}))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const InventorySourceStorageField: FC<InventorySourceStorageFieldProps> = ({
<Select
ref={field.ref}
id={fieldId}
testId={`source-storage-${fieldId}`}
isDisabled={isSubmitting}
value={(field.value as StorageMappingValue).name}
onSelect={async (_event, value) => {
Expand Down
1 change: 1 addition & 0 deletions src/storageMaps/create/fields/SourceProviderField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const SourceProviderField: FC = () => {
isDisabled={isSubmitting}
placeholder={t('Select source provider')}
id={StorageMapFieldId.SourceProvider}
testId="source-provider-select"
namespace={project}
value={field.value?.metadata?.name ?? ''}
onSelect={(_, value) => {
Expand Down
1 change: 1 addition & 0 deletions src/storageMaps/create/fields/TargetProviderField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const TargetProviderField: FC = () => {
isDisabled={isSubmitting}
placeholder={t('Select target provider')}
id={StorageMapFieldId.TargetProvider}
testId="target-provider-select"
namespace={project}
value={field.value?.metadata?.name ?? ''}
onSelect={(_, value) => {
Expand Down
2 changes: 1 addition & 1 deletion src/storageMaps/details/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const transformFormValuesToK8sSpec = (
export const transformStorageMapToFormValues = (
storageMap: V1beta1StorageMap,
): UpdateMappingsFormData => {
if (isEmpty(storageMap) || !storageMap?.spec?.map) {
if (isEmpty(storageMap) || !storageMap?.spec?.map || isEmpty(storageMap.spec.map)) {
return {
storageMap: [defaultStorageMapping],
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { expect } from '@playwright/test';

import { sharedProviderFixtures as test } from '../../../fixtures/resourceFixtures';
import { PlanDetailsPage } from '../../../page-objects/PlanDetailsPage/PlanDetailsPage';
import { OffloadPlugins, OffloadSecrets, StorageProducts } from '../../../types/test-data';
import { V2_11_0 } from '../../../utils/version/constants';
import { requireVersion } from '../../../utils/version/version';

test.describe('Storage Offloading - Plan Details Mappings Tab', { tag: '@downstream' }, () => {
requireVersion(test, V2_11_0);

test('should display storage mappings and allow editing offload from plan Mappings tab', async ({
page,
testPlan,
testProvider: _testProvider,
}) => {
if (!testPlan) throw new Error('testPlan is required');

const planDetailsPage = new PlanDetailsPage(page);

await test.step('Navigate to Mappings tab', async () => {
await planDetailsPage.mappingsTab.navigateToMappingsTab();
await planDetailsPage.mappingsTab.verifyMappingsTab();
});

await test.step('Verify storage map section has mappings', async () => {
await planDetailsPage.mappingsTab.storageMapReviewTable.waitFor({ state: 'visible' });

const storageMappingCount =
await planDetailsPage.mappingsTab.getStorageMappingCountFromReviewTable();
expect(storageMappingCount).toBeGreaterThan(0);

const storageMapName = await planDetailsPage.mappingsTab.getStorageMapName();
expect(storageMapName).toBeTruthy();
});

await test.step('Open storage map edit modal and verify offload options', async () => {
const modal = await planDetailsPage.mappingsTab.openStorageMapEditModal();
await modal.verifyModalStructure();

const mappingCount = await modal.getMappingCount();
expect(mappingCount).toBeGreaterThan(0);

for (let i = 0; i < mappingCount; i += 1) {
await modal.offload.verifyOffloadToggleVisible(i);
}

await modal.verifySaveButtonDisabled();
await modal.cancel();
});

await test.step('Configure offload options and save', async () => {
const modal = await planDetailsPage.mappingsTab.openStorageMapEditModal();

await modal.offload.expandOffloadOptions(0);
await modal.offload.verifyAllDropdownsVisible(0);

await modal.offload.selectOffloadPlugin(0, OffloadPlugins.VSPHERE_XCOPY);
await modal.offload.selectStorageSecret(0, OffloadSecrets.VS8_SECRET);
await modal.offload.selectStorageProduct(0, StorageProducts.NETAPP_ONTAP);

await modal.verifySaveButtonEnabled();
await modal.save();
});

await test.step('Verify Mappings tab is still functional after save', async () => {
await planDetailsPage.mappingsTab.verifyMappingsTab();

const modal = await planDetailsPage.mappingsTab.openStorageMapEditModal();
await modal.verifyModalStructure();

const mappingCount = await modal.getMappingCount();
expect(mappingCount).toBeGreaterThan(0);

await modal.offload.verifyOffloadToggleVisible(0);
await modal.cancel();
});
});
});
26 changes: 24 additions & 2 deletions testing/playwright/e2e/downstream/plans/plan-hooks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { requireVersion } from '../../../utils/version/version';

const HOOK_RUNNER_IMAGE = 'quay.io/konveyor/hook-runner:latest';
const UPDATED_HOOK_RUNNER_IMAGE = 'quay.io/konveyor/hook-runner:v0.2.0';
const PRE_HOOK_SERVICE_ACCOUNT = 'pre-hook-sa';
const POST_HOOK_SERVICE_ACCOUNT = 'post-hook-sa';
const UPDATED_SERVICE_ACCOUNT = 'updated-hook-sa';

const loadPlaybookFromTemplate = (templatePath: string, hookName: string): string => {
const absolutePath = path.resolve(__dirname, '../../..', templatePath);
Expand Down Expand Up @@ -75,6 +78,7 @@ test.describe('Plan Hooks', { tag: '@downstream' }, () => {
await wizard.hooks.configurePreMigrationHook({
enabled: true,
hookRunnerImage: HOOK_RUNNER_IMAGE,
serviceAccount: PRE_HOOK_SERVICE_ACCOUNT,
ansiblePlaybook: preHookPlaybook,
});
});
Expand All @@ -83,14 +87,28 @@ test.describe('Plan Hooks', { tag: '@downstream' }, () => {
await wizard.hooks.configurePostMigrationHook({
enabled: true,
hookRunnerImage: HOOK_RUNNER_IMAGE,
serviceAccount: POST_HOOK_SERVICE_ACCOUNT,
ansiblePlaybook: postHookPlaybook,
});
});

await test.step('Verify hooks in review step', async () => {
await wizard.clickNext();
await wizard.review.verifyStepVisible();
await wizard.review.verifyHooksSection();
await wizard.review.verifyHooksSection(
{
enabled: true,
hookRunnerImage: HOOK_RUNNER_IMAGE,
serviceAccount: PRE_HOOK_SERVICE_ACCOUNT,
ansiblePlaybook: preHookPlaybook,
},
{
enabled: true,
hookRunnerImage: HOOK_RUNNER_IMAGE,
serviceAccount: POST_HOOK_SERVICE_ACCOUNT,
ansiblePlaybook: postHookPlaybook,
},
);
});

await test.step('Create plan', async () => {
Expand All @@ -106,13 +124,17 @@ test.describe('Plan Hooks', { tag: '@downstream' }, () => {
await planDetailsPage.hooksTab.verifyPostMigrationHookEnabled(true);
await planDetailsPage.hooksTab.verifyHookRunnerImage('pre', HOOK_RUNNER_IMAGE);
await planDetailsPage.hooksTab.verifyHookRunnerImage('post', HOOK_RUNNER_IMAGE);
await planDetailsPage.hooksTab.verifyServiceAccount('pre', PRE_HOOK_SERVICE_ACCOUNT);
await planDetailsPage.hooksTab.verifyServiceAccount('post', POST_HOOK_SERVICE_ACCOUNT);
});

await test.step('Edit pre-migration hook with new image', async () => {
await test.step('Edit pre-migration hook with new image and service account', async () => {
await planDetailsPage.hooksTab.openPreMigrationHookEditModal();
await planDetailsPage.hooksTab.hookEditModal.setHookRunnerImage(UPDATED_HOOK_RUNNER_IMAGE);
await planDetailsPage.hooksTab.hookEditModal.setServiceAccount(UPDATED_SERVICE_ACCOUNT);
await planDetailsPage.hooksTab.hookEditModal.save();
await planDetailsPage.hooksTab.verifyHookRunnerImage('pre', UPDATED_HOOK_RUNNER_IMAGE);
await planDetailsPage.hooksTab.verifyServiceAccount('pre', UPDATED_SERVICE_ACCOUNT);
});

await test.step('Remove pre-migration hook', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { expect } from '@playwright/test';

import { providerOnlyFixtures as test } from '../../../fixtures/resourceFixtures';
import { CreatePlanWizardPage } from '../../../page-objects/CreatePlanWizard/CreatePlanWizardPage';
import {
createPlanTestData,
OffloadPluginK8sValues,
OffloadPlugins,
OffloadSecrets,
StorageProductK8sValues,
StorageProducts,
} from '../../../types/test-data';
import { MTV_NAMESPACE } from '../../../utils/resource-manager/constants';
import { V2_11_0 } from '../../../utils/version/constants';
import { requireVersion } from '../../../utils/version/version';

test.describe(
'Storage Offloading - Plan Wizard with Offload + Review',
{ tag: '@downstream' },
() => {
requireVersion(test, V2_11_0);

test('should configure offload in plan wizard and display it correctly in review', async ({
page,
resourceManager,
testProvider,
}) => {
if (!testProvider) throw new Error('testProvider is required');

const planName = `offload-test-${crypto.randomUUID().slice(0, 8)}`;
const testPlanData = createPlanTestData({
planName,
sourceProvider: testProvider.metadata.name,
targetProject: { name: 'default', isPreexisting: true },
storageMap: {
name: `${planName}-storage-map`,
isPreexisting: false,
},
});

const wizard = new CreatePlanWizardPage(page, resourceManager);

await test.step('Navigate to Storage Map step', async () => {
await wizard.navigate();
await wizard.navigateToStorageMapStep(testPlanData);
});

await test.step('Verify offload options visible on Storage Map step', async () => {
await wizard.storageMap.verifyStepVisible();
await wizard.storageMap.waitForData();

await wizard.storageMap.useNewStorageMapRadio.check();

await wizard.storageMap.offload.verifyOffloadToggleVisible(0);
});

await test.step('Configure offload options', async () => {
await wizard.storageMap.offload.expandOffloadOptions(0);
await wizard.storageMap.offload.verifyAllDropdownsVisible(0);

await wizard.storageMap.offload.selectOffloadPlugin(0, OffloadPlugins.VSPHERE_XCOPY);
await wizard.storageMap.offload.selectStorageSecret(0, OffloadSecrets.VS8_SECRET);
await wizard.storageMap.offload.selectStorageProduct(0, StorageProducts.NETAPP_ONTAP);
});

await test.step('Proceed past Storage Map and skip to review', async () => {
await wizard.clickNext();
await wizard.clickSkipToReview();
});

await test.step('Verify offload details in review', async () => {
await wizard.review.verifyStepVisible();

await expect(wizard.review.storageMapSection).toBeVisible();

await wizard.review.verifyStorageMapOffloadDetails(0, {
offloadPlugin: OffloadPluginK8sValues.VSPHERE_XCOPY,
storageProduct: StorageProductK8sValues.NETAPP_ONTAP,
storageSecret: OffloadSecrets.VS8_SECRET,
});
});

await test.step('Create plan and register for cleanup', async () => {
await wizard.clickNext();
await wizard.waitForPlanCreation();
resourceManager.addPlan(planName, MTV_NAMESPACE);
});
});
},
);
Loading