Skip to content

Commit ac98a37

Browse files
HusneShabbirHusneShabbir
andauthored
chore(e2e): add translations to scorecard tests (#1731)
* add translations * add accessibility checks in each test * mdfd fr project str * test restructure --------- Co-authored-by: HusneShabbir <[email protected]>
1 parent bb36f0f commit ac98a37

File tree

10 files changed

+250
-97
lines changed

10 files changed

+250
-97
lines changed

workspaces/scorecard/packages/app/e2e-tests/pages/CatalogPage.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,24 @@ export class CatalogPage {
2222
this.page = page;
2323
}
2424

25-
async navigateToCatalog() {
25+
async navigateToCatalog(locale: string) {
2626
const enterButton = this.page.getByRole('button', { name: 'Enter' });
2727
await expect(enterButton).toBeVisible();
2828
await enterButton.click();
2929
await expect(this.page.getByText('My Company Catalog')).toBeVisible();
30+
await this.switchToLocale(this.page, locale);
3031
}
3132

3233
async openComponent(componentName: string) {
3334
const link = this.page.getByRole('link', { name: componentName });
3435
await expect(link).toBeVisible();
3536
await link.click();
3637
}
38+
39+
async switchToLocale(page: Page, locale: string): Promise<void> {
40+
await page.getByRole('link', { name: 'Settings' }).click();
41+
await page.getByRole('button', { name: 'English' }).click();
42+
await page.getByRole('option', { name: locale }).click();
43+
await page.locator('a').filter({ hasText: 'Home' }).click();
44+
}
3745
}

workspaces/scorecard/packages/app/e2e-tests/pages/ComponentImportPage.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.

workspaces/scorecard/packages/app/e2e-tests/pages/ScorecardPage.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,26 @@
1616

1717
import { Page, expect } from '@playwright/test';
1818
import { waitUntilApiCallSucceeds } from '../utils/apiUtils';
19+
import { ScorecardMessages } from '../utils/translationUtils';
1920

2021
export class ScorecardPage {
2122
readonly page: Page;
23+
readonly translations: ScorecardMessages;
2224

23-
constructor(page: Page) {
25+
constructor(page: Page, translations: ScorecardMessages) {
2426
this.page = page;
27+
this.translations = translations;
2528
}
2629

2730
get scorecardMetrics() {
2831
return [
2932
{
30-
title: 'GitHub open PRs',
31-
description:
32-
'Current count of open Pull Requests for a given GitHub repository.',
33+
title: this.translations.metric['github.open_prs'].title,
34+
description: this.translations.metric['github.open_prs'].description,
3335
},
3436
{
35-
title: 'Jira open blocking tickets',
36-
description:
37-
'Highlights the number of critical, blocking issues that are currently open in Jira.',
37+
title: this.translations.metric['jira.open_issues'].title,
38+
description: this.translations.metric['jira.open_issues'].description,
3839
},
3940
];
4041
}
@@ -56,12 +57,16 @@ export class ScorecardPage {
5657
}
5758

5859
async expectEmptyState() {
59-
await expect(this.page.getByText('No scorecards added yet')).toBeVisible();
60+
await expect(
61+
this.page.getByText(this.translations.emptyState.title),
62+
).toBeVisible();
6063
await expect(this.page.getByRole('article')).toContainText(
61-
'Scorecards help you monitor component health at a glance. To begin, explore our documentation for setup guidelines.',
64+
this.translations.emptyState.description,
6265
);
6366
await expect(
64-
this.page.getByRole('link', { name: 'View documentation' }),
67+
this.page.getByRole('link', {
68+
name: this.translations.emptyState.button,
69+
}),
6570
).toBeVisible();
6671
}
6772

@@ -85,9 +90,15 @@ export class ScorecardPage {
8590
await expect(scorecardCard.getByText(description)).toBeVisible();
8691

8792
// Check that threshold information is present (Error, Warning, Success)
88-
await expect(scorecardCard.getByText(/Error/)).toBeVisible();
89-
await expect(scorecardCard.getByText(/Warning/)).toBeVisible();
90-
await expect(scorecardCard.getByText(/Success/)).toBeVisible();
93+
await expect(
94+
scorecardCard.getByText(this.translations.thresholds.error),
95+
).toBeVisible();
96+
await expect(
97+
scorecardCard.getByText(this.translations.thresholds.warning),
98+
).toBeVisible();
99+
await expect(
100+
scorecardCard.getByText(this.translations.thresholds.success),
101+
).toBeVisible();
91102
}
92103

93104
async isScorecardVisible(scorecardTitle: string): Promise<boolean> {

workspaces/scorecard/packages/app/e2e-tests/scorecard.test.ts

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import { test, expect } from '@playwright/test';
1818
import { mockScorecardResponse } from './utils/apiUtils';
19-
import { ComponentImportPage } from './pages/ComponentImportPage';
2019
import { CatalogPage } from './pages/CatalogPage';
2120
import { ScorecardPage } from './pages/ScorecardPage';
2221
import { setupRBAC } from './utils/rbacSetup';
@@ -26,89 +25,115 @@ import {
2625
unavailableMetricResponse,
2726
invalidThresholdResponse,
2827
} from './utils/scorecardResponseUtils';
28+
import {
29+
ScorecardMessages,
30+
evaluateMessage,
31+
getTranslations,
32+
} from './utils/translationUtils';
33+
import { runAccessibilityTests } from './utils/accessibility';
2934

3035
test.describe.serial('Pre-RBAC Access Tests', () => {
36+
let translations: ScorecardMessages;
37+
let currentLocale: string;
38+
39+
test.beforeAll(async ({ browser }) => {
40+
const context = await browser.newContext();
41+
const page = await context.newPage();
42+
currentLocale = await page.evaluate(() => globalThis.navigator.language);
43+
translations = getTranslations(currentLocale);
44+
await context.close();
45+
});
46+
3147
test('Display access denied message when RBAC is not configured', async ({
3248
page,
33-
}) => {
49+
}, testInfo) => {
3450
const catalogPage = new CatalogPage(page);
3551
await page.goto('/');
36-
await catalogPage.navigateToCatalog();
52+
await catalogPage.navigateToCatalog(currentLocale);
3753
await catalogPage.openComponent('Red Hat Developer Hub');
3854
await page.getByText('Scorecard').click();
3955

40-
await expect(page.getByText('Missing permission')).toBeVisible();
56+
await expect(
57+
page.getByText(translations.permissionRequired.title),
58+
).toBeVisible();
4159
await expect(page.getByRole('article')).toContainText(
42-
'To view Scorecard plugin, contact your administrator to give the scorecard.metric.read permission.',
60+
evaluateMessage(
61+
translations.permissionRequired.description,
62+
'scorecard.metric.read',
63+
),
4364
);
65+
66+
await runAccessibilityTests(page, testInfo);
4467
});
4568
});
4669

4770
test.describe.serial('Scorecard Plugin Tests', () => {
4871
let catalogPage: CatalogPage;
49-
let importPage: ComponentImportPage;
5072
let scorecardPage: ScorecardPage;
73+
let translations: ScorecardMessages;
74+
let currentLocale: string;
5175

5276
test.beforeAll(async ({ browser }) => {
5377
const context = await browser.newContext();
5478
const page = await context.newPage();
5579

5680
await setupRBAC(page);
5781

82+
currentLocale = await page.evaluate(() => globalThis.navigator.language);
83+
translations = getTranslations(currentLocale);
84+
5885
await context.close();
5986
});
6087

6188
test.beforeEach(async ({ page }) => {
6289
catalogPage = new CatalogPage(page);
63-
importPage = new ComponentImportPage(page);
64-
scorecardPage = new ScorecardPage(page);
90+
scorecardPage = new ScorecardPage(page, translations);
6591
});
6692

67-
test('Import component and validate scorecard tabs for GitHub PRs and Jira tickets', async ({
93+
test('Validate scorecard tabs for GitHub PRs and Jira tickets', async ({
6894
page,
69-
}) => {
95+
}, testInfo) => {
7096
await mockScorecardResponse(page, customScorecardResponse);
7197

7298
await page.goto('/');
73-
await catalogPage.navigateToCatalog();
74-
await importPage.startComponentImport();
75-
await importPage.analyzeComponent(
76-
'https://github.com/rhdh-pai-qe/backstage-catalog/blob/main/catalog-info.yaml',
77-
);
78-
await importPage.viewImportedComponent();
99+
await catalogPage.navigateToCatalog(currentLocale);
100+
await catalogPage.openComponent('Red Hat Developer Hub');
79101
await scorecardPage.openTab();
80-
81102
await scorecardPage.verifyScorecardValues({
82-
'GitHub open PRs': '9',
83-
'Jira open blocking tickets': '8',
103+
[translations.metric['github.open_prs'].title]: '9',
104+
[translations.metric['jira.open_issues'].title]: '8',
84105
});
85106

86107
for (const metric of scorecardPage.scorecardMetrics) {
87108
await scorecardPage.validateScorecardAriaFor(metric);
88109
}
110+
111+
await runAccessibilityTests(page, testInfo);
89112
});
90113

91114
test('Display empty state when scorecard API returns no metrics', async ({
92115
page,
93-
}) => {
116+
}, testInfo) => {
94117
await mockScorecardResponse(page, emptyScorecardResponse);
95118

96119
await page.goto('/');
97-
await catalogPage.navigateToCatalog();
98-
await catalogPage.openComponent('rhdh-app');
120+
await catalogPage.navigateToCatalog(currentLocale);
121+
await catalogPage.openComponent('Red Hat Developer Hub');
99122
await scorecardPage.openTab();
100123

101124
await scorecardPage.expectEmptyState();
125+
126+
await runAccessibilityTests(page, testInfo);
102127
});
103128

104129
test('Displays error state for unavailable data while rendering metrics', async ({
105130
page,
106-
}) => {
131+
}, testInfo) => {
107132
await mockScorecardResponse(page, unavailableMetricResponse);
108133

109134
await page.goto('/');
110-
await catalogPage.navigateToCatalog();
111-
await catalogPage.openComponent('rhdh-app');
135+
await catalogPage.navigateToCatalog(currentLocale);
136+
await catalogPage.openComponent('Red Hat Developer Hub');
112137
await scorecardPage.openTab();
113138

114139
const jiraMetric = scorecardPage.scorecardMetrics[1];
@@ -125,9 +150,10 @@ test.describe.serial('Scorecard Plugin Tests', () => {
125150
expect(isGithubVisible).toBe(true);
126151

127152
const errorLocator = page.getByRole('heading', {
128-
name: 'Metric data unavailable',
153+
name: translations.errors.metricDataUnavailable,
129154
});
130155
await expect(errorLocator).toBeVisible();
156+
await runAccessibilityTests(page, testInfo);
131157

132158
await errorLocator.hover();
133159
const errorMetric = unavailableMetricResponse.find(
@@ -145,12 +171,12 @@ test.describe.serial('Scorecard Plugin Tests', () => {
145171

146172
test('Display error state for invalid threshold config while rendering metrics', async ({
147173
page,
148-
}) => {
174+
}, testInfo) => {
149175
await mockScorecardResponse(page, invalidThresholdResponse);
150176

151177
await page.goto('/');
152-
await catalogPage.navigateToCatalog();
153-
await catalogPage.openComponent('rhdh-app');
178+
await catalogPage.navigateToCatalog(currentLocale);
179+
await catalogPage.openComponent('Red Hat Developer Hub');
154180
await scorecardPage.openTab();
155181

156182
const githubMetric = scorecardPage.scorecardMetrics[0];
@@ -167,9 +193,10 @@ test.describe.serial('Scorecard Plugin Tests', () => {
167193
expect(isJiraVisible).toBe(true);
168194

169195
const errorLocator = page.getByRole('heading', {
170-
name: 'Invalid thresholds',
196+
name: translations.errors.invalidThresholds,
171197
});
172198
await expect(errorLocator).toBeVisible();
199+
await runAccessibilityTests(page, testInfo);
173200

174201
await errorLocator.hover();
175202
const errorTooltip = invalidThresholdResponse.find(
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import AxeBuilder from '@axe-core/playwright';
18+
import { expect, Page, TestInfo } from '@playwright/test';
19+
20+
export async function runAccessibilityTests(
21+
page: Page,
22+
testInfo: TestInfo,
23+
attachName = 'accessibility-scan-results.json',
24+
) {
25+
const accessibilityScanResults = await new AxeBuilder({ page })
26+
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
27+
.analyze();
28+
29+
await testInfo.attach(attachName, {
30+
body: JSON.stringify(accessibilityScanResults, null, 2),
31+
contentType: 'application/json',
32+
});
33+
34+
expect(
35+
accessibilityScanResults.violations,
36+
'Accessibility violations found',
37+
).toEqual([]);
38+
}

workspaces/scorecard/packages/app/e2e-tests/utils/apiUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { Page, expect } from '@playwright/test';
1717

1818
export async function waitUntilApiCallSucceeds(
1919
page: Page,
20-
urlPart: string = '/api/scorecard/metrics/catalog/Component/default/rhdh-app',
20+
urlPart: string = '/api/scorecard/metrics/catalog/Component/default/red-hat-developer-hub',
2121
): Promise<void> {
2222
const response = await page.waitForResponse(
2323
async res => {
@@ -32,7 +32,7 @@ export async function waitUntilApiCallSucceeds(
3232
}
3333

3434
const SCORECARD_API_ROUTE =
35-
'**/api/scorecard/metrics/catalog/Component/default/rhdh-app';
35+
'**/api/scorecard/metrics/catalog/Component/default/red-hat-developer-hub';
3636

3737
export async function mockScorecardResponse(
3838
page: Page,

0 commit comments

Comments
 (0)