diff --git a/workspaces/adoption-insights/packages/app/e2e-tests/insights.test.ts b/workspaces/adoption-insights/packages/app/e2e-tests/insights.test.ts index 6bf3f6505f..e7a04a7175 100644 --- a/workspaces/adoption-insights/packages/app/e2e-tests/insights.test.ts +++ b/workspaces/adoption-insights/packages/app/e2e-tests/insights.test.ts @@ -36,6 +36,7 @@ import { InsightsMessages, getTranslations, replaceTemplate, + escapeRegex, } from './utils/translations.js'; import { visitComponent, @@ -249,7 +250,9 @@ test.describe(() => { const panel = getPanel( page, new RegExp( - replaceTemplate(translations.searches.totalCount, { count: '[1,2]' }), + escapeRegex( + replaceTemplate(translations.searches.totalCount, { count: '' }), + ).replace('', '[1,2]'), ), ); await panel.scrollIntoViewIfNeeded(); @@ -257,12 +260,16 @@ test.describe(() => { const averageTextContent = replaceTemplate( translations.searches.averageText, { - count: '[1,2]', + count: '__COUNT__', period: translations.searches.hour, }, ); const averageText = `${translations.searches.averagePrefix} ${averageTextContent}${translations.searches.averageSuffix}`; - await expect(panel).toContainText(new RegExp(averageText)); + const escapedAverageText = escapeRegex(averageText).replace( + '__COUNT__', + '[1,2]', + ); + await expect(panel).toContainText(new RegExp(escapedAverageText)); }); test('New data shows in top templates', async ({ diff --git a/workspaces/adoption-insights/packages/app/e2e-tests/utils/insightsHelpers.ts b/workspaces/adoption-insights/packages/app/e2e-tests/utils/insightsHelpers.ts index 722870bdac..2ee35e8cc0 100644 --- a/workspaces/adoption-insights/packages/app/e2e-tests/utils/insightsHelpers.ts +++ b/workspaces/adoption-insights/packages/app/e2e-tests/utils/insightsHelpers.ts @@ -15,6 +15,24 @@ */ import { Page, expect } from '@playwright/test'; +/** + * Mapping of locale codes to their native display names + */ +const LOCALE_DISPLAY_NAMES: Record = { + en: 'English', + fr: 'Français', + it: 'Italiano', + ja: '日本語', +}; + +/** + * Get the display name for a locale code + */ +function getLocaleDisplayName(locale: string): string { + const baseLocale = locale.split('-')[0]; + return LOCALE_DISPLAY_NAMES[baseLocale] || locale; +} + /** * Navigate to a page using the navigation link text */ @@ -79,10 +97,14 @@ export async function switchToLocale( page: Page, locale: string, ): Promise { - await page.getByRole('link', { name: 'Settings' }).click(); - await page.getByRole('button', { name: 'English' }).click(); - await page.getByRole('option', { name: locale }).click(); - await page.locator('a').filter({ hasText: 'Home' }).click(); + const baseLocale = locale.split('-')[0]; + if (baseLocale !== 'en') { + const displayName = getLocaleDisplayName(locale); + await page.getByRole('link', { name: 'Settings' }).click(); + await page.getByRole('button', { name: 'English' }).click(); + await page.getByRole('option', { name: displayName }).click(); + await page.locator('a').filter({ hasText: 'Home' }).click(); + } } /** diff --git a/workspaces/adoption-insights/packages/app/e2e-tests/utils/translations.ts b/workspaces/adoption-insights/packages/app/e2e-tests/utils/translations.ts index 3f44262332..5790ac2514 100644 --- a/workspaces/adoption-insights/packages/app/e2e-tests/utils/translations.ts +++ b/workspaces/adoption-insights/packages/app/e2e-tests/utils/translations.ts @@ -18,9 +18,10 @@ /* eslint-disable @backstage/no-relative-monorepo-imports */ import { adoptionInsightsMessages } from '../../../../plugins/adoption-insights/src/translations/ref.js'; import adoptionInsightsTranslationDe from '../../../../plugins/adoption-insights/src/translations/de.js'; -import adoptionInsightsTranslationFr from '../../../../plugins/adoption-insights/src/translations/fr.js'; import adoptionInsightsTranslationEs from '../../../../plugins/adoption-insights/src/translations/es.js'; +import adoptionInsightsTranslationFr from '../../../../plugins/adoption-insights/src/translations/fr.js'; import adoptionInsightsTranslationIt from '../../../../plugins/adoption-insights/src/translations/it.js'; +import adoptionInsightsTranslationJa from '../../../../plugins/adoption-insights/src/translations/ja.js'; /* eslint-enable @backstage/no-relative-monorepo-imports */ export type InsightsMessages = typeof adoptionInsightsMessages; @@ -44,14 +45,16 @@ export function getTranslations(locale: string) { switch (locale) { case 'en': return adoptionInsightsMessages; - case 'fr': - return transform(adoptionInsightsTranslationFr.messages); case 'de': return transform(adoptionInsightsTranslationDe.messages); case 'es': return transform(adoptionInsightsTranslationEs.messages); + case 'fr': + return transform(adoptionInsightsTranslationFr.messages); case 'it': return transform(adoptionInsightsTranslationIt.messages); + case 'ja': + return transform(adoptionInsightsTranslationJa.messages); default: return adoptionInsightsMessages; } @@ -73,3 +76,12 @@ export function replaceTemplate( } return result; } + +/** + * Escape special regex characters in a string + * @param str - String to escape + * @returns String with regex special characters escaped + */ +export function escapeRegex(str: string): string { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} diff --git a/workspaces/adoption-insights/playwright.config.ts b/workspaces/adoption-insights/playwright.config.ts index 72c8983448..18b19de0f1 100644 --- a/workspaces/adoption-insights/playwright.config.ts +++ b/workspaces/adoption-insights/playwright.config.ts @@ -60,5 +60,21 @@ export default defineConfig({ locale: 'fr', }, }, + { + name: 'it', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'it', + }, + }, + { + name: 'ja', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'ja', + }, + }, ], }); diff --git a/workspaces/bulk-import/packages/app/e2e-tests/app.test.ts b/workspaces/bulk-import/packages/app/e2e-tests/app.test.ts index b5c1a7c533..0e0a809a3b 100644 --- a/workspaces/bulk-import/packages/app/e2e-tests/app.test.ts +++ b/workspaces/bulk-import/packages/app/e2e-tests/app.test.ts @@ -77,7 +77,7 @@ test.describe('Bulk Import', () => { // Wait for the sidebar to be visible before navigating await expect(sharedPage.getByText('My Company Catalog')).toBeVisible(); // Wait for catalog to load - use English text since we haven't switched locale yet - await expect(sharedPage.getByText('All Components (1)')).toBeVisible({ + await expect(sharedPage.getByText('All Components (0)')).toBeVisible({ timeout: waitTimeout, }); @@ -257,7 +257,7 @@ test.describe('Bulk Import', () => { .getByRole('button', { name: translations.common.import }) .click(); await expect( - sharedPage.getByText(translations.status.imported), + sharedPage.locator('tbody').getByText(translations.status.imported), ).toBeVisible(); await expect(sharedPage.locator('tbody')).toMatchAriaSnapshot(` - cell "${translations.status.waitingForApproval} ${translations.repositories.pr} , Opens in a new window": diff --git a/workspaces/bulk-import/packages/app/e2e-tests/utils/helpers.ts b/workspaces/bulk-import/packages/app/e2e-tests/utils/helpers.ts index 0530243109..d42b51d560 100644 --- a/workspaces/bulk-import/packages/app/e2e-tests/utils/helpers.ts +++ b/workspaces/bulk-import/packages/app/e2e-tests/utils/helpers.ts @@ -17,19 +17,39 @@ import AxeBuilder from '@axe-core/playwright'; import { expect, Page, TestInfo } from '@playwright/test'; +/** + * Mapping of locale codes to their native display names + */ +const LOCALE_DISPLAY_NAMES: Record = { + en: 'English', + fr: 'Français', + it: 'Italiano', + ja: '日本語', +}; + +/** + * Get the display name for a locale code + */ +export function getLocaleDisplayName(locale: string): string { + const baseLocale = locale.split('-')[0]; + return LOCALE_DISPLAY_NAMES[baseLocale] || locale; +} + /** * Switch to a different locale in the application settings * @param page - Playwright page object - * @param locale - The locale to switch to (e.g., 'en', 'fr', 'de', 'es') + * @param locale - The locale to switch to (e.g., 'en', 'fr', 'it', 'ja') */ export async function switchToLocale( page: Page, locale: string, ): Promise { - if (locale !== 'en') { + const baseLocale = locale.split('-')[0]; + if (baseLocale !== 'en') { + const displayName = getLocaleDisplayName(locale); await page.getByRole('link', { name: 'Settings' }).click(); await page.getByRole('button', { name: 'English' }).click(); - await page.getByRole('option', { name: locale }).click(); + await page.getByRole('option', { name: displayName }).click(); await page.locator('a').filter({ hasText: 'Home' }).click(); // Wait for page to settle after locale switch and reload await page.waitForLoadState('networkidle'); diff --git a/workspaces/bulk-import/packages/app/e2e-tests/utils/translations.ts b/workspaces/bulk-import/packages/app/e2e-tests/utils/translations.ts index b472068128..2db082cbaf 100644 --- a/workspaces/bulk-import/packages/app/e2e-tests/utils/translations.ts +++ b/workspaces/bulk-import/packages/app/e2e-tests/utils/translations.ts @@ -18,8 +18,10 @@ /* eslint-disable @backstage/no-relative-monorepo-imports */ import { bulkImportMessages } from '../../../../plugins/bulk-import/src/translations/ref.js'; import bulkImportTranslationDe from '../../../../plugins/bulk-import/src/translations/de.js'; -import bulkImportTranslationFr from '../../../../plugins/bulk-import/src/translations/fr.js'; import bulkImportTranslationEs from '../../../../plugins/bulk-import/src/translations/es.js'; +import bulkImportTranslationFr from '../../../../plugins/bulk-import/src/translations/fr.js'; +import bulkImportTranslationIt from '../../../../plugins/bulk-import/src/translations/it.js'; +import bulkImportTranslationJa from '../../../../plugins/bulk-import/src/translations/ja.js'; /* eslint-enable @backstage/no-relative-monorepo-imports */ export type BulkImportMessages = typeof bulkImportMessages; @@ -43,12 +45,16 @@ export function getTranslations(locale: string): BulkImportMessages { switch (locale) { case 'en': return bulkImportMessages; - case 'fr': - return transform(bulkImportTranslationFr.messages); case 'de': return transform(bulkImportTranslationDe.messages); case 'es': return transform(bulkImportTranslationEs.messages); + case 'fr': + return transform(bulkImportTranslationFr.messages); + case 'it': + return transform(bulkImportTranslationIt.messages); + case 'ja': + return transform(bulkImportTranslationJa.messages); default: return bulkImportMessages; } diff --git a/workspaces/bulk-import/playwright.config.ts b/workspaces/bulk-import/playwright.config.ts index 28793799e5..9f97ec35e0 100644 --- a/workspaces/bulk-import/playwright.config.ts +++ b/workspaces/bulk-import/playwright.config.ts @@ -64,5 +64,21 @@ export default defineConfig({ locale: 'fr', }, }, + { + name: 'it', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'it', + }, + }, + { + name: 'ja', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'ja', + }, + }, ], }); diff --git a/workspaces/extensions/packages/app/e2e-tests/extensions.test.ts b/workspaces/extensions/packages/app/e2e-tests/extensions.test.ts index 991c7c15fb..e820a170d9 100644 --- a/workspaces/extensions/packages/app/e2e-tests/extensions.test.ts +++ b/workspaces/extensions/packages/app/e2e-tests/extensions.test.ts @@ -20,6 +20,24 @@ import { runAccessibilityTests } from './utils/accessibility'; import { ExtensionHelper } from './utils/helper'; import { ExtensionsMessages, getTranslations } from './utils/translations'; +/** + * Mapping of locale codes to their native display names + */ +const LOCALE_DISPLAY_NAMES: Record = { + en: 'English', + fr: 'Français', + it: 'Italiano', + ja: '日本語', +}; + +/** + * Get the display name for a locale code + */ +function getLocaleDisplayName(locale: string): string { + const baseLocale = locale.split('-')[0]; + return LOCALE_DISPLAY_NAMES[baseLocale] || locale; +} + test.describe('Admin > Extensions', () => { let extensions: Extensions; let extensionHelper: ExtensionHelper; @@ -28,9 +46,13 @@ test.describe('Admin > Extensions', () => { let sharedContext: BrowserContext; async function switchToLocale(page: Page, locale: string): Promise { + const baseLocale = locale.split('-')[0]; + if (baseLocale === 'en') return; + + const displayName = getLocaleDisplayName(locale); await page.getByRole('link', { name: 'Settings' }).click(); await page.getByRole('button', { name: 'English' }).click(); - await page.getByRole('option', { name: locale }).click(); + await page.getByRole('option', { name: displayName }).click(); await page.locator('a').filter({ hasText: 'Home' }).click(); } diff --git a/workspaces/extensions/packages/app/e2e-tests/pages/extensions.ts b/workspaces/extensions/packages/app/e2e-tests/pages/extensions.ts index da3a1e1741..4d41a6251f 100644 --- a/workspaces/extensions/packages/app/e2e-tests/pages/extensions.ts +++ b/workspaces/extensions/packages/app/e2e-tests/pages/extensions.ts @@ -84,6 +84,7 @@ export class Extensions { const navLink = this.page.getByRole('link', { name: `${navText}` }).first(); await navLink.waitFor({ state: 'visible', timeout: 15_000 }); await navLink.dispatchEvent('click'); + await this.page.waitForTimeout(10000); await this.page .getByRole('heading', { name: navText }) .first() diff --git a/workspaces/extensions/packages/app/e2e-tests/utils/translations.ts b/workspaces/extensions/packages/app/e2e-tests/utils/translations.ts index c94c08e556..9df34ceecd 100644 --- a/workspaces/extensions/packages/app/e2e-tests/utils/translations.ts +++ b/workspaces/extensions/packages/app/e2e-tests/utils/translations.ts @@ -18,9 +18,10 @@ /* eslint-disable @backstage/no-relative-monorepo-imports */ import { extensionsMessages } from '../../../../plugins/extensions/src/translations/ref.js'; import extensionsTranslationDe from '../../../../plugins/extensions/src/translations/de.js'; -import extensionsTranslationFr from '../../../../plugins/extensions/src/translations/fr.js'; import extensionsTranslationEs from '../../../../plugins/extensions/src/translations/es.js'; +import extensionsTranslationFr from '../../../../plugins/extensions/src/translations/fr.js'; import extensionsTranslationIt from '../../../../plugins/extensions/src/translations/it.js'; +import extensionsTranslationJa from '../../../../plugins/extensions/src/translations/ja.js'; /* eslint-enable @backstage/no-relative-monorepo-imports */ export type ExtensionsMessages = typeof extensionsMessages; @@ -44,14 +45,16 @@ export function getTranslations(locale: string) { switch (locale) { case 'en': return extensionsMessages; - case 'fr': - return transform(extensionsTranslationFr.messages); case 'de': return transform(extensionsTranslationDe.messages); case 'es': return transform(extensionsTranslationEs.messages); + case 'fr': + return transform(extensionsTranslationFr.messages); case 'it': return transform(extensionsTranslationIt.messages); + case 'ja': + return transform(extensionsTranslationJa.messages); default: return extensionsMessages; } diff --git a/workspaces/extensions/playwright.config.ts b/workspaces/extensions/playwright.config.ts index 9d35599e70..9178d96b4e 100644 --- a/workspaces/extensions/playwright.config.ts +++ b/workspaces/extensions/playwright.config.ts @@ -70,5 +70,21 @@ export default defineConfig({ locale: 'fr', }, }, + { + name: 'it', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'it', + }, + }, + { + name: 'ja', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'ja', + }, + }, ], }); diff --git a/workspaces/global-floating-action-button/packages/app/e2e-tests/app.test.ts b/workspaces/global-floating-action-button/packages/app/e2e-tests/app.test.ts index 27356dbfab..b5e645bb9b 100644 --- a/workspaces/global-floating-action-button/packages/app/e2e-tests/app.test.ts +++ b/workspaces/global-floating-action-button/packages/app/e2e-tests/app.test.ts @@ -26,6 +26,8 @@ import { import { GlobalFloatingActionButtonMessages, getTranslations, + getEnglishTranslations, + TEST_IDS, } from './utils/translations.js'; import { runAccessibilityTests } from './utils/accessibility.js'; @@ -61,14 +63,32 @@ test.describe('Global Floating Action Button Tests', () => { await sharedPage.waitForTimeout(1000); }); + /** + * Get the menu button - handles both English and translated button names + * The button accessible name may be "Menu" (English) even when tooltips are translated + */ + async function getMenuButton(index: number = 0) { + const englishTranslations = getEnglishTranslations(); + // Try translated button name first, then English fallback + let menuButton = sharedPage.getByRole('button', { + name: translations.fab.menu.tooltip, + }); + if ((await menuButton.count()) === 0) { + menuButton = sharedPage.getByRole('button', { + name: englishTranslations.fab.menu.tooltip, + }); + } + return index === 0 ? menuButton.first() : menuButton.nth(index); + } + test('global floating action buttons should be visible', async ({ browser: _browser, }, testInfo) => { - const menuButton = sharedPage.getByRole('button', { - name: translations.fab.menu.tooltip, - }); - const count = await menuButton.count(); - expect(count).toBe(2); + // Use getMenuButton to get the first one, then check total count + const firstMenuButton = await getMenuButton(0); + await expect(firstMenuButton).toBeVisible(); + const secondMenuButton = await getMenuButton(1); + await expect(secondMenuButton).toBeVisible(); await runAccessibilityTests(sharedPage, testInfo); }); @@ -76,14 +96,13 @@ test.describe('Global Floating Action Button Tests', () => { test('should display menu items with correct accessibility structure', async ({ browser: _browser, }, testInfo) => { - await sharedPage - .getByRole('button', { name: translations.fab.menu.tooltip }) - .first() - .click(); + await (await getMenuButton(0)).click(); await runAccessibilityTests(sharedPage, testInfo); - await expect(sharedPage.getByTestId('settings')).toMatchAriaSnapshot(` + // Note: test IDs use translated labels in this component + await expect(sharedPage.getByTestId(TEST_IDS.settings)) + .toMatchAriaSnapshot(` - button "Settings": - paragraph: Settings `); @@ -95,7 +114,8 @@ test.describe('Global Floating Action Button Tests', () => { - paragraph: ${translations.fab.github.label} `); - await expect(sharedPage.getByTestId('search')).toMatchAriaSnapshot(` + await expect(sharedPage.getByTestId(TEST_IDS.search)) + .toMatchAriaSnapshot(` - button "Search": - paragraph `); @@ -109,20 +129,14 @@ test.describe('Global Floating Action Button Tests', () => { }); test('should display correct tooltip texts for floating action button elements', async () => { - await sharedPage - .getByRole('button', { name: translations.fab.menu.tooltip }) - .first() - .click(); + await (await getMenuButton(0)).click(); - await sharedPage - .getByRole('button', { name: translations.fab.menu.tooltip }) - .first() - .hover(); + await (await getMenuButton(0)).hover(); await expect(sharedPage.getByRole('tooltip')).toContainText( translations.fab.menu.tooltip, ); - await sharedPage.getByTestId('settings').hover(); + await sharedPage.getByTestId(TEST_IDS.settings).hover(); await expect( sharedPage.getByRole('tooltip', { name: 'Settings' }), ).toContainText('Settings'); @@ -136,7 +150,7 @@ test.describe('Global Floating Action Button Tests', () => { }), ).toContainText(translations.fab.github.tooltip); - await sharedPage.getByTestId('search').hover(); + await sharedPage.getByTestId(TEST_IDS.search).hover(); await expect( sharedPage.getByRole('tooltip', { name: 'Search' }), ).toContainText('Search'); @@ -152,8 +166,15 @@ test.describe('Global Floating Action Button Tests', () => { }); test('test menu items', async () => { - await testSettingsMenuItem(sharedPage, translations.fab.menu.tooltip); - await testSearchMenuItem(sharedPage, translations.fab.menu.tooltip); + const englishTranslations = getEnglishTranslations(); + const menuTooltip = + (await sharedPage + .getByRole('button', { name: translations.fab.menu.tooltip }) + .count()) > 0 + ? translations.fab.menu.tooltip + : englishTranslations.fab.menu.tooltip; + await testSettingsMenuItem(sharedPage, menuTooltip); + await testSearchMenuItem(sharedPage, menuTooltip); await testCreateMenuItem(sharedPage); }); }); @@ -162,14 +183,14 @@ test.describe('Global Floating Action Button Tests', () => { test('should display menu items with correct accessibility structure', async ({ browser: _browser, }, testInfo) => { - await sharedPage - .getByRole('button', { name: translations.fab.menu.tooltip }) - .nth(1) - .click(); + await (await getMenuButton(1)).click(); await runAccessibilityTests(sharedPage, testInfo); - await expect(sharedPage.getByRole('main')).toMatchAriaSnapshot(` + // Note: test IDs use translated labels in this component + await expect( + sharedPage.getByTestId(translations.fab.apis.label.toLowerCase()), + ).toMatchAriaSnapshot(` - button "${translations.fab.apis.label}": - paragraph `); @@ -183,14 +204,8 @@ test.describe('Global Floating Action Button Tests', () => { }); test('should display correct tooltip texts for floating action button elements', async () => { - await sharedPage - .getByRole('button', { name: translations.fab.menu.tooltip }) - .nth(1) - .click(); - await sharedPage - .getByRole('button', { name: translations.fab.menu.tooltip }) - .nth(1) - .hover(); + await (await getMenuButton(1)).click(); + await (await getMenuButton(1)).hover(); await expect(sharedPage.getByRole('tooltip')).toContainText( translations.fab.menu.tooltip, ); @@ -214,8 +229,15 @@ test.describe('Global Floating Action Button Tests', () => { }); test('test menu items', async () => { - await testDocsMenuItem(sharedPage, translations.fab.menu.tooltip); - await testApisMenuItem(sharedPage, translations.fab.menu.tooltip); + const englishTranslations = getEnglishTranslations(); + const menuTooltip = + (await sharedPage + .getByRole('button', { name: translations.fab.menu.tooltip }) + .count()) > 0 + ? translations.fab.menu.tooltip + : englishTranslations.fab.menu.tooltip; + await testDocsMenuItem(sharedPage, menuTooltip); + await testApisMenuItem(sharedPage, menuTooltip); }); }); }); diff --git a/workspaces/global-floating-action-button/packages/app/e2e-tests/utils/helpers.ts b/workspaces/global-floating-action-button/packages/app/e2e-tests/utils/helpers.ts index 6b4710d2d6..90ca4a7586 100644 --- a/workspaces/global-floating-action-button/packages/app/e2e-tests/utils/helpers.ts +++ b/workspaces/global-floating-action-button/packages/app/e2e-tests/utils/helpers.ts @@ -20,6 +20,24 @@ import { GlobalFloatingActionButtonMessages, } from './translations.js'; +/** + * Mapping of locale codes to their native display names + */ +const LOCALE_DISPLAY_NAMES: Record = { + en: 'English', + fr: 'Français', + it: 'Italiano', + ja: '日本語', +}; + +/** + * Get the display name for a locale code + */ +function getLocaleDisplayName(locale: string): string { + const baseLocale = locale.split('-')[0]; + return LOCALE_DISPLAY_NAMES[baseLocale] || locale; +} + async function getPageTranslations( page: Page, ): Promise { @@ -46,10 +64,12 @@ export async function switchToLocale( page: Page, locale: string, ): Promise { - if (locale !== 'en') { + const baseLocale = locale.split('-')[0]; + if (baseLocale !== 'en') { + const displayName = getLocaleDisplayName(locale); await page.getByRole('link', { name: 'Settings' }).click(); await page.getByRole('button', { name: 'English' }).click(); - await page.getByRole('option', { name: locale }).click(); + await page.getByRole('option', { name: displayName }).click(); await page.locator('a').filter({ hasText: 'Home' }).click(); } } @@ -124,7 +144,7 @@ export async function testApisMenuItem( const translations = await getPageTranslations(page); await openLeftMenu(page, menuTooltip); await page.getByTestId(translations.fab.apis.label.toLowerCase()).click(); - await expect(page).toHaveURL('/api-docs'); + await expect(page).toHaveURL(/\/api-docs/); await expect(page.locator('h1')).toContainText('APIs'); await expect(page.locator('header')).toContainText('My Company API Explorer'); await expect( diff --git a/workspaces/global-floating-action-button/packages/app/e2e-tests/utils/translations.ts b/workspaces/global-floating-action-button/packages/app/e2e-tests/utils/translations.ts index 3219cb9e3b..dc37edc55e 100644 --- a/workspaces/global-floating-action-button/packages/app/e2e-tests/utils/translations.ts +++ b/workspaces/global-floating-action-button/packages/app/e2e-tests/utils/translations.ts @@ -18,8 +18,10 @@ /* eslint-disable @backstage/no-relative-monorepo-imports */ import { globalFloatingActionButtonMessages } from '../../../../plugins/global-floating-action-button/src/translations/ref.js'; import globalFloatingActionButtonTranslationDe from '../../../../plugins/global-floating-action-button/src/translations/de.js'; -import globalFloatingActionButtonTranslationFr from '../../../../plugins/global-floating-action-button/src/translations/fr.js'; import globalFloatingActionButtonTranslationEs from '../../../../plugins/global-floating-action-button/src/translations/es.js'; +import globalFloatingActionButtonTranslationFr from '../../../../plugins/global-floating-action-button/src/translations/fr.js'; +import globalFloatingActionButtonTranslationIt from '../../../../plugins/global-floating-action-button/src/translations/it.js'; +import globalFloatingActionButtonTranslationJa from '../../../../plugins/global-floating-action-button/src/translations/ja.js'; /* eslint-enable @backstage/no-relative-monorepo-imports */ export type GlobalFloatingActionButtonMessages = @@ -48,13 +50,34 @@ export function getTranslations(locale: string) { switch (languageCode) { case 'en': return globalFloatingActionButtonMessages; - case 'fr': - return transform(globalFloatingActionButtonTranslationFr.messages); case 'de': return transform(globalFloatingActionButtonTranslationDe.messages); case 'es': return transform(globalFloatingActionButtonTranslationEs.messages); + case 'fr': + return transform(globalFloatingActionButtonTranslationFr.messages); + case 'it': + return transform(globalFloatingActionButtonTranslationIt.messages); + case 'ja': + return transform(globalFloatingActionButtonTranslationJa.messages); default: return globalFloatingActionButtonMessages; } } + +/** + * Get the English (fallback) translations + */ +export function getEnglishTranslations() { + return globalFloatingActionButtonMessages; +} + +/** + * Test IDs that are always in English, regardless of locale + * Note: Most FAB items use translated labels as test IDs, so use + * translations.fab.X.label.toLowerCase() for those instead + */ +export const TEST_IDS = { + settings: 'settings', + search: 'search', +}; diff --git a/workspaces/global-floating-action-button/playwright.config.ts b/workspaces/global-floating-action-button/playwright.config.ts index 601b121d2a..0849dcce05 100644 --- a/workspaces/global-floating-action-button/playwright.config.ts +++ b/workspaces/global-floating-action-button/playwright.config.ts @@ -60,5 +60,21 @@ export default defineConfig({ locale: 'fr', }, }, + { + name: 'it', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'it', + }, + }, + { + name: 'ja', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'ja', + }, + }, ], }); diff --git a/workspaces/global-header/playwright.config.ts b/workspaces/global-header/playwright.config.ts index 601b121d2a..0849dcce05 100644 --- a/workspaces/global-header/playwright.config.ts +++ b/workspaces/global-header/playwright.config.ts @@ -60,5 +60,21 @@ export default defineConfig({ locale: 'fr', }, }, + { + name: 'it', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'it', + }, + }, + { + name: 'ja', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'ja', + }, + }, ], }); diff --git a/workspaces/homepage/playwright.config.ts b/workspaces/homepage/playwright.config.ts index 866d41647e..10af80500f 100644 --- a/workspaces/homepage/playwright.config.ts +++ b/workspaces/homepage/playwright.config.ts @@ -62,5 +62,23 @@ export default defineConfig({ locale: 'fr', }, }, + { + name: 'it', + testDir: 'packages/app/e2e-tests', + grep: /Cards/, + use: { + channel: 'chrome', + locale: 'it', + }, + }, + { + name: 'ja', + testDir: 'packages/app/e2e-tests', + grep: /Cards/, + use: { + channel: 'chrome', + locale: 'ja', + }, + }, ], }); diff --git a/workspaces/lightspeed/packages/app/e2e-tests/utils/testHelper.ts b/workspaces/lightspeed/packages/app/e2e-tests/utils/testHelper.ts index 2e0a6b1dc1..6b4f0179b5 100644 --- a/workspaces/lightspeed/packages/app/e2e-tests/utils/testHelper.ts +++ b/workspaces/lightspeed/packages/app/e2e-tests/utils/testHelper.ts @@ -18,10 +18,32 @@ import { Page, expect } from '@playwright/test'; import { mockFeedbackReceived } from './devMode'; import { LightspeedMessages } from './translations'; +/** + * Mapping of locale codes to their native display names + */ +const LOCALE_DISPLAY_NAMES: Record = { + en: 'English', + fr: 'Français', + it: 'Italiano', + ja: '日本語', +}; + +/** + * Get the display name for a locale code + */ +export function getLocaleDisplayName(locale: string): string { + const baseLocale = locale.split('-')[0]; + return LOCALE_DISPLAY_NAMES[baseLocale] || locale; +} + export const switchToLocale = async (page: Page, locale: string) => { + const baseLocale = locale.split('-')[0]; + if (baseLocale === 'en') return; + + const displayName = getLocaleDisplayName(locale); await page.getByRole('link', { name: 'Settings' }).click(); await page.getByRole('button', { name: 'English' }).click(); - await page.getByRole('option', { name: locale }).click(); + await page.getByRole('option', { name: displayName }).click(); await page.locator('a').filter({ hasText: 'Home' }).click(); }; diff --git a/workspaces/lightspeed/playwright.config.ts b/workspaces/lightspeed/playwright.config.ts index 052cd0e855..7cdc64df6c 100644 --- a/workspaces/lightspeed/playwright.config.ts +++ b/workspaces/lightspeed/playwright.config.ts @@ -61,5 +61,23 @@ export default defineConfig({ locale: 'fr', }, }, + // TODO: Enable after translation bugs are fixed + // See bug report: [add your bug link here] + // { + // name: 'it', + // testDir: 'packages/app/e2e-tests', + // use: { + // channel: 'chrome', + // locale: 'it', + // }, + // }, + // { + // name: 'ja', + // testDir: 'packages/app/e2e-tests', + // use: { + // channel: 'chrome', + // locale: 'ja', + // }, + // }, ], }); diff --git a/workspaces/quickstart/playwright.config.ts b/workspaces/quickstart/playwright.config.ts index df41951ef8..7a85de82b2 100644 --- a/workspaces/quickstart/playwright.config.ts +++ b/workspaces/quickstart/playwright.config.ts @@ -60,6 +60,26 @@ export default defineConfig({ baseURL: 'http://localhost:3000', }, }, + { + name: 'it', + testDir: 'packages/app/e2e-tests', + testIgnore: '**/quick-start-developer.spec.ts', + use: { + channel: 'chrome', + locale: 'it', + baseURL: 'http://localhost:3000', + }, + }, + { + name: 'ja', + testDir: 'packages/app/e2e-tests', + testIgnore: '**/quick-start-developer.spec.ts', + use: { + channel: 'chrome', + locale: 'ja', + baseURL: 'http://localhost:3000', + }, + }, { name: 'dev-config', testDir: 'packages/app/e2e-tests', @@ -80,5 +100,25 @@ export default defineConfig({ baseURL: 'http://localhost:3001', }, }, + { + name: 'dev-config-it', + testDir: 'packages/app/e2e-tests', + testMatch: '**/quick-start-developer.spec.ts', + use: { + channel: 'chrome', + locale: 'it', + baseURL: 'http://localhost:3001', + }, + }, + { + name: 'dev-config-ja', + testDir: 'packages/app/e2e-tests', + testMatch: '**/quick-start-developer.spec.ts', + use: { + channel: 'chrome', + locale: 'ja', + baseURL: 'http://localhost:3001', + }, + }, ], }); diff --git a/workspaces/scorecard/playwright.config.ts b/workspaces/scorecard/playwright.config.ts index f267c4541c..b3823c687e 100644 --- a/workspaces/scorecard/playwright.config.ts +++ b/workspaces/scorecard/playwright.config.ts @@ -64,5 +64,21 @@ export default defineConfig({ locale: 'fr', }, }, + { + name: 'it', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'it', + }, + }, + { + name: 'ja', + testDir: 'packages/app/e2e-tests', + use: { + channel: 'chrome', + locale: 'ja', + }, + }, ], });