Skip to content
Merged
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 185 additions & 18 deletions tests/playwright/src/rhel-extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,27 @@
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import type { NavigationBar } from '@podman-desktop/tests-playwright';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';

import type { Browser, Locator, Page } from '@playwright/test';
import type { ConfirmInputValue } from '@podman-desktop/tests-playwright';
import {
AuthenticationPage,
expect as playExpect,
findPageWithTitleInBrowser,
getEntryFromLogs,
NavigationBar,
performBrowserLogin,
ResourceConnectionCardPage,
ResourceElementActions,
ResourceElementState,
ResourcesPage,
RunnerOptions,
startChromium,
test,
waitForPodmanMachineStartup,
waitUntil,
} from '@podman-desktop/tests-playwright';

const extensionName = 'rhel-vms';
Expand All @@ -31,6 +46,17 @@ let extensionInstalled = false;
const skipInstallation = process.env.SKIP_INSTALLATION;
const extensionURL = process.env.OCI_IMAGE ?? 'ghcr.io/redhat-developer/podman-desktop-rhel-ext:next';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const browserOutputPath = [__dirname, '..', 'output', 'browser'];

const expectedAuthPageTitle = 'Log In';
const regex = new RegExp(/((http|https):\/\/.*$)/);
let signInButton: Locator;
let browser: Browser;
let chromiumPage: Page;
const chromePort = '9222';

test.use({
runnerOptions: new RunnerOptions({
customFolder: 'rhel-e2e-tests',
Expand All @@ -51,29 +77,145 @@ test.afterAll(async ({ runner }) => {
});

test.describe.serial('RHEL Extension E2E Tests', () => {
test.describe.configure({ retries: 1 });
test('Go to settings and check if extension is already installed', async ({ navigationBar }) => {
const extensionsPage = await navigationBar.openExtensions();
if (await extensionsPage.extensionIsInstalled(extensionLabel)) extensionInstalled = true;
test.describe.serial('Authentication Extension', () => {
test('Go to settings and check if extension is already installed', async ({ navigationBar }) => {
const extensionsPage = await navigationBar.openExtensions();
if (await extensionsPage.extensionIsInstalled(extensionLabel)) extensionInstalled = true;
});

test('Uninstalled previous version of rhel extension', async ({ navigationBar }) => {
test.skip(!extensionInstalled || !!skipInstallation);
test.setTimeout(120_000);
console.log('Extension found already installed, trying to remove!');
await ensureRhelExtensionIsRemoved(navigationBar);
});

test('Install extension through Extension page', async ({ navigationBar }) => {
test.skip(!!skipInstallation);
test.setTimeout(200_000);

const extensionsPage = await navigationBar.openExtensions();
await extensionsPage.installExtensionFromOCIImage(extensionURL);

await playExpect
.poll(async () => await extensionsPage.extensionIsInstalled(extensionLabel), { timeout: 30_000 })
.toBeTruthy();
});
});

test('Uninstalled previous version of rhel extension', async ({ navigationBar }) => {
test.skip(!extensionInstalled || !!skipInstallation);
test.setTimeout(120_000);
console.log('Extension found already installed, trying to remove!');
await ensureRhelExtensionIsRemoved(navigationBar);
test.describe.serial('Red Hat Authentication extension installation', () => {
test('SSO provider is available in Authentication Page', async ({ page, navigationBar }) => {
const settingsBar = await navigationBar.openSettings();
await settingsBar.openTabPage(AuthenticationPage);

const authPage = new AuthenticationPage(page);
await playExpect(authPage.heading).toBeVisible({ timeout: 10_000 });

signInButton = page.getByRole('button', { name: 'Sign in' });
await playExpect(signInButton).toBeVisible();
});

test('Can open authentication page in browser', async ({ navigationBar, page }) => {
test.setTimeout(120_000);
const settingsBar = await navigationBar.openSettings();
await settingsBar.openTabPage(AuthenticationPage);
const authPage = new AuthenticationPage(page);
await playExpect(authPage.heading).toBeVisible({ timeout: 10_000 });

// start up chrome instance and return browser object
browser = await startChromium(chromePort, path.join(...browserOutputPath));

// open the link from PD
await page.bringToFront();

await playExpect(signInButton).toBeEnabled({ timeout: 10_000 });
await signInButton.click();

await page.waitForTimeout(5_000);

const urlMatch = await getEntryFromLogs(page, /\.*openid-connect.*/, regex, 'sso.redhat.com');
if (urlMatch) {
const context = await browser.newContext();
const newPage = await context.newPage();
await newPage.goto(urlMatch);
await newPage.waitForURL(/sso.redhat.com/);
chromiumPage = newPage;
const page = await findPageWithTitleInBrowser(browser, expectedAuthPageTitle);
console.log(`Found page with title: ${await page?.title()}`);
} else {
throw new Error('Did not find Initial SSO Login Page');
}
});

test('User can authenticate via browser', async () => {
// Activate the browser window and perform login
playExpect(chromiumPage).toBeDefined();
if (!chromiumPage) {
throw new Error('Chromium browser page was not initialized');
}
await chromiumPage.bringToFront();
console.log(`Switched to Chrome tab with title: ${await chromiumPage.title()}`);
const usernameAction: ConfirmInputValue = {
inputLocator: chromiumPage.getByRole('textbox', { name: 'Red Hat login or email' }),
inputValue: process.env.DVLPR_USERNAME ?? 'unknown',
confirmLocator: chromiumPage.getByRole('button', { name: 'Next' }),
};
const passwordAction: ConfirmInputValue = {
inputLocator: chromiumPage.getByRole('textbox', { name: 'Password' }),
inputValue: process.env.DVLPR_PASSWORD ?? 'unknown',
confirmLocator: chromiumPage.getByRole('button', { name: 'Log in' }),
};
await performBrowserLogin(chromiumPage, /Log In/, usernameAction, passwordAction, async chromiumPage => {
const backButton = chromiumPage.getByRole('button', { name: 'Go back to Podman Desktop' });
await playExpect(backButton).toBeEnabled();
await backButton.click();
});
await chromiumPage.close();
});
});

test('Install extension through Extension page', async ({ navigationBar }) => {
test.skip(!!skipInstallation);
test.setTimeout(200_000);
test.describe.serial('RHEL VMs Extension', () => {
test('Create RHEL VM', async ({ page }) => {
await createRhelVM(page);

const resourcesPage = new ResourcesPage(page);
await playExpect(resourcesPage.heading).toBeVisible({ timeout: 10_000 });

const machineCard = new ResourceConnectionCardPage(page, 'macadam', 'rhel');
await playExpect.poll(async () => machineCard.doesResourceElementExist(), { timeout: 30_000 }).toBeTruthy();
playExpect(await machineCard.resourceElementConnectionStatus.innerText()).toContain(ResourceElementState.Off);
});

test('Start RHEL VM', async ({ page }) => {
test.setTimeout(70_000);
const machineCard = new ResourceConnectionCardPage(page, 'macadam', 'rhel');
await machineCard.performConnectionAction(ResourceElementActions.Start);

await waitUntil(
async () =>
(await machineCard.resourceElementConnectionStatus.innerText()).includes(ResourceElementState.Running),
{ timeout: 60_000, sendError: true },
);
});

test('Stop RHEL VM', async ({ page }) => {
test.setTimeout(70_000);
const machineCard = new ResourceConnectionCardPage(page, 'macadam', 'rhel');
await machineCard.performConnectionAction(ResourceElementActions.Stop);

await waitUntil(
async () => (await machineCard.resourceElementConnectionStatus.innerText()).includes(ResourceElementState.Off),
{ timeout: 30_000, sendError: true },
);
});

const extensionsPage = await navigationBar.openExtensions();
await extensionsPage.installExtensionFromOCIImage(extensionURL);
test('Remove RHEL VM', async ({ page }) => {
test.setTimeout(70_000);
const machineCard = new ResourceConnectionCardPage(page, 'macadam', 'rhel');
await machineCard.performConnectionAction(ResourceElementActions.Delete);

await playExpect
.poll(async () => await extensionsPage.extensionIsInstalled(extensionLabel), { timeout: 30_000 })
.toBeTruthy();
await playExpect.poll(async () => machineCard.doesResourceElementExist(), { timeout: 30_000 }).toBeFalsy();
});
});

test('Remove RHEL extension through Settings', async ({ navigationBar }) => {
Expand All @@ -99,3 +241,28 @@ async function ensureRhelExtensionIsRemoved(navigationBar: NavigationBar): Promi
.poll(async () => await extensionsPage.extensionIsInstalled(extensionLabel), { timeout: 30_000 })
.toBeFalsy();
}

async function createRhelVM(page: Page, timeout = 120_000): Promise<void> {
Comment thread
cbr7 marked this conversation as resolved.
const navigationBar = new NavigationBar(page);
const rhelResourceCard = new ResourceConnectionCardPage(page, 'macadam');

const settingsPage = await navigationBar.openSettings();
const resourcesPage = await settingsPage.openTabPage(ResourcesPage);
await playExpect(resourcesPage.heading).toBeVisible({ timeout: 10_000 });
await playExpect.poll(async () => resourcesPage.resourceCardIsVisible('macadam')).toBeTruthy();
await playExpect(rhelResourceCard.createButton).toBeVisible();

await rhelResourceCard.createButton.click();

const rhelVMNameInput = page.getByLabel('Machine Name', { exact: true });
await playExpect(rhelVMNameInput).toBeVisible({ timeout: 10_000 });
await playExpect(rhelVMNameInput).toHaveValue('rhel');

const createRhelVMButton = page.getByRole('button', { name: 'Create', exact: true });
await playExpect(createRhelVMButton).toBeEnabled({ timeout: 10_000 });
await createRhelVMButton.click();

const goBackButton = page.getByRole('button', { name: 'Go back to resources' });
await playExpect(goBackButton).toBeEnabled({ timeout: timeout });
await goBackButton.click();
}