diff --git a/e2etests/mocks/recommendation-card-metadata.mocks.ts b/e2etests/mocks/recommendation-card-metadata.mocks.ts index 529c3ca0d..579d9be4e 100644 --- a/e2etests/mocks/recommendation-card-metadata.mocks.ts +++ b/e2etests/mocks/recommendation-card-metadata.mocks.ts @@ -120,15 +120,7 @@ export const getCardMetaData = (recommendationsPage: RecommendationsPage): CardM tableLocator: recommendationsPage.notDeallocatedInstancesTableSavingsValue, modalColumnLocator: recommendationsPage.modalColumn6, }, - { - name: 'Obsolete Images', - savingsValue: recommendationsPage.obsoleteImagesCardSavingsValue, - seeAllBtn: recommendationsPage.obsoleteImagesSeeAllBtn, - errorLocator: recommendationsPage.obsoleteImagesError, - tableLocator: recommendationsPage.obsoleteImagesTableSavingsValue, - modalColumnLocator: recommendationsPage.modalColumn6, - }, - { + { name: 'Obsolete IPs', savingsValue: recommendationsPage.obsoleteIPsCardSavingsValue, seeAllBtn: recommendationsPage.obsoleteIPsSeeAllBtn, @@ -174,6 +166,14 @@ export const getCardMetaData = (recommendationsPage: RecommendationsPage): CardM seeAllBtn: recommendationsPage.resourcesWithInsecureSecurityGroupsSettingsSeeAllBtn, errorLocator: recommendationsPage.resourcesWithInsecureSecurityGroupsSettingsError, }, + { + name: 'Snapshots with non-used Images', + savingsValue: recommendationsPage.snapshotsWithNonUsedImagesCardSavingsValue, + seeAllBtn: recommendationsPage.snapshotsWithNonUsedImagesSeeAllBtn, + errorLocator: recommendationsPage.snapshotsWithNonUsedImagesError, + tableLocator: recommendationsPage.snapshotsWithNonUsedImagesTableSavingsValue, + modalColumnLocator: undefined, //TODO: Unable to determine which column this card uses without any savings data + }, { name: 'Under Utilized Instances', savingsValue: recommendationsPage.underutilizedInstancesCardSavingsValue, diff --git a/e2etests/pages/header.ts b/e2etests/pages/header.ts index 90170bf79..a64de8c0d 100644 --- a/e2etests/pages/header.ts +++ b/e2etests/pages/header.ts @@ -1,5 +1,6 @@ import { BasePage } from './base-page'; import { Locator, Page } from '@playwright/test'; +import { EEnvironment } from '../types/enums'; /** * Represents the Header component of the page. @@ -68,10 +69,10 @@ export class Header extends BasePage { getOrganizationNameForEnvironment(): string { const env = process.env.ENVIRONMENT; switch (env) { - case 'dev': - case 'test': + case EEnvironment.DEV: + case EEnvironment.TEST: return 'SoftwareOne (Test Env'; - case 'staging': + case EEnvironment.STAGING: return 'Marketplace Platform'; default: diff --git a/e2etests/pages/recommendations-page.ts b/e2etests/pages/recommendations-page.ts index 9464fad16..2fe6be847 100644 --- a/e2etests/pages/recommendations-page.ts +++ b/e2etests/pages/recommendations-page.ts @@ -150,10 +150,6 @@ export class RecommendationsPage extends BasePage { readonly notDeallocatedInstancesSeeAllBtn: Locator; readonly notDeallocatedInstancesError: Locator; readonly notDeallocatedInstancesTableSavingsValue: Locator; - readonly obsoleteImagesCardSavingsValue: Locator; - readonly obsoleteImagesSeeAllBtn: Locator; - readonly obsoleteImagesError: Locator; - readonly obsoleteImagesTableSavingsValue: Locator; readonly obsoleteIPsCardSavingsValue: Locator; readonly obsoleteIPsSeeAllBtn: Locator; readonly obsoleteIPsError: Locator; @@ -189,6 +185,10 @@ export class RecommendationsPage extends BasePage { readonly underutilizedRDSInstancesSeeAllBtn: Locator; readonly underutilizedRDSInstancesError: Locator; readonly underutilizedRDSInstancesTableSavingsValue: Locator; + readonly snapshotsWithNonUsedImagesCardSavingsValue: Locator; + readonly snapshotsWithNonUsedImagesSeeAllBtn: Locator; + readonly snapshotsWithNonUsedImagesError: Locator; + readonly snapshotsWithNonUsedImagesTableSavingsValue: Locator; /** * Initializes a new instance of the RecommendationsPage class. @@ -323,13 +323,13 @@ export class RecommendationsPage extends BasePage { intelligentTiering: 'Intelligent Tiering', notAttachedVolumes: 'Not attached Volumes', notDeallocatedInstances: 'Not deallocated Instances', - obsoleteImages: 'Obsolete images', obsoleteIPs: 'Obsolete IPs', obsoleteSnapshotChains: 'Obsolete snapshot chains', obsoleteSnapshots: 'Obsolete snapshots', publicS3Buckets: 'Public S3 buckets', reservedInstancesOpportunities: 'Reserved instances opportunities', resourcesWithInsecureSecurityGroupsSettings: 'Resources with insecure Security Groups settings', + snapshotsWithNonUsedImages: 'Snapshots with non-used Images', underutilizedInstances: 'Underutilized instances', underutilizedRDSInstances: 'Underutilized RDS Instances', }; @@ -365,21 +365,6 @@ export class RecommendationsPage extends BasePage { await this.selectFromComboBox(this.dataSourcesSelect, dataSource, true); } - /** - * Clicks the RI/SP card on the Recommendations page. - * - * This method interacts with the `ri_spCard` locator to simulate a user clicking - * on the RI/SP card element. The action is typically used to navigate to a specific - * section or trigger functionality associated with the RI/SP card. - * - * @returns {Promise} Resolves when the click action is complete. - */ - async clickRI_SPCard(): Promise { - await this.page.waitForLoadState(); - await this.delay(2000); - await this.ri_spCard.click(); - } - /** * Selects a category from the categories combo box. * @param {string} category - The category to select. @@ -452,27 +437,6 @@ export class RecommendationsPage extends BasePage { await this.s3DuplicatesCard.click(); } - /** - * Retrieves the saved amount with commitments value from the page. - * Parses the text content of the saved value element into a numeric value. - * - * @returns {Promise} The parsed saved amount with commitments value. - */ - async getSavedWithCommitmentsValue(): Promise { - const value = await this.savedWithCommitmentsValue.textContent(); - return this.parseCurrencyValue(value); - } - - /** - * Retrieves the percentage of saved expenses covered with commitments. - * Extracts the text content of the relevant element. - * - * @returns {Promise} The percentage value as a string. - */ - async getSavedExpensesWithCommitmentsPercentageValue(): Promise { - return await this.computeExpensesWithCommitmentsValue.textContent(); - } - /** * Retrieves a currency value given its locator. * @@ -518,11 +482,12 @@ export class RecommendationsPage extends BasePage { { label: 'Intelligent Tiering', locator: this.intelligentTieringCardSavingsValue }, { label: 'Not Attached Volumes', locator: this.notAttachedVolumesCardSavingsValue }, { label: 'Not Deallocated Instances', locator: this.notDeallocatedInstancesCardSavingsValue }, - { label: 'Obsolete Images', locator: this.obsoleteImagesCardSavingsValue }, { label: 'Obsolete IPs', locator: this.obsoleteIPsCardSavingsValue }, { label: 'Obsolete Snapshots', locator: this.obsoleteSnapshotsCardSavingsValue }, { label: 'Obsolete Snapshot Chains', locator: this.obsoleteSnapshotChainsCardSavingsValue }, { label: 'Reserved Instances Opportunities', locator: this.reservedInstancesOpportunitiesCardSavingsValue }, + { label: 'Public S3 Buckets', locator: this.publicS3BucketsCardSavingsValue }, + { label: 'Snapshots With Non-used Images', locator: this.snapshotsWithNonUsedImagesCardSavingsValue}, { label: 'Underutilized Instances', locator: this.underutilizedInstancesCardSavingsValue }, { label: 'Underutilized RDS Instances', locator: this.underutilizedRDSInstancesCardSavingsValue }, ]; diff --git a/e2etests/tests/anomalies-tests.spec.ts b/e2etests/tests/anomalies-tests.spec.ts index c8e296e92..5fd1d22da 100644 --- a/e2etests/tests/anomalies-tests.spec.ts +++ b/e2etests/tests/anomalies-tests.spec.ts @@ -143,8 +143,8 @@ test.describe('[MPT-14737] Anomalies Tests', { tag: ['@ui', '@anomalies'] }, () // Validate timestamps are reasonable (within last 14 days for safety) const now = Math.floor(Date.now() / 1000); - const thirtyDaysAgo = now - 14 * 86400; - expect.soft(oldestTimestamp).toBeGreaterThan(thirtyDaysAgo); + const fifteenDaysAgo = now - 15 * 86400; + expect.soft(oldestTimestamp).toBeGreaterThan(fifteenDaysAgo); expect.soft(latestTimestamp).toBeLessThanOrEqual(now); // Validate each breakdown value is a number diff --git a/e2etests/tests/pools-tests.spec.ts b/e2etests/tests/pools-tests.spec.ts index 2aa2204a1..51a70b9ba 100644 --- a/e2etests/tests/pools-tests.spec.ts +++ b/e2etests/tests/pools-tests.spec.ts @@ -351,7 +351,7 @@ test.describe('[MPT-12743] Pools Tests', { tag: ['@ui', '@pools'] }, () => { const multiplier = extractMultiplier(await poolsPage.subPoolColumn4.first().textContent()); subPoolForecast = await poolsPage.getSubPoolForecastThisMonth(1); - expect.soft(multiplier).toBe(calculateMultiplier(subPoolForecast, subPoolLimit)); + expect.soft(isWithinRoundingDrift(multiplier, calculateMultiplier(subPoolForecast, subPoolLimit), 0.1)).toBe(true); }); await test.step('Assert that expand requiring attention does expand when sub-pools limits are exceeded', async () => { diff --git a/e2etests/tests/recommendations-tests.spec.ts b/e2etests/tests/recommendations-tests.spec.ts index 3eb93d9e4..524b9d17e 100644 --- a/e2etests/tests/recommendations-tests.spec.ts +++ b/e2etests/tests/recommendations-tests.spec.ts @@ -18,7 +18,7 @@ test.describe('[MPT-11310] Recommendations page tests', { tag: ['@ui', '@recomme await recommendationsPage.selectCategory('All'); }); - test('[230511] Verify Card total savings match possible monthly savings', {tag: '@p1'}, async ({ recommendationsPage }) => { + test('[230511] Verify Card total savings match possible monthly savings', { tag: '@p1' }, async ({ recommendationsPage }) => { let possibleMonthlySavings: number; let cardTotalSavings: number; @@ -68,6 +68,7 @@ test.describe('[MPT-11310] Recommendations page tests', { tag: ['@ui', '@recomme await test.step('Verify behaviour is correct if no completed duplicate checks', async () => { debugLog('No successfully completed checks found.'); + // eslint-disable-next-line playwright/no-nested-step await test.step('Verify S3 Duplicate Finder table shows no duplicate checks message', async () => { await recommendationsPage.clickS3DuplicatesCard(); await expect @@ -99,59 +100,43 @@ test.describe('[MPT-11310] Recommendations page tests', { tag: ['@ui', '@recomme await expect(recommendationsPage.allCardHeadings.first()).toHaveText('Public S3 buckets'); }); - test('[230598] Verify only the correct applicable services are displayed for SWO Customisation', {tag: '@p1'}, async ({ recommendationsPage }) => { - await test.step('Verify applicable services combo box options shows expected items', async () => { - await recommendationsPage.applicableServices.click(); - const expectedVisibleServices = [ - recommendationsPage.liAwsIAM, - recommendationsPage.liAwsEC2, - recommendationsPage.liAwsEC2EBS, - recommendationsPage.liAwsEC2VPC, - recommendationsPage.liAwsRDS, - recommendationsPage.liAwsS3, - recommendationsPage.liAwsKinesis, - recommendationsPage.liAzureCompute, - recommendationsPage.liAzureNetwork, - recommendationsPage.liGcpIAM, - recommendationsPage.liGcpCloudStorage, - ]; - const expectedHiddenServices = [ - recommendationsPage.liAliBabaECS, - recommendationsPage.liAliBabaVPC, - recommendationsPage.liAliBabaEBS, - recommendationsPage.liAliBabaSLB, - ]; - - for (const service of expectedVisibleServices) { - await expect.soft(service).toBeVisible(); - } - for (const service of expectedHiddenServices) { - await expect.soft(service).toBeHidden(); - } - }); - - await test.step('Verify that no AliBaba applicable services are displayed on any cards', async () => { - await recommendationsPage.liAll.click(); - await recommendationsPage.allCardHeadings.last().waitFor(); - const aliBabaIcons = [ - recommendationsPage.aliBabaEBS_Icon, - recommendationsPage.aliBabaECS_Icon, - recommendationsPage.aliBabaSLB_Icon, - recommendationsPage.aliBabaECS_VPC_Icon, - recommendationsPage.aliBabaRDS_Icon, - ]; - - for (const icon of aliBabaIcons) { - await expect.soft(icon).toBeHidden(); - } - }); + test( + '[230598] Verify only the correct applicable services are displayed for SWO Customisation', + { tag: '@p1' }, + async ({ recommendationsPage }) => { + await test.step('Verify applicable services combo box options shows expected items', async () => { + await recommendationsPage.applicableServices.click(); + const expectedVisibleServices = [ + recommendationsPage.liAwsIAM, + recommendationsPage.liAwsEC2, + recommendationsPage.liAwsEC2EBS, + recommendationsPage.liAwsEC2VPC, + recommendationsPage.liAwsRDS, + recommendationsPage.liAwsS3, + recommendationsPage.liAwsKinesis, + recommendationsPage.liAzureCompute, + recommendationsPage.liAzureNetwork, + recommendationsPage.liGcpIAM, + recommendationsPage.liGcpCloudStorage, + ]; + const expectedHiddenServices = [ + recommendationsPage.liAliBabaECS, + recommendationsPage.liAliBabaVPC, + recommendationsPage.liAliBabaEBS, + recommendationsPage.liAliBabaSLB, + ]; - await test.step('Verify that no AliBaba applicable services are displayed in the applicable services column of the table', async () => { - await recommendationsPage.clickTableButton(); - await recommendationsPage.allNameTableButtons.last().waitFor(); - const cells = await recommendationsPage.applicableServicesColumn.all(); + for (const service of expectedVisibleServices) { + await expect.soft(service).toBeVisible(); + } + for (const service of expectedHiddenServices) { + await expect.soft(service).toBeHidden(); + } + }); - for (const cell of cells) { + await test.step('Verify that no AliBaba applicable services are displayed on any cards', async () => { + await recommendationsPage.liAll.click(); + await recommendationsPage.allCardHeadings.last().waitFor(); const aliBabaIcons = [ recommendationsPage.aliBabaEBS_Icon, recommendationsPage.aliBabaECS_Icon, @@ -159,12 +144,32 @@ test.describe('[MPT-11310] Recommendations page tests', { tag: ['@ui', '@recomme recommendationsPage.aliBabaECS_VPC_Icon, recommendationsPage.aliBabaRDS_Icon, ]; + for (const icon of aliBabaIcons) { - await expect.soft(cell.locator(icon)).toBeHidden(); + await expect.soft(icon).toBeHidden(); } - } - }); - }); + }); + + await test.step('Verify that no AliBaba applicable services are displayed in the applicable services column of the table', async () => { + await recommendationsPage.clickTableButton(); + await recommendationsPage.allNameTableButtons.last().waitFor(); + const cells = await recommendationsPage.applicableServicesColumn.all(); + + for (const cell of cells) { + const aliBabaIcons = [ + recommendationsPage.aliBabaEBS_Icon, + recommendationsPage.aliBabaECS_Icon, + recommendationsPage.aliBabaSLB_Icon, + recommendationsPage.aliBabaECS_VPC_Icon, + recommendationsPage.aliBabaRDS_Icon, + ]; + for (const icon of aliBabaIcons) { + await expect.soft(cell.locator(icon)).toBeHidden(); + } + } + }); + } + ); const verifyCardsAndTable = async (recommendationsPage: any, category: string, expectedCardHeadings: string[]) => { await recommendationsPage.selectCategory(category); @@ -208,16 +213,17 @@ test.describe('[MPT-11310] Recommendations page tests', { tag: ['@ui', '@recomme 'Intelligent Tiering', 'Not attached Volumes', 'Not deallocated Instances', - 'Obsolete images', 'Obsolete IPs', 'Obsolete snapshot chains', 'Obsolete snapshots', 'Public S3 buckets', 'Reserved instances opportunities', + 'Snapshots with non-used Images', 'Underutilized instances', 'Underutilized RDS Instances', ]; + // eslint-disable-next-line playwright/expect-expect test('[230515] Verify all expected cards are present when All category selected', async ({ recommendationsPage }) => { await verifyCardsAndTable(recommendationsPage, 'All', allExpectedCardHeadings); }); @@ -239,6 +245,7 @@ test.describe('[MPT-11310] Recommendations page tests', { tag: ['@ui', '@recomme } expect(errorCount, 'No cards should be displaying errors').toBe(0); }); + // eslint-disable-next-line playwright/expect-expect test('[230518] Verify all expected cards are present when Savings category selected', async ({ recommendationsPage }) => { const expectedCardHeadings = [ 'Abandoned Amazon S3 buckets', @@ -254,17 +261,18 @@ test.describe('[MPT-11310] Recommendations page tests', { tag: ['@ui', '@recomme 'Intelligent Tiering', 'Not attached Volumes', 'Not deallocated Instances', - 'Obsolete images', 'Obsolete IPs', 'Obsolete snapshot chains', 'Obsolete snapshots', 'Reserved instances opportunities', + 'Snapshots with non-used Images', 'Underutilized instances', 'Underutilized RDS Instances', ]; await verifyCardsAndTable(recommendationsPage, 'Savings', expectedCardHeadings); }); + // eslint-disable-next-line playwright/expect-expect test('[230519] Verify all expected cards are present when Security category selected', async ({ recommendationsPage }) => { const expectedCardHeadings = [ 'IAM users with unused console access', @@ -374,13 +382,13 @@ test.describe('[MPT-11310] Recommendations page tests', { tag: ['@ui', '@recomme 'Intelligent Tiering', 'Not Attached Volumes', 'Not Deallocated Instances', - 'Obsolete Images', 'Obsolete IPs', 'Obsolete Snapshot Chains', 'Obsolete Snapshots', `Public S3 Buckets`, 'Reserved Instances Opportunities', `Resources With Insecure Security Groups Settings`, + 'Snapshots with non-used Images', 'Under Utilized Instances', 'Under Utilized RDS Instances', ];