From b499dfcf93d1cd96dc8ab750f2fb1d629229b9ed Mon Sep 17 00:00:00 2001 From: Tom Koscielniak Date: Thu, 19 Jun 2025 11:46:39 +0200 Subject: [PATCH] playwright: Add single login for all tests Add a single login for all tests in the form of a global setup. This commit also removes the login from all tests and replaces it with navigation to landing page and revamps the popup closing logic from being applied in logging step into a separate fixture. --- .gitignore | 1 + playwright.config.ts | 7 ++++++- playwright/Customizations/Firewall.spec.ts | 13 ++++++++----- playwright/Customizations/Hostname.spec.ts | 13 ++++++++----- playwright/Customizations/Kernel.spec.ts | 13 ++++++++----- playwright/Customizations/Locale.spec.ts | 13 ++++++++----- playwright/Customizations/Systemd.spec.ts | 13 ++++++++----- playwright/Customizations/Timezone.spec.ts | 13 ++++++++----- playwright/fixtures/customizations.ts | 8 ++++++++ playwright/fixtures/popupHandler.ts | 19 +++++++++++++++++++ playwright/global.setup.ts | 12 ++++++++++++ playwright/helpers/login.ts | 13 +++++++++++++ playwright/helpers/wizardHelpers.ts | 4 +++- playwright/test.spec.ts | 21 ++++++++++++++------- 14 files changed, 124 insertions(+), 39 deletions(-) create mode 100644 playwright/fixtures/customizations.ts create mode 100644 playwright/fixtures/popupHandler.ts create mode 100644 playwright/global.setup.ts diff --git a/.gitignore b/.gitignore index f52550d9..55a8e719 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ rpmbuild /blob-report/ /playwright/.cache/ .env +.auth diff --git a/playwright.config.ts b/playwright.config.ts index fde83958..c6be20e7 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -34,9 +34,14 @@ export default defineConfig({ ignoreHTTPSErrors: true, }, projects: [ + { name: 'setup', testMatch: /.*\.setup\.ts/ }, { name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + use: { + ...devices['Desktop Chrome'], + storageState: '.auth/user.json', + }, + dependencies: ['setup'], }, ], }); diff --git a/playwright/Customizations/Firewall.spec.ts b/playwright/Customizations/Firewall.spec.ts index e2fa5481..ba909e7c 100644 --- a/playwright/Customizations/Firewall.spec.ts +++ b/playwright/Customizations/Firewall.spec.ts @@ -1,10 +1,13 @@ import { expect } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; -import { test } from '../fixtures/cleanup'; +import { test } from '../fixtures/customizations'; import { isHosted } from '../helpers/helpers'; -import { login } from '../helpers/login'; -import { navigateToOptionalSteps, ibFrame } from '../helpers/navHelpers'; +import { + navigateToOptionalSteps, + ibFrame, + navigateToLandingPage, +} from '../helpers/navHelpers'; import { registerLater, fillInDetails, @@ -24,8 +27,8 @@ test('Create a blueprint with Firewall customization', async ({ // Delete the blueprint after the run fixture await cleanup.add(() => deleteBlueprint(page, blueprintName)); - // Login, navigate to IB and get the frame - await login(page); + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); const frame = await ibFrame(page); await test.step('Navigate to optional steps in Wizard', async () => { diff --git a/playwright/Customizations/Hostname.spec.ts b/playwright/Customizations/Hostname.spec.ts index 509635b3..dff4e848 100644 --- a/playwright/Customizations/Hostname.spec.ts +++ b/playwright/Customizations/Hostname.spec.ts @@ -1,10 +1,13 @@ import { expect } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; -import { test } from '../fixtures/cleanup'; +import { test } from '../fixtures/customizations'; import { isHosted } from '../helpers/helpers'; -import { login } from '../helpers/login'; -import { navigateToOptionalSteps, ibFrame } from '../helpers/navHelpers'; +import { + navigateToOptionalSteps, + ibFrame, + navigateToLandingPage, +} from '../helpers/navHelpers'; import { registerLater, fillInDetails, @@ -25,8 +28,8 @@ test('Create a blueprint with Hostname customization', async ({ // Delete the blueprint after the run fixture await cleanup.add(() => deleteBlueprint(page, blueprintName)); - // Login, navigate to IB and get the frame - await login(page); + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); const frame = await ibFrame(page); await test.step('Navigate to optional steps in Wizard', async () => { diff --git a/playwright/Customizations/Kernel.spec.ts b/playwright/Customizations/Kernel.spec.ts index 7a3f28b9..89bbc5dd 100644 --- a/playwright/Customizations/Kernel.spec.ts +++ b/playwright/Customizations/Kernel.spec.ts @@ -1,10 +1,13 @@ import { expect } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; -import { test } from '../fixtures/cleanup'; +import { test } from '../fixtures/customizations'; import { isHosted } from '../helpers/helpers'; -import { login } from '../helpers/login'; -import { navigateToOptionalSteps, ibFrame } from '../helpers/navHelpers'; +import { + navigateToOptionalSteps, + ibFrame, + navigateToLandingPage, +} from '../helpers/navHelpers'; import { registerLater, fillInDetails, @@ -24,8 +27,8 @@ test('Create a blueprint with Kernel customization', async ({ // Delete the blueprint after the run fixture await cleanup.add(() => deleteBlueprint(page, blueprintName)); - // Login, navigate to IB and get the frame - await login(page); + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); const frame = await ibFrame(page); await test.step('Navigate to optional steps in Wizard', async () => { diff --git a/playwright/Customizations/Locale.spec.ts b/playwright/Customizations/Locale.spec.ts index af580176..8f628250 100644 --- a/playwright/Customizations/Locale.spec.ts +++ b/playwright/Customizations/Locale.spec.ts @@ -1,10 +1,13 @@ import { expect } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; -import { test } from '../fixtures/cleanup'; +import { test } from '../fixtures/customizations'; import { isHosted } from '../helpers/helpers'; -import { login } from '../helpers/login'; -import { navigateToOptionalSteps, ibFrame } from '../helpers/navHelpers'; +import { + navigateToOptionalSteps, + ibFrame, + navigateToLandingPage, +} from '../helpers/navHelpers'; import { registerLater, fillInDetails, @@ -24,8 +27,8 @@ test('Create a blueprint with Locale customization', async ({ // Delete the blueprint after the run fixture await cleanup.add(() => deleteBlueprint(page, blueprintName)); - // Login, navigate to IB and get the frame - await login(page); + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); const frame = await ibFrame(page); await test.step('Navigate to optional steps in Wizard', async () => { diff --git a/playwright/Customizations/Systemd.spec.ts b/playwright/Customizations/Systemd.spec.ts index 0586f01c..499703eb 100644 --- a/playwright/Customizations/Systemd.spec.ts +++ b/playwright/Customizations/Systemd.spec.ts @@ -1,10 +1,13 @@ import { expect } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; -import { test } from '../fixtures/cleanup'; +import { test } from '../fixtures/customizations'; import { isHosted } from '../helpers/helpers'; -import { login } from '../helpers/login'; -import { navigateToOptionalSteps, ibFrame } from '../helpers/navHelpers'; +import { + navigateToOptionalSteps, + ibFrame, + navigateToLandingPage, +} from '../helpers/navHelpers'; import { registerLater, fillInDetails, @@ -24,8 +27,8 @@ test('Create a blueprint with Systemd customization', async ({ // Delete the blueprint after the run fixture await cleanup.add(() => deleteBlueprint(page, blueprintName)); - // Login, navigate to IB and get the frame - await login(page); + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); const frame = await ibFrame(page); await test.step('Navigate to optional steps in Wizard', async () => { diff --git a/playwright/Customizations/Timezone.spec.ts b/playwright/Customizations/Timezone.spec.ts index 3c78e2d2..403d6ef9 100644 --- a/playwright/Customizations/Timezone.spec.ts +++ b/playwright/Customizations/Timezone.spec.ts @@ -1,10 +1,13 @@ import { expect } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; -import { test } from '../fixtures/cleanup'; +import { test } from '../fixtures/customizations'; import { isHosted } from '../helpers/helpers'; -import { login } from '../helpers/login'; -import { navigateToOptionalSteps, ibFrame } from '../helpers/navHelpers'; +import { + navigateToOptionalSteps, + ibFrame, + navigateToLandingPage, +} from '../helpers/navHelpers'; import { registerLater, fillInDetails, @@ -24,8 +27,8 @@ test('Create a blueprint with Timezone customization', async ({ // Delete the blueprint after the run fixture await cleanup.add(() => deleteBlueprint(page, blueprintName)); - // Login, navigate to IB and get the frame - await login(page); + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); const frame = await ibFrame(page); await test.step('Navigate to optional steps in Wizard', async () => { diff --git a/playwright/fixtures/customizations.ts b/playwright/fixtures/customizations.ts new file mode 100644 index 00000000..ea926454 --- /dev/null +++ b/playwright/fixtures/customizations.ts @@ -0,0 +1,8 @@ +// This is a common fixture for the customizations tests +import { mergeTests } from '@playwright/test'; + +import { test as cleanupTest } from './cleanup'; +import { test as popupTest } from './popupHandler'; + +// Combine the fixtures into one +export const test = mergeTests(cleanupTest, popupTest); diff --git a/playwright/fixtures/popupHandler.ts b/playwright/fixtures/popupHandler.ts new file mode 100644 index 00000000..dad80348 --- /dev/null +++ b/playwright/fixtures/popupHandler.ts @@ -0,0 +1,19 @@ +import { test as base } from '@playwright/test'; + +import { closePopupsIfExist } from '../helpers/helpers'; + +export interface PopupHandlerFixture { + popupHandler: void; +} + +// This fixture will close any popups that might get opened during the test execution +export const test = base.extend({ + popupHandler: [ + async ({ page }, use) => { + await closePopupsIfExist(page); + // eslint-disable-next-line react-hooks/rules-of-hooks + await use(undefined); + }, + { auto: true }, + ], +}); diff --git a/playwright/global.setup.ts b/playwright/global.setup.ts new file mode 100644 index 00000000..78a8f071 --- /dev/null +++ b/playwright/global.setup.ts @@ -0,0 +1,12 @@ +import { test as setup } from '@playwright/test'; + +import { login, storeStorageStateAndToken } from './helpers/login'; + +setup.describe('Setup', () => { + setup.describe.configure({ retries: 3 }); + + setup('Authenticate', async ({ page }) => { + await login(page); + await storeStorageStateAndToken(page); + }); +}); diff --git a/playwright/helpers/login.ts b/playwright/helpers/login.ts index b283bba3..a52d4ce2 100644 --- a/playwright/helpers/login.ts +++ b/playwright/helpers/login.ts @@ -1,3 +1,5 @@ +import path from 'path'; + import { type Page, expect } from '@playwright/test'; import { closePopupsIfExist, isHosted, togglePreview } from './helpers'; @@ -74,3 +76,14 @@ const loginConsole = async (page: Page, user: string, password: string) => { await togglePreview(page); await expect(page.getByRole('heading', { name: 'All images' })).toBeVisible(); }; + +export const storeStorageStateAndToken = async (page: Page) => { + const { cookies } = await page + .context() + .storageState({ path: path.join(__dirname, '../../.auth/user.json') }); + process.env.TOKEN = `Bearer ${ + cookies.find((cookie) => cookie.name === 'cs_jwt')?.value + }`; + // eslint-disable-next-line playwright/no-wait-for-timeout + await page.waitForTimeout(100); +}; diff --git a/playwright/helpers/wizardHelpers.ts b/playwright/helpers/wizardHelpers.ts index 3d5296c0..9aa537ca 100644 --- a/playwright/helpers/wizardHelpers.ts +++ b/playwright/helpers/wizardHelpers.ts @@ -1,6 +1,6 @@ import { expect, FrameLocator, type Page, test } from '@playwright/test'; -import { isHosted } from './helpers'; +import { closePopupsIfExist, isHosted } from './helpers'; import { ibFrame, navigateToLandingPage } from './navHelpers'; /** @@ -70,6 +70,8 @@ export const fillInImageOutputGuest = async (page: Page | FrameLocator) => { * @param blueprintName - the name of the blueprint to delete */ export const deleteBlueprint = async (page: Page, blueprintName: string) => { + // Since new browser is opened during the BP cleanup, we need to call the popup closer again + await closePopupsIfExist(page); await test.step( 'Delete the blueprint with name: ' + blueprintName, async () => { diff --git a/playwright/test.spec.ts b/playwright/test.spec.ts index a49d09aa..c20fc087 100644 --- a/playwright/test.spec.ts +++ b/playwright/test.spec.ts @@ -1,14 +1,15 @@ import { expect, test } from '@playwright/test'; import { v4 as uuidv4 } from 'uuid'; -import { isHosted } from './helpers/helpers'; -import { login } from './helpers/login'; -import { ibFrame } from './helpers/navHelpers'; +import { closePopupsIfExist, isHosted } from './helpers/helpers'; +import { ibFrame, navigateToLandingPage } from './helpers/navHelpers'; test.describe.serial('test', () => { const blueprintName = uuidv4(); test('create blueprint', async ({ page }) => { - await login(page); + await closePopupsIfExist(page); + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); const frame = await ibFrame(page); frame.getByRole('heading', { name: 'Images About image builder' }); @@ -84,12 +85,14 @@ test.describe.serial('test', () => { }); test('edit blueprint', async ({ page }) => { + await closePopupsIfExist(page); // package searching is really slow the first time in cockpit if (!isHosted()) { test.setTimeout(300000); } - await login(page); + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); const frame = await ibFrame(page); await frame .getByRole('textbox', { name: 'Search input' }) @@ -120,7 +123,9 @@ test.describe.serial('test', () => { }); test('build blueprint', async ({ page }) => { - await login(page); + await closePopupsIfExist(page); + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); const frame = await ibFrame(page); await frame .getByRole('textbox', { name: 'Search input' }) @@ -137,7 +142,9 @@ test.describe.serial('test', () => { }); test('delete blueprint', async ({ page }) => { - await login(page); + await closePopupsIfExist(page); + // Navigate to IB landing page and get the frame + await navigateToLandingPage(page); const frame = await ibFrame(page); await frame .getByRole('textbox', { name: 'Search input' })