playwright: Add check to ensure test is authenticated

In order to fix the flakiness in cockpit plugin tests introduced by the single login, the tests need a check if they are authenticated at the start of their execution and relog in case the session expired.
This commit is contained in:
Tom Koscielniak 2025-06-27 11:25:18 +02:00 committed by Klara Simickova
parent 0597541af2
commit 0319c81b41
9 changed files with 98 additions and 21 deletions

View file

@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid';
import { FILE_SYSTEM_CUSTOMIZATION_URL } from '../../src/constants';
import { test } from '../fixtures/cleanup';
import { isHosted } from '../helpers/helpers';
import { ensureAuthenticated } from '../helpers/login';
import {
navigateToOptionalSteps,
ibFrame,
@ -28,6 +29,8 @@ test('Create a blueprint with Filesystem customization', async ({
// Delete the blueprint after the run fixture
await cleanup.add(() => deleteBlueprint(page, blueprintName));
await ensureAuthenticated(page);
// Login, navigate to IB and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);

View file

@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { test } from '../fixtures/customizations';
import { isHosted } from '../helpers/helpers';
import { ensureAuthenticated } from '../helpers/login';
import {
navigateToOptionalSteps,
ibFrame,
@ -27,6 +28,8 @@ test('Create a blueprint with Firewall customization', async ({
// Delete the blueprint after the run fixture
await cleanup.add(() => deleteBlueprint(page, blueprintName));
await ensureAuthenticated(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);

View file

@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { test } from '../fixtures/customizations';
import { isHosted } from '../helpers/helpers';
import { ensureAuthenticated } from '../helpers/login';
import {
navigateToOptionalSteps,
ibFrame,
@ -28,6 +29,8 @@ test('Create a blueprint with Hostname customization', async ({
// Delete the blueprint after the run fixture
await cleanup.add(() => deleteBlueprint(page, blueprintName));
await ensureAuthenticated(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);

View file

@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { test } from '../fixtures/customizations';
import { isHosted } from '../helpers/helpers';
import { ensureAuthenticated } from '../helpers/login';
import {
navigateToOptionalSteps,
ibFrame,
@ -27,6 +28,8 @@ test('Create a blueprint with Kernel customization', async ({
// Delete the blueprint after the run fixture
await cleanup.add(() => deleteBlueprint(page, blueprintName));
await ensureAuthenticated(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);

View file

@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { test } from '../fixtures/customizations';
import { isHosted } from '../helpers/helpers';
import { ensureAuthenticated } from '../helpers/login';
import {
navigateToOptionalSteps,
ibFrame,
@ -27,6 +28,8 @@ test('Create a blueprint with Locale customization', async ({
// Delete the blueprint after the run fixture
await cleanup.add(() => deleteBlueprint(page, blueprintName));
await ensureAuthenticated(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);

View file

@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { test } from '../fixtures/customizations';
import { isHosted } from '../helpers/helpers';
import { ensureAuthenticated } from '../helpers/login';
import {
navigateToOptionalSteps,
ibFrame,
@ -27,6 +28,8 @@ test('Create a blueprint with Systemd customization', async ({
// Delete the blueprint after the run fixture
await cleanup.add(() => deleteBlueprint(page, blueprintName));
await ensureAuthenticated(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);

View file

@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { test } from '../fixtures/customizations';
import { isHosted } from '../helpers/helpers';
import { ensureAuthenticated } from '../helpers/login';
import {
navigateToOptionalSteps,
ibFrame,
@ -27,6 +28,8 @@ test('Create a blueprint with Timezone customization', async ({
// Delete the blueprint after the run fixture
await cleanup.add(() => deleteBlueprint(page, blueprintName));
await ensureAuthenticated(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
const frame = await ibFrame(page);

View file

@ -23,6 +23,38 @@ export const login = async (page: Page) => {
return loginCockpit(page, user, password);
};
/**
* Checks if the user is already authenticated, if not, logs them in
* @param page - the page object
*/
export const ensureAuthenticated = async (page: Page) => {
// Navigate to the target page
if (isHosted()) {
await page.goto('/insights/image-builder/landing');
} else {
await page.goto('/cockpit-image-builder');
}
// Check for authentication success indicator
const successIndicator = isHosted()
? page.getByRole('heading', { name: 'All images' })
: ibFrame(page).getByRole('heading', { name: 'All images' });
let isAuthenticated = false;
try {
// Give it a 30 second period to load, it's less expensive than having to rerun the test
await expect(successIndicator).toBeVisible({ timeout: 30000 });
isAuthenticated = true;
} catch {
isAuthenticated = false;
}
if (!isAuthenticated) {
// Not authenticated, need to login
await login(page);
}
};
const loginCockpit = async (page: Page, user: string, password: string) => {
await page.goto('/cockpit-image-builder');
@ -33,26 +65,36 @@ const loginCockpit = async (page: Page, user: string, password: string) => {
// image-builder lives inside an iframe
const frame = ibFrame(page);
// cockpit-image-builder needs superuser, expect an error message
// when the user does not have admin priviliges
await expect(
frame.getByRole('heading', { name: 'Access is limited' })
).toBeVisible();
await page.getByRole('button', { name: 'Limited access' }).click();
try {
// Check if the user already has administrative access
await expect(
page.getByRole('button', { name: 'Administrative access' })
).toBeVisible();
} catch {
// If not, try to gain it
// cockpit-image-builder needs superuser, expect an error message
// when the user does not have admin priviliges
await expect(
frame.getByRole('heading', { name: 'Access is limited' })
).toBeVisible();
await page.getByRole('button', { name: 'Limited access' }).click();
// different popup opens based on type of account (can be passwordless)
const authenticateButton = page.getByRole('button', { name: 'Authenticate' });
const closeButton = page.getByText('Close');
await expect(authenticateButton.or(closeButton)).toBeVisible();
// different popup opens based on type of account (can be passwordless)
const authenticateButton = page.getByRole('button', {
name: 'Authenticate',
});
const closeButton = page.getByText('Close');
await expect(authenticateButton.or(closeButton)).toBeVisible();
if (await authenticateButton.isVisible()) {
// with password
await page.getByRole('textbox', { name: 'Password' }).fill(password);
await authenticateButton.click();
}
if (await closeButton.isVisible()) {
// passwordless
await closeButton.click();
if (await authenticateButton.isVisible()) {
// with password
await page.getByRole('textbox', { name: 'Password' }).fill(password);
await authenticateButton.click();
}
if (await closeButton.isVisible()) {
// passwordless
await closeButton.click();
}
}
// expect to have administrative access
@ -81,9 +123,18 @@ 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
}`;
if (isHosted()) {
// For hosted service, look for cs_jwt token
process.env.TOKEN = `Bearer ${
cookies.find((cookie) => cookie.name === 'cs_jwt')?.value
}`;
} else {
// For Cockpit, we don't need a TOKEN but we can still store it for consistency
const cockpitCookie = cookies.find((cookie) => cookie.name === 'cockpit');
if (cockpitCookie) {
process.env.TOKEN = cockpitCookie.value;
}
}
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(100);
};

View file

@ -2,11 +2,13 @@ import { expect, test } from '@playwright/test';
import { v4 as uuidv4 } from 'uuid';
import { closePopupsIfExist, isHosted } from './helpers/helpers';
import { ensureAuthenticated } from './helpers/login';
import { ibFrame, navigateToLandingPage } from './helpers/navHelpers';
test.describe.serial('test', () => {
const blueprintName = uuidv4();
test('create blueprint', async ({ page }) => {
await ensureAuthenticated(page);
await closePopupsIfExist(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
@ -85,6 +87,7 @@ test.describe.serial('test', () => {
});
test('edit blueprint', async ({ page }) => {
await ensureAuthenticated(page);
await closePopupsIfExist(page);
// package searching is really slow the first time in cockpit
if (!isHosted()) {
@ -123,6 +126,7 @@ test.describe.serial('test', () => {
});
test('build blueprint', async ({ page }) => {
await ensureAuthenticated(page);
await closePopupsIfExist(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);
@ -142,6 +146,7 @@ test.describe.serial('test', () => {
});
test('delete blueprint', async ({ page }) => {
await ensureAuthenticated(page);
await closePopupsIfExist(page);
// Navigate to IB landing page and get the frame
await navigateToLandingPage(page);