From 188e3b26ab9f1e6657e50b5eab446f23ae1a864a Mon Sep 17 00:00:00 2001 From: Ihor Mykhno Date: Tue, 8 Sep 2026 21:49:49 +0200 Subject: [PATCH 1/2] test(scorecard): add license file exists KPI and drill-down entity coverage Signed-off-by: Ihor Mykhno imykhno@redhat.com Assisted-By: Cursor --- .../e2e-tests/constants/aggregations.ts | 7 ++ .../app-legacy/e2e-tests/constants/routes.ts | 2 + .../e2e-tests/pages/ScorecardDrillDownPage.ts | 72 +++++++++-- .../app-legacy/e2e-tests/scorecard.test.ts | 114 ++++++++++++++++++ .../e2e-tests/utils/homepageWidgetUtils.ts | 13 +- .../utils/mockHomepageAggregations.ts | 16 ++- .../e2e-tests/utils/scorecardResponseUtils.ts | 104 ++++++++++++++++ 7 files changed, 313 insertions(+), 15 deletions(-) diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/constants/aggregations.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/constants/aggregations.ts index 521a09f9955..03d5d59afa9 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/constants/aggregations.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/constants/aggregations.ts @@ -20,6 +20,7 @@ export const AGGREGATED_CARDS_METRIC_IDS = { githubOpenPrsKpi: 'openPrsKpi', jiraOpenIssuesKpi: 'openIssuesKpi', gitHubOpenPrsWeightedKpi: 'openPrsWeightedKpi', + licenseFileExistsKpi: 'licenseFileExistsKpi', } as const; /** Must match `title` in App.tsx homepage widget config (Add widget picker). */ @@ -29,6 +30,7 @@ export const AGGREGATED_CARDS_WIDGET_TITLES = { githubOpenPrsKpi: 'Scorecard: GitHub open PRs', jiraOpenIssuesKpi: 'Scorecard: Jira open blocking tickets', gitHubOpenPrsWeightedKpi: 'Scorecard: GitHub open PRs (weighted health)', + licenseFileExistsKpi: 'Scorecard: LICENSE file exists', } as const; export const AGGREGATED_CARDS_METADATA = { @@ -57,4 +59,9 @@ export const AGGREGATED_CARDS_METADATA = { title: 'Scorecard: GitHub open PRs (weighted health)', metricId: 'github.openPRs', }, + licenseFileExistsKpi: { + id: AGGREGATED_CARDS_METRIC_IDS.licenseFileExistsKpi, + title: 'Scorecard: LICENSE file exists', + metricId: 'filecheck.license', + }, } as const; diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/constants/routes.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/constants/routes.ts index 7d0c7fcea6b..a8c99abb4d0 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/constants/routes.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/constants/routes.ts @@ -33,4 +33,6 @@ export const ScorecardRoutes = { '**/api/scorecard/aggregations/jira.openIssues', GITHUB_OPEN_PRS_METRIC_AGGREGATION_ROUTE: '**/api/scorecard/aggregations/github.openPRs', + LICENSE_FILE_EXISTS_KPI_AGGREGATION_ROUTE: + '**/api/scorecard/aggregations/licenseFileExistsKpi', } as const; diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/ScorecardDrillDownPage.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/ScorecardDrillDownPage.ts index b10b3e5dfc4..c86a6889745 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/ScorecardDrillDownPage.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/ScorecardDrillDownPage.ts @@ -23,10 +23,11 @@ import { getEntitiesPageMissingPermission, getEntitiesPageNoDataFound, getEntitiesTableHeaderLabels, + getHomepageEntityCalculationHealthText, getSomeEntitiesNotReportingTooltip, } from '../utils/translationUtils'; -type MetricId = 'github.openPRs' | 'jira.openIssues'; +type MetricId = 'github.openPRs' | 'jira.openIssues' | 'filecheck.license'; export type DrillDownCardLocatorOptions = { aggregationId?: string; @@ -167,19 +168,50 @@ export class ScorecardDrillDownPage { * When mocks report no calculation failures, the drill-down must not show the * calculation-warning icon next to the Entities heading. */ - async expectNoDrillDownCalculationErrorWarningIcon() { - const heading = this.page.getByRole('heading', { - level: 3, - name: this.translations.entitiesPage.entitiesTable.title, + private getEntitiesTableHeading(): Locator { + const titlePrefix = this.translations.entitiesPage.entitiesTable.title; + return this.page.getByRole('heading', { level: 3 }).filter({ + hasText: titlePrefix, }); - await expect(heading.locator('svg.MuiSvgIcon-colorWarning')).toHaveCount(0); } - /** Verifies the "some entities not reporting" icon tooltip on the drill-down card. */ - async verifySomeEntitiesNotReportingTooltip() { - const icon = this.page.getByTestId('ReportProblemOutlinedIcon'); - await expect(icon).toBeVisible(); - await icon.hover(); + private getEntitiesTableCalculationWarningIcon(): Locator { + return this.getEntitiesTableHeading().locator('svg'); + } + + async expectNoDrillDownCalculationErrorWarningIcon() { + await expect(this.getEntitiesTableCalculationWarningIcon()).toHaveCount(0); + } + + async expectDrillDownCalculationErrorWarningIcon() { + await expect(this.getEntitiesTableCalculationWarningIcon()).toBeVisible(); + } + + /** Verifies the calculation-error tooltip on the Entities table heading icon. */ + async verifyEntitiesTableCalculationErrorTooltip() { + await this.getEntitiesTableCalculationWarningIcon().hover(); + const tooltipText = getSomeEntitiesNotReportingTooltip(this.translations); + await expect(this.page.getByRole('tooltip')).toContainText(tooltipText); + } + + /** Verifies the "some entities not reporting" tooltip on the drill-down card subheader link. */ + async verifySomeEntitiesNotReportingTooltip( + metricId: MetricId, + options?: DrillDownCardLocatorOptions & { + healthy?: string; + total?: string; + }, + ) { + const card = this.getDrillDownCard(metricId, options); + const healthy = options?.healthy ?? '8'; + const total = options?.total ?? '10'; + const linkText = getHomepageEntityCalculationHealthText( + this.translations, + healthy, + total, + ); + const link = card.getByRole('link', { name: linkText }); + await link.hover(); const tooltipText = getSomeEntitiesNotReportingTooltip(this.translations); await expect(this.page.getByRole('tooltip')).toContainText(tooltipText); } @@ -220,6 +252,24 @@ export class ScorecardDrillDownPage { } } + async clickEntityLink(entitySlug: string) { + const entitiesTable = this.getEntitiesTable(); + const slug = encodeURIComponent(entitySlug); + const link = entitiesTable + .locator('tbody') + .locator(`a[href*="/catalog/default/component/${slug}"]`) + .first(); + await link.click(); + await expect(this.page).toHaveURL( + new RegExp( + `/catalog/default/component/${slug.replace( + /[.*+?^${}()|[\]\\]/g, + '\\$&', + )}`, + ), + ); + } + async verifyMetricColumnSort() { const tableHeaders = getEntitiesTableHeaderLabels(this.translations); const entitiesTable = this.getEntitiesTable(); diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/scorecard.test.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/scorecard.test.ts index 4da130c7827..192f74b2bda 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/scorecard.test.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/scorecard.test.ts @@ -51,6 +51,9 @@ import { githubCustomAggregatedResponse, gitHubPartiallyAggregatedResponse, gitHubWeightedPartiallyAggregatedResponse, + licenseFileExistsAggregatedResponse, + githubEntitiesDrillDownWithCalculationErrorsResponse, + weightedKpiEntitiesDrillDownResponse, } from './utils/scorecardResponseUtils'; import { ScorecardMessages, @@ -471,6 +474,50 @@ test.describe('Scorecard Plugin Tests', () => { } }); + test.describe('Filecheck homepage KPI - licenseFileExistsKpi', () => { + let card: Locator; + const aggregationMetadata = + AGGREGATED_CARDS_METADATA.licenseFileExistsKpi; + const aggregatedResponse = licenseFileExistsAggregatedResponse; + + test.beforeAll(async () => { + await setupHomepageAggregationCard(page, homePage, { + aggregationMetadata, + route: ScorecardRoutes.LICENSE_FILE_EXISTS_KPI_AGGREGATION_ROUTE, + response: aggregatedResponse, + }); + card = homePage.getCard(aggregationMetadata.id); + }); + + test('Verify title and description', async () => { + await expect(card).toBeVisible(); + await expect(card).toContainText(aggregatedResponse.metadata.title); + await expect(card).toContainText( + aggregatedResponse.metadata.description, + ); + }); + + test('Verify exist and missing threshold buckets', async () => { + const existLabel = translations.thresholds.exist ?? 'Exist'; + const missingLabel = translations.thresholds.missing ?? 'Missing'; + await expect(card.getByText(existLabel, { exact: true })).toBeVisible(); + await expect( + card.getByText(missingLabel, { exact: true }), + ).toBeVisible(); + }); + + test('Verify drill-down link', async () => { + await homePage.clickDrillDownLink(card); + await scorecardDrillDownPage.expectOnPage('filecheck.license', { + aggregationId: aggregationMetadata.id, + }); + await scorecardDrillDownPage.expectPageTitle( + 'filecheck.license', + aggregatedResponse.metadata.title, + ); + }); + }); + test.describe('Deprecated homepage card (metricId only)', () => { let card: Locator; const aggregationMetadata = @@ -789,6 +836,26 @@ test.describe('Scorecard Plugin Tests', () => { openPrsWeightedAggregatedResponse.metadata.title, ); }); + + test('Verify drill-down entities table', async () => { + await mockScorecardEntitiesDrillDown( + page, + weightedKpiEntitiesDrillDownResponse, + 'github.openPRs', + ); + + await page.goto( + `/scorecard/aggregations/${aggregationMetadata.id}/metrics/github.openPRs`, + ); + + await scorecardDrillDownPage.expectOnPage('github.openPRs', { + aggregationId: aggregationMetadata.id, + }); + await scorecardDrillDownPage.expectTableHeadersVisible(); + await scorecardDrillDownPage.expectEntityNamesVisible([ + 'red-hat-developer-hub', + ]); + }); }); test('Verify empty aggregated response shows no data', async () => { @@ -953,6 +1020,53 @@ test.describe('Scorecard Plugin Tests', () => { await test.step('Verify metric column sort', async () => { await scorecardDrillDownPage.verifyMetricColumnSort(); }); + + await test.step('Navigate to entity from drill-down table', async () => { + await scorecardDrillDownPage.clickEntityLink('red-hat-developer-hub'); + }); + }); + + test('GitHub drill-down: calculation errors show warning icon and tooltip', async () => { + const aggregationMetadata = + AGGREGATED_CARDS_METADATA.githubDefaultAggregation; + const partialAggregationResponse = { + ...githubAggregatedResponse, + result: { + ...githubAggregatedResponse.result, + calculationErrorCount: 2, + }, + }; + + await mockApiResponse( + page, + ScorecardRoutes.GITHUB_OPEN_PRS_METRIC_AGGREGATION_ROUTE, + partialAggregationResponse, + ); + await mockScorecardEntitiesDrillDown( + page, + githubEntitiesDrillDownWithCalculationErrorsResponse, + 'github.openPRs', + ); + + const entitiesResponse = page.waitForResponse( + res => + res + .url() + .includes( + '/api/scorecard/metrics/github.openPRs/catalog/aggregations/entities', + ) && res.status() === 200, + ); + + await page.goto( + `/scorecard/aggregations/${aggregationMetadata.id}/metrics/github.openPRs`, + ); + await entitiesResponse; + + await scorecardDrillDownPage.expectOnPage('github.openPRs', { + aggregationId: aggregationMetadata.id, + }); + await scorecardDrillDownPage.expectDrillDownCalculationErrorWarningIcon(); + await scorecardDrillDownPage.verifyEntitiesTableCalculationErrorTooltip(); }); test('Jira scorecard: tooltips, entity drill-down, and metric sort', async () => { diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/homepageWidgetUtils.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/homepageWidgetUtils.ts index 5f73d070892..2613f34e825 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/homepageWidgetUtils.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/homepageWidgetUtils.ts @@ -38,13 +38,20 @@ async function addWidget(homePage: HomePage, widgetTitle: string) { await homePage.saveChanges(); } -export async function addAggregatedScorecardWidgets(homePage: HomePage) { +export async function addAggregatedScorecardWidgets( + homePage: HomePage, + widgetIds: Record = AGGREGATED_CARDS_METRIC_IDS, +) { await homePage.navigateToHome(); await homePage.enterEditMode(); await homePage.clearAllCards(); - for (const instanceId of Object.keys(AGGREGATED_CARDS_METRIC_IDS)) { - await homePage.addCard(AGGREGATED_CARDS_WIDGET_TITLES[instanceId]); + for (const instanceId of Object.keys(widgetIds)) { + await homePage.addCard( + AGGREGATED_CARDS_WIDGET_TITLES[ + instanceId as keyof typeof AGGREGATED_CARDS_WIDGET_TITLES + ], + ); } await homePage.saveChanges(); diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/mockHomepageAggregations.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/mockHomepageAggregations.ts index 4436db50ecc..ae8cc28d74b 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/mockHomepageAggregations.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/mockHomepageAggregations.ts @@ -20,9 +20,11 @@ import { ScorecardRoutes } from '../constants/routes'; import { emptyGithubAggregatedResponse, emptyJiraAggregatedResponse, + emptyLicenseFileExistsAggregatedResponse, emptyOpenPrsWeightedAggregatedResponse, githubAggregatedResponse, jiraAggregatedResponse, + licenseFileExistsKpiMetadataResponse, notAllowedAggregationErrorBody, openIssuesKpiMetadataResponse, openPrsKpiMetadataResponse, @@ -41,6 +43,9 @@ function aggregationMetadataForRequestUrl(url: string): object { if (url.includes('openPrsWeightedKpi')) { return openPrsWeightedKpiMetadataResponse; } + if (url.includes('licenseFileExistsKpi')) { + return licenseFileExistsKpiMetadataResponse; + } if (url.includes('jira.openIssues')) { return jiraAggregatedResponse.metadata; } @@ -80,7 +85,7 @@ export async function mockAggregationNoDataFound(page: Page): Promise { await page.route('**/api/scorecard/aggregations/**', async route => { const url = route.request().url(); - if (url.includes('metadata')) { + if (url.includes('/metadata')) { await route.fulfill({ status: 200, contentType: 'application/json', @@ -98,6 +103,15 @@ export async function mockAggregationNoDataFound(page: Page): Promise { return; } + if (url.includes(AGGREGATED_CARDS_METRIC_IDS.licenseFileExistsKpi)) { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify(emptyLicenseFileExistsAggregatedResponse), + }); + return; + } + const githubAggregations = [ AGGREGATED_CARDS_METRIC_IDS.githubOpenPrsKpi, AGGREGATED_CARDS_METRIC_IDS.githubMetricId, diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/scorecardResponseUtils.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/scorecardResponseUtils.ts index 2254692aaab..d2231c6baad 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/scorecardResponseUtils.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/scorecardResponseUtils.ts @@ -940,3 +940,107 @@ export const fileCheckScorecardResponse = [ }, }, ]; + +export const licenseFileExistsKpiMetadataResponse = { + title: 'License File Exists KPI', + description: + 'This KPI provides information about whether the license file exists in the repository.', + type: 'boolean', + history: true, + aggregationType: 'statusGrouped', +}; + +const FILECHECK_BOOLEAN_THRESHOLDS = { + rules: [ + { key: 'exist', expression: '==true' }, + { key: 'missing', expression: '==false' }, + ], +}; + +/** Matches `scorecard.aggregationKPIs.licenseFileExistsKpi` in app-config.yaml */ +export const licenseFileExistsAggregatedResponse = { + id: 'filecheck.license', + status: 'success' as const, + metadata: { + ...licenseFileExistsKpiMetadataResponse, + }, + result: { + values: [ + { count: 7, name: 'exist' }, + { count: 3, name: 'missing' }, + ], + total: 10, + timestamp: '2026-01-24T14:10:32.776Z', + thresholds: FILECHECK_BOOLEAN_THRESHOLDS, + entitiesConsidered: 10, + calculationErrorCount: 0, + }, +}; + +export const emptyLicenseFileExistsAggregatedResponse = { + ...licenseFileExistsAggregatedResponse, + result: { + total: 0, + values: [ + { count: 0, name: 'exist' }, + { count: 0, name: 'missing' }, + ], + timestamp: '2026-01-24T14:10:32.858Z', + thresholds: FILECHECK_BOOLEAN_THRESHOLDS, + entitiesConsidered: 0, + calculationErrorCount: 0, + }, +}; + +export const githubEntitiesDrillDownWithCalculationErrorsResponse = { + ...githubEntitiesDrillDownResponse, + entityHealth: { + totalEntities: 10, + calculationErrorCount: 2, + countsArePartial: true, + }, +}; + +export const weightedKpiEntitiesDrillDownResponse = { + metricId: 'github.openPRs', + metricMetadata: { + title: 'GitHub Open PRs KPI (weighted health)', + description: + 'Weighted health score for open PRs by threshold status across your entities.', + type: 'number', + }, + entities: [ + { + entityRef: 'component:default/red-hat-developer-hub', + entityNamespace: 'default', + entityName: 'red-hat-developer-hub', + entityKind: 'Component', + owner: 'group:default/red-hat', + metricValue: 9, + timestamp: '2026-03-12T08:09:29.732Z', + status: 'success', + }, + { + entityRef: 'component:default/all-scorecards-service', + entityNamespace: 'default', + entityName: 'all-scorecards-service', + entityKind: 'Component', + owner: 'user:development/guest', + metricValue: 46, + timestamp: '2026-03-12T08:09:29.663Z', + status: 'warning', + }, + ], + pagination: { + page: 1, + pageSize: 5, + total: 2, + totalPages: 1, + isCapped: false, + }, + entityHealth: { + totalEntities: 2, + calculationErrorCount: 0, + countsArePartial: false, + }, +}; From b50688ccb5d79c67a1aec86fb69305765cbfc13f Mon Sep 17 00:00:00 2001 From: Ihor Mykhno Date: Wed, 9 Sep 2026 16:43:20 +0200 Subject: [PATCH 2/2] test(scorecard): improve e2e coverage with accessibility checks, test ids, and page object refactor Signed-off-by: Ihor Mykhno imykhno@redhat.com Assisted-By: Cursor --- .../app-legacy/e2e-tests/pages/HomePage.ts | 5 ++ .../e2e-tests/pages/ScorecardDrillDownPage.ts | 48 ++++++------------- .../app-legacy/e2e-tests/scorecard.test.ts | 7 ++- .../e2e-tests/utils/homepageWidgetUtils.ts | 21 +++++--- .../EntitiesTable/EntitiesTableWrapper.tsx | 6 ++- .../__tests__/EntitiesTableWrapper.test.tsx | 8 ++-- 6 files changed, 49 insertions(+), 46 deletions(-) diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/HomePage.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/HomePage.ts index 034be0215a0..f3860029bf8 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/HomePage.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/HomePage.ts @@ -69,6 +69,11 @@ export class HomePage { ) { cardPattern = /Scorecard:\s*GitHub open PRs \(weighted health\)|ScorecardGitHubOpenPrsWeightedKpi/i; + } else if ( + cardName === AGGREGATED_CARDS_WIDGET_TITLES.licenseFileExistsKpi + ) { + cardPattern = + /Scorecard:\s*LICENSE file exists|scorecard-filecheck\.license/i; } else { cardPattern = new RegExp(escapeRegex(cardName), 'i'); } diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/ScorecardDrillDownPage.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/ScorecardDrillDownPage.ts index c86a6889745..7eef91d6c83 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/ScorecardDrillDownPage.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/pages/ScorecardDrillDownPage.ts @@ -23,7 +23,6 @@ import { getEntitiesPageMissingPermission, getEntitiesPageNoDataFound, getEntitiesTableHeaderLabels, - getHomepageEntityCalculationHealthText, getSomeEntitiesNotReportingTooltip, } from '../utils/translationUtils'; @@ -176,7 +175,9 @@ export class ScorecardDrillDownPage { } private getEntitiesTableCalculationWarningIcon(): Locator { - return this.getEntitiesTableHeading().locator('svg'); + return this.getEntitiesTableHeading().getByTestId( + 'entities-table-calculation-warning-icon', + ); } async expectNoDrillDownCalculationErrorWarningIcon() { @@ -194,28 +195,6 @@ export class ScorecardDrillDownPage { await expect(this.page.getByRole('tooltip')).toContainText(tooltipText); } - /** Verifies the "some entities not reporting" tooltip on the drill-down card subheader link. */ - async verifySomeEntitiesNotReportingTooltip( - metricId: MetricId, - options?: DrillDownCardLocatorOptions & { - healthy?: string; - total?: string; - }, - ) { - const card = this.getDrillDownCard(metricId, options); - const healthy = options?.healthy ?? '8'; - const total = options?.total ?? '10'; - const linkText = getHomepageEntityCalculationHealthText( - this.translations, - healthy, - total, - ); - const link = card.getByRole('link', { name: linkText }); - await link.hover(); - const tooltipText = getSomeEntitiesNotReportingTooltip(this.translations); - await expect(this.page.getByRole('tooltip')).toContainText(tooltipText); - } - async expectTableHeadersVisible() { const tableHeaders = getEntitiesTableHeaderLabels(this.translations); const headerNames = [ @@ -252,21 +231,22 @@ export class ScorecardDrillDownPage { } } - async clickEntityLink(entitySlug: string) { - const entitiesTable = this.getEntitiesTable(); + private getEntityLink(entitySlug: string): Locator { const slug = encodeURIComponent(entitySlug); - const link = entitiesTable + return this.getEntitiesTable() .locator('tbody') .locator(`a[href*="/catalog/default/component/${slug}"]`) .first(); - await link.click(); + } + + async clickEntityLink(entitySlug: string) { + await this.getEntityLink(entitySlug).click(); + } + + async expectOnEntityPage(entitySlug: string) { + const slug = encodeURIComponent(entitySlug); await expect(this.page).toHaveURL( - new RegExp( - `/catalog/default/component/${slug.replace( - /[.*+?^${}()|[\]\\]/g, - '\\$&', - )}`, - ), + new RegExp(`/catalog/default/component/${escapeRegex(slug)}`), ); } diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/scorecard.test.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/scorecard.test.ts index 192f74b2bda..9d9b8c01e22 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/scorecard.test.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/scorecard.test.ts @@ -497,13 +497,15 @@ test.describe('Scorecard Plugin Tests', () => { ); }); - test('Verify exist and missing threshold buckets', async () => { + test('Verify exist and missing threshold buckets', async ({}, testInfo) => { const existLabel = translations.thresholds.exist ?? 'Exist'; const missingLabel = translations.thresholds.missing ?? 'Missing'; await expect(card.getByText(existLabel, { exact: true })).toBeVisible(); await expect( card.getByText(missingLabel, { exact: true }), ).toBeVisible(); + + await runAccessibilityTests(page, testInfo); }); test('Verify drill-down link', async () => { @@ -1023,6 +1025,9 @@ test.describe('Scorecard Plugin Tests', () => { await test.step('Navigate to entity from drill-down table', async () => { await scorecardDrillDownPage.clickEntityLink('red-hat-developer-hub'); + await scorecardDrillDownPage.expectOnEntityPage( + 'red-hat-developer-hub', + ); }); }); diff --git a/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/homepageWidgetUtils.ts b/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/homepageWidgetUtils.ts index 2613f34e825..a63a2e7d082 100644 --- a/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/homepageWidgetUtils.ts +++ b/workspaces/scorecard/packages/app-legacy/e2e-tests/utils/homepageWidgetUtils.ts @@ -23,6 +23,14 @@ import { import { mockApiResponse, waitForAggregationResponse } from './apiUtils'; import { mockAggregationNoDataFound } from './mockHomepageAggregations'; +type AggregatedCardWidgetKey = keyof typeof AGGREGATED_CARDS_METRIC_IDS; + +function isAggregatedCardWidgetKey( + key: string, +): key is AggregatedCardWidgetKey { + return key in AGGREGATED_CARDS_WIDGET_TITLES; +} + type SetupHomepageAggregationCardOptions = { aggregationMetadata: { id: string; title: string }; route: string; @@ -40,18 +48,19 @@ async function addWidget(homePage: HomePage, widgetTitle: string) { export async function addAggregatedScorecardWidgets( homePage: HomePage, - widgetIds: Record = AGGREGATED_CARDS_METRIC_IDS, + widgetIds: Partial< + typeof AGGREGATED_CARDS_METRIC_IDS + > = AGGREGATED_CARDS_METRIC_IDS, ) { await homePage.navigateToHome(); await homePage.enterEditMode(); await homePage.clearAllCards(); for (const instanceId of Object.keys(widgetIds)) { - await homePage.addCard( - AGGREGATED_CARDS_WIDGET_TITLES[ - instanceId as keyof typeof AGGREGATED_CARDS_WIDGET_TITLES - ], - ); + if (!isAggregatedCardWidgetKey(instanceId)) { + throw new Error(`Unknown homepage scorecard widget id: ${instanceId}`); + } + await homePage.addCard(AGGREGATED_CARDS_WIDGET_TITLES[instanceId]); } await homePage.saveChanges(); diff --git a/workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/EntitiesTable/EntitiesTableWrapper.tsx b/workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/EntitiesTable/EntitiesTableWrapper.tsx index f1007301be0..f88be52167c 100644 --- a/workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/EntitiesTable/EntitiesTableWrapper.tsx +++ b/workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/EntitiesTable/EntitiesTableWrapper.tsx @@ -66,7 +66,11 @@ export const EntitiesTableWrapper: FC = ({ cursor: 'pointer', }} > - + )} diff --git a/workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/EntitiesTable/__tests__/EntitiesTableWrapper.test.tsx b/workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/EntitiesTable/__tests__/EntitiesTableWrapper.test.tsx index 1592c0ad882..499e30f7b10 100644 --- a/workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/EntitiesTable/__tests__/EntitiesTableWrapper.test.tsx +++ b/workspaces/scorecard/plugins/scorecard/src/components/ScorecardPage/EntitiesTable/__tests__/EntitiesTableWrapper.test.tsx @@ -49,7 +49,7 @@ describe('EntitiesTableWrapper', () => { }); it('should not render calculation warning icon by default', () => { - const { container } = render( + render( Content @@ -58,12 +58,12 @@ describe('EntitiesTableWrapper', () => { ); expect( - container.querySelector('.MuiSvgIcon-colorWarning'), + screen.queryByTestId('entities-table-calculation-warning-icon'), ).not.toBeInTheDocument(); }); it('should render calculation warning icon when showCalculationWarning', () => { - const { container } = render( + render( Content @@ -72,7 +72,7 @@ describe('EntitiesTableWrapper', () => { ); expect( - container.querySelector('.MuiSvgIcon-colorWarning'), + screen.getByTestId('entities-table-calculation-warning-icon'), ).toBeInTheDocument(); expect( screen.getByLabelText(