From 76e5a2ad0dab8a792d4ab4867b679146519bf7af Mon Sep 17 00:00:00 2001 From: Gustavo Lira Date: Wed, 3 Jul 2024 20:27:45 -0300 Subject: [PATCH] RHIDP-744 [Test Automation] Automate RHDH theme customization - E2E Refactor custom theme E2E test and update uihelper functions Signed-off-by: Gustavo Lira --- .../config_map/configmap-app-config-rhdh.yaml | 10 ++- e2e-tests/playwright/e2e/custom-theme.spec.ts | 50 ----------- .../plugins/custom-theme/custom-theme.spec.ts | 52 ++++++++++++ .../plugins/custom-theme/theme-verifier.ts | 83 +++++++++++++++++++ .../support/pageObjects/global-obj.ts | 4 + e2e-tests/playwright/utils/UIhelper.ts | 36 +++++++- 6 files changed, 181 insertions(+), 54 deletions(-) delete mode 100644 e2e-tests/playwright/e2e/custom-theme.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/custom-theme/custom-theme.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/custom-theme/theme-verifier.ts diff --git a/.ibm/pipelines/resources/config_map/configmap-app-config-rhdh.yaml b/.ibm/pipelines/resources/config_map/configmap-app-config-rhdh.yaml index 72cd88b1f6..a71a435713 100644 --- a/.ibm/pipelines/resources/config_map/configmap-app-config-rhdh.yaml +++ b/.ibm/pipelines/resources/config_map/configmap-app-config-rhdh.yaml @@ -16,6 +16,11 @@ data: headerColor1: "rgb(248, 248, 248)" headerColor2: "rgb(248, 248, 248)" navigationIndicatorColor: "rgb(255,95,21)" + dark: + primaryColor: '#ab75cf' + headerColor1: 'rgb(0, 0, 208)' + headerColor2: 'rgb(255, 246, 140)' + navigationIndicatorColor: 'rgb(244, 238, 169)' backend: auth: keys: @@ -44,6 +49,8 @@ data: # see https://backstage.io/docs/auth/ to learn about auth providers environment: development providers: + guest: + dangerouslyAllowOutsideDevelopment: true # Plugin: GitHub github: development: @@ -53,8 +60,6 @@ data: development: clientId: ${GOOGLE_CLIENT_ID} clientSecret: ${GOOGLE_CLIENT_SECRET} - guest: - dangerouslyAllowOutsideDevelopment: true proxy: skipInvalidProxies: true @@ -99,7 +104,6 @@ data: rules: - allow: [User, Group] - dynatrace: baseUrl: temp argocd: diff --git a/e2e-tests/playwright/e2e/custom-theme.spec.ts b/e2e-tests/playwright/e2e/custom-theme.spec.ts deleted file mode 100644 index 80afd7674d..0000000000 --- a/e2e-tests/playwright/e2e/custom-theme.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { test, Page, expect } from '@playwright/test'; -import { UIhelper } from '../utils/UIhelper'; -import { Common, setupBrowser } from '../utils/Common'; - -let page: Page; - -test.describe('CustomTheme should be applied', () => { - let common: Common; - let uiHelper: UIhelper; - - test.beforeAll(async ({ browser }, testInfo) => { - page = (await setupBrowser(browser, testInfo)).page; - common = new Common(page); - uiHelper = new UIhelper(page); - - await common.loginAsGuest(); - }); - - /* eslint-disable-next-line no-empty-pattern */ - test('Verify that theme colors are applied and make screenshots', async ({} /* NOSONAR */, testInfo) => { - await uiHelper.openSidebar('Settings'); - - const header = await page.locator('header').first(); - await expect(header).toHaveCSS( - 'background-image', - 'none, linear-gradient(90deg, rgb(248, 248, 248), rgb(248, 248, 248))', - ); - - await page.screenshot({ - path: 'screenshots/cusotm-theme-inspection.png', - fullPage: true, - }); - - await testInfo.attach('cusotm-theme-inspection', { - path: 'screenshots/cusotm-theme-inspection.png', - }); - - await page.locator('[name=pin]').click(); - - await page.screenshot({ - path: 'screenshots/cusotm-theme-inspection-collapsed.png', - fullPage: true, - }); - await testInfo.attach('cusotm-theme-inspection-collapsed', { - path: 'screenshots/cusotm-theme-inspection-collapsed.png', - }); - - await common.signOut(); - }); -}); diff --git a/e2e-tests/playwright/e2e/plugins/custom-theme/custom-theme.spec.ts b/e2e-tests/playwright/e2e/plugins/custom-theme/custom-theme.spec.ts new file mode 100644 index 0000000000..e1d4866423 --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/custom-theme/custom-theme.spec.ts @@ -0,0 +1,52 @@ +import { test, Page, TestInfo } from '@playwright/test'; +import { Common, setupBrowser } from '../../../utils/Common'; +import { ThemeVerifier } from './theme-verifier'; + +let page: Page; + +test.describe('CustomTheme should be applied', () => { + let common: Common; + let themeVerifier: ThemeVerifier; + + test.beforeAll(async ({ browser }, testInfo) => { + page = (await setupBrowser(browser, testInfo)).page; + common = new Common(page); + themeVerifier = new ThemeVerifier(page); + + await common.loginAsGuest(); + }); + + // eslint-disable-next-line no-empty-pattern + test('Verify that theme light colors are applied and make screenshots', async ({}, testInfo: TestInfo) => { + await themeVerifier + .setTheme('Light') + .verifyHeaderGradient( + 'none, linear-gradient(90deg, rgb(248, 248, 248), rgb(248, 248, 248))', + ) + .verifyBorderLeftColor('rgb(255, 95, 21)') + .takeScreenshotAndAttach( + 'screenshots/custom-theme-light-inspection.png', + testInfo, + 'custom-theme-light-inspection', + ) + // .verifyPrimaryColors('rgb(255, 95, 21)') //TODO: comment out when the primary color issue is fixed (RHIDP-3107) + .execute(); + }); + + // eslint-disable-next-line no-empty-pattern + test('Verify that theme dark colors are applied and make screenshots', async ({}, testInfo: TestInfo) => { + await themeVerifier + .setTheme('Dark') + .verifyHeaderGradient( + 'none, linear-gradient(90deg, rgb(0, 0, 208), rgb(255, 246, 140))', + ) + .verifyBorderLeftColor('rgb(244, 238, 169)') + .takeScreenshotAndAttach( + 'screenshots/custom-theme-dark-inspection.png', + testInfo, + 'custom-theme-dark-inspection', + ) + // .verifyPrimaryColors('#ab75cf') //TODO: comment out when the primary color issue is fixed (RHIDP-3107) + .execute(); + }); +}); diff --git a/e2e-tests/playwright/e2e/plugins/custom-theme/theme-verifier.ts b/e2e-tests/playwright/e2e/plugins/custom-theme/theme-verifier.ts new file mode 100644 index 0000000000..d629f4f615 --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/custom-theme/theme-verifier.ts @@ -0,0 +1,83 @@ +import { Page, expect, TestInfo } from '@playwright/test'; +import { UIhelper } from '../../../utils/UIhelper'; +import { UIhelperPO } from '../../../support/pageObjects/global-obj'; + +export class ThemeVerifier { + private readonly page: Page; + private uiHelper: UIhelper; + private readonly tasks: Array<() => Promise>; + + constructor(page: Page) { + this.page = page; + this.uiHelper = new UIhelper(page); + this.tasks = []; + } + + setTheme(theme: 'Light' | 'Dark'): this { + this.tasks.push(async () => { + await this.uiHelper.openSidebar('Settings'); + await this.uiHelper.clickBtnByTitleIfNotPressed(`Select theme ${theme}`); + }); + return this; + } + + verifyHeaderGradient(expectedGradient: string): this { + this.tasks.push(async () => { + const header = await this.page.locator('main header'); + await expect(header).toHaveCSS('background-image', expectedGradient); + }); + return this; + } + + verifyBorderLeftColor(expectedColor: string): this { + this.tasks.push(async () => { + const locator = await this.page.locator("a[aria-label='Settings']"); + await expect(locator).toHaveCSS( + 'border-left', + `3px solid ${expectedColor}`, + ); + }); + return this; + } + + verifyPrimaryColors(colorPrimary: string): this { + this.tasks.push(async () => { + await this.uiHelper.checkCssColor( + this.page, + UIhelperPO.MuiTypographyColorPrimary, + colorPrimary, + ); + await this.uiHelper.checkCssColor( + this.page, + UIhelperPO.MuiSwitchColorPrimary, + colorPrimary, + ); + await this.uiHelper.openSidebar('Catalog'); + await this.uiHelper.checkCssColor( + this.page, + UIhelperPO.MuiButtonTextPrimary, + colorPrimary, + ); + await this.uiHelper.openSidebar('Settings'); + }); + return this; + } + + takeScreenshotAndAttach( + screenshotPath: string, + testInfo: TestInfo, + description: string, + ): this { + this.tasks.push(async () => { + await this.page.screenshot({ path: screenshotPath, fullPage: true }); + await testInfo.attach(description, { path: screenshotPath }); + }); + return this; + } + + async execute(): Promise { + for (const task of this.tasks) { + await task(); + } + } +} diff --git a/e2e-tests/playwright/support/pageObjects/global-obj.ts b/e2e-tests/playwright/support/pageObjects/global-obj.ts index 0e26b082e2..7cf4d146be 100644 --- a/e2e-tests/playwright/support/pageObjects/global-obj.ts +++ b/e2e-tests/playwright/support/pageObjects/global-obj.ts @@ -5,10 +5,14 @@ export const waitsObjs = { export const UIhelperPO = { MuiButtonLabel: 'span[class^="MuiButton-label"]', + MuiToggleButtonLabel: 'span[class^="MuiToggleButton-label"]', MuiBoxLabel: 'div[class*="MuiBox-root"] label', MuiTableHead: 'th[class*="MuiTableCell-root"]', MuiTableCell: 'td[class*="MuiTableCell-root"]', MuiTableRow: 'tr[class*="MuiTableRow-root"]', + MuiTypographyColorPrimary: '.MuiTypography-colorPrimary', + MuiSwitchColorPrimary: '.MuiSwitch-colorPrimary', + MuiButtonTextPrimary: '.MuiButton-textPrimary', MuiCard: cardHeading => `//div[contains(@class,'MuiCardHeader-root') and descendant::*[text()='${cardHeading}']]/..`, MuiTable: 'table.MuiTable-root', diff --git a/e2e-tests/playwright/utils/UIhelper.ts b/e2e-tests/playwright/utils/UIhelper.ts index 11699fe7bb..d787fd2ef9 100644 --- a/e2e-tests/playwright/utils/UIhelper.ts +++ b/e2e-tests/playwright/utils/UIhelper.ts @@ -3,7 +3,6 @@ import { UIhelperPO } from '../support/pageObjects/global-obj'; export class UIhelper { private page: Page; - private selectors: { [key: string]: string }; constructor(page: Page) { this.page = page; @@ -46,6 +45,15 @@ export class UIhelper { return button; } + async clickBtnByTitleIfNotPressed(title: string) { + const button = this.page.locator(`button[title="${title}"]`); + const isPressed = await button.getAttribute('aria-pressed'); + + if (isPressed === 'false') { + await button.click(); + } + } + async verifyDivHasText(divText: string) { await expect( this.page.locator(`div`).filter({ hasText: divText }), @@ -247,4 +255,30 @@ export class UIhelper { const rowCount = await this.page.locator(rowSelector).count(); expect(rowCount).toBeGreaterThan(0); } + + // Function to convert hexadecimal to RGB or return RGB if it's already in RGB + toRgb(color: string): string { + if (color.startsWith('rgb')) { + return color; + } + + const bigint = parseInt(color.slice(1), 16); + const r = (bigint >> 16) & 255; + const g = (bigint >> 8) & 255; + const b = bigint & 255; + return `rgb(${r}, ${g}, ${b})`; + } + + async checkCssColor(page: Page, selector: string, expectedColor: string) { + const elements = await page.locator(selector); + const count = await elements.count(); + const expectedRgbColor = this.toRgb(expectedColor); + + for (let i = 0; i < count; i++) { + const color = await elements + .nth(i) + .evaluate(el => window.getComputedStyle(el).color); + expect(color).toBe(expectedRgbColor); + } + } }