Skip to content

Commit

Permalink
chore(test): create playwground e2e test
Browse files Browse the repository at this point in the history
Signed-off-by: Vladimir Lazar <vlazar@redhat.com>
  • Loading branch information
cbr7 committed Dec 17, 2024
1 parent 6da9143 commit be5b63b
Showing 3 changed files with 141 additions and 0 deletions.
58 changes: 58 additions & 0 deletions tests/playwright/src/ai-lab-extension.spec.ts
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ import { AILabExtensionDetailsPage } from './model/podman-extension-ai-lab-detai
import type { AILabCatalogPage } from './model/ai-lab-catalog-page';
import { handleWebview } from './utils/webviewHandler';
import type { AILabServiceDetailsPage } from './model/ai-lab-service-details-page';
import type { AILabPlaygroundsPage } from './model/ai-lab-playgrounds-page';

const AI_LAB_EXTENSION_OCI_IMAGE =
process.env.EXTENSION_OCI_IMAGE ?? 'ghcr.io/containers/podman-desktop-extension-ai-lab:nightly';
@@ -204,6 +205,63 @@ test.describe.serial(`AI Lab extension installation and verification`, { tag: '@
});
});

['instructlab/granite-7b-lab-GGUF'].forEach(modelName => {
test.describe.serial(`AI Lan playground creation and deletion`, () => {
let catalogPage: AILabCatalogPage;
let playgroundsPage: AILabPlaygroundsPage;

const playgroundName = 'test playground';

test.skip(isLinux, `Skipping model service creation on Linux`);
test.beforeAll(`Open AI Lab Catalog`, async ({ runner, page, navigationBar }) => {
[page, webview] = await handleWebview(runner, page, navigationBar);
aiLabPage = new AILabPage(page, webview);
await aiLabPage.navigationBar.waitForLoad();

catalogPage = await aiLabPage.navigationBar.openCatalog();
await catalogPage.waitForLoad();
});

test(`Download ${modelName} model if not available`, async () => {
test.setTimeout(310_000);
if (!(await catalogPage.isModelDownloaded(modelName))) {
await catalogPage.downloadModel(modelName);
}
await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await waitForCatalogModel(modelName), { timeout: 300_000, intervals: [5_000] })
.toBeTruthy();
});

test(`Create AI Lab playground for ${modelName}`, async () => {
playgroundsPage = await aiLabPage.navigationBar.openPlaygrounds();
await playgroundsPage.waitForLoad();

await playgroundsPage.createNewPlayground(playgroundName);
await playgroundsPage.waitForLoad();
await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await playgroundsPage.doesPlaygroundExist(playgroundName), { timeout: 60_000 })
.toBeTruthy();
});

test(`Delete AI Lab playground for ${modelName}`, async () => {
await playgroundsPage.deletePlayground(playgroundName);
await playgroundsPage.waitForLoad();

await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await playgroundsPage.doesPlaygroundExist(playgroundName), { timeout: 60_000 })
.toBeFalsy();
});

test.afterAll(`Cleaning up service model`, async () => {
test.setTimeout(60_000);
await cleanupServiceModels();
});
});
});

['Audio to Text', 'ChatBot', 'Summarizer', 'Code Generation'].forEach(appName => {
test.describe.serial(`AI Recipe installation`, () => {
let recipesCatalogPage: AILabRecipesCatalogPage;
7 changes: 7 additions & 0 deletions tests/playwright/src/model/ai-lab-navigation-bar.ts
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ import { AILabRecipesCatalogPage } from './ai-lab-recipes-catalog-page';
import { AiRunningAppsPage } from './ai-lab-running-apps-page';
import { AiModelServicePage } from './ai-lab-model-service-page';
import { AILabCatalogPage } from './ai-lab-catalog-page';
import { AILabPlaygroundsPage } from './ai-lab-playgrounds-page';

export class AILabNavigationBar extends AILabBasePage {
readonly navigationBar: Locator;
@@ -71,4 +72,10 @@ export class AILabNavigationBar extends AILabBasePage {
await this.catalogButton.click();
return new AILabCatalogPage(this.page, this.webview);
}

async openPlaygrounds(): Promise<AILabPlaygroundsPage> {
await playExpect(this.playgroundsButton).toBeEnabled();
await this.playgroundsButton.click();
return new AILabPlaygroundsPage(this.page, this.webview);
}
}
76 changes: 76 additions & 0 deletions tests/playwright/src/model/ai-lab-playgrounds-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import type { Locator, Page } from '@playwright/test';
import { expect as playExpect } from '@playwright/test';
import { AILabBasePage } from './ai-lab-base-page';
import { handleConfirmationDialog } from '@podman-desktop/tests-playwright';

export class AILabPlaygroundsPage extends AILabBasePage {
readonly additionalActions: Locator;
readonly newPlaygroundButton: Locator;
readonly playgroundNameInput: Locator;
readonly createPlaygroundButton: Locator;

constructor(page: Page, webview: Page) {
super(page, webview, 'Playground Environments');
this.additionalActions = this.webview.getByRole('group', { name: 'additionalActions' });
this.newPlaygroundButton = this.additionalActions.getByRole('button', { name: 'New Playground', exact: true });
this.playgroundNameInput = this.webview.getByRole('textbox', { name: 'playgroundName' });
this.createPlaygroundButton = this.webview.getByRole('button', { name: 'Create playground', exact: true });
}

async waitForLoad(): Promise<void> {
await playExpect(this.heading).toBeVisible();
}

async createNewPlayground(name: string): Promise<this> {
await playExpect(this.newPlaygroundButton).toBeEnabled();
await this.newPlaygroundButton.click();
await playExpect(this.playgroundNameInput).toBeVisible();
await this.playgroundNameInput.fill(name);
await playExpect(this.playgroundNameInput).toHaveValue(name);
await playExpect(this.createPlaygroundButton).toBeEnabled();
await this.createPlaygroundButton.click();
return this;
}

async deletePlayground(playgroundName: string): Promise<this> {
const playgroundRow = await this.getPlaygroundRowByName(playgroundName);
if (!playgroundRow) {
throw new Error(`Playground ${playgroundName} not found`);
}
const deleteButton = playgroundRow.getByRole('button', { name: 'Delete conversation', exact: true });
await playExpect(deleteButton).toBeEnabled();
await deleteButton.click();
await handleConfirmationDialog(this.page, 'Podman AI Lab', true, 'Confirm');
return this;
}

async doesPlaygroundExist(playgroundName: string): Promise<boolean> {
return (await this.getPlaygroundRowByName(playgroundName)) !== undefined;
}

private async getPlaygroundRowByName(playgroundName: string): Promise<Locator | undefined> {
const row = this.webview.getByRole('row', { name: playgroundName, exact: true });
if ((await row.count()) > 0) {
return row;
}
return undefined;
}
}

0 comments on commit be5b63b

Please sign in to comment.