import { expect, Page } from '@playwright/test';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const staticDir = path.resolve(__dirname, '..', 'static');
export const clickCreate = async (page: Page) => {
await page.getByRole('button', { name: /创\s*建/ }).click();
};
export const clickCreateType = async (page: Page, type: string) => {
await page.getByRole('button', { name: /创\s*建/ }).click();
await page.getByRole('button', { name: type, exact: true }).click();
};
export const uploadYamlFile = async (page: Page, yamlFileName: string) => {
const filePath = path.join(staticDir, yamlFileName);
const input = page.locator('input[type="file"]').first();
await input.setInputFiles(filePath);
};
export const fillYamlContent = async (page: Page, yamlContent: string) => {
const editor = page.locator('.cm-editor .cm-content, .CodeMirror').first();
await editor.click();
await page.keyboard.press('Control+a');
await page.keyboard.insertText(yamlContent);
};
export const saveCreateOrEdit = async (page: Page) => {
await page.getByRole('button', { name: /保\s*存|确\s*定|确\s*认/ }).first().click();
};
export const cancelCreateOrEdit = async (page: Page) => {
await page.getByRole('button', { name: /取\s*消/ }).first().click();
};
export const searchResource = async (page: Page, name: string) => {
await page.getByRole('textbox', { name: /搜索.*名称/ }).fill(name);
await page.getByRole('button', { name: 'search' }).click();
};
export const deleteResourceInList = async (page: Page, name: string) => {
const row = page.getByRole('row', { name: new RegExp(name) }).first();
await row.locator('svg').click();
await page.getByRole('button', { name: /删\s*除/ }).click();
};
export const confirmDeleteDialog = async (page: Page) => {
await page.getByText('已了解删除操作无法恢复').click();
await page.getByRole('dialog').getByRole('button', { name: /删\s*除/ }).click();
};
export const cancelDeleteDialog = async (page: Page) => {
await page.getByRole('dialog').getByRole('button', { name: /取\s*消/ }).click();
};
export const clickRefresh = async (page: Page) => {
await page.getByRole('button', { name: 'sync' }).click();
};
export const clickResourceNameInList = async (page: Page, name: string) => {
const row = page.getByRole('row', { name: new RegExp(name) }).first();
await row.getByText(name, { exact: true }).click();
};
export const clickDetailTab = async (page: Page) => {
await page.getByRole('tab', { name: '详情' }).click();
};
export const clickYamlTab = async (page: Page) => {
await page.getByRole('tab', { name: 'YAML' }).click();
};
export const clickEventTab = async (page: Page) => {
await page.getByRole('tab', { name: '事件' }).click();
};
export type LabelAnnotationKind = 'label' | 'annotation';
export type LabelAnnotationEntry = 'icon' | 'menu';
export const openModifyLabelAnnotationEditor = async (
page: Page,
kind: LabelAnnotationKind,
entry: LabelAnnotationEntry,
): Promise<void> => {
const successMsg = kind === 'label' ? /编辑标签成功/ : /编辑注解成功/;
await expect(page.getByText(successMsg)).not.toBeVisible({ timeout: 5000 });
if (entry === 'menu') {
await page.getByRole('button', { name: '操作 down' }).click();
await page.getByRole('button', { name: kind === 'label' ? /修改标签|编辑标签/ : /修改注解|编辑注解/ }).click();
return;
}
const sectionLabel = kind === 'label' ? /^标签:$/ : /^注解:$/;
await page.locator('div').filter({ hasText: sectionLabel }).locator('svg').click();
};
export const confirmAddLabelAnnotation = async (page: Page, key?: string, value?: string) => {
await page.getByRole('button', { name: 'plus-circle' }).click();
if (key && value) {
await page.getByRole('textbox').nth(-2).fill(key);
await page.getByRole('textbox').nth(-1).fill(value);
}
};
export const clickDeleteLabelAnnotation = async (page: Page, key: string) => {
const dialog = page.getByRole('dialog');
const textboxes = dialog.getByRole('textbox');
const count = await textboxes.count();
for (let i = 0; i < count; i += 2) {
if (await textboxes.nth(i).inputValue() === key) {
const rowindex = i / 2;
await dialog.locator('[aria-label=delete]').nth(rowindex).click();
await page.getByRole('button', { name: /确\s*定/ }).click();
return;
}
}
throw new Error(`Label/annotation row not found for key: ${key}`);
};
export const cancelLabelAnnotationDialog = async (page: Page) => {
await page.getByRole('dialog').getByRole('button', { name: /取\s*消/ }).click();
};
export const confirmLabelAnnotationDialog = async (page: Page) => {
await page.getByRole('dialog').getByRole('button', { name: /确\s*定/ }).click();
};
export const ExpectAddLabelAnnotationSuccess = async (
page: Page,
kind: LabelAnnotationKind,
key?: string,
value?: string,
) => {
await confirmAddLabelAnnotation(page, key, value);
await confirmLabelAnnotationDialog(page);
const successMsg = (kind === 'label' ? /编辑标签成功/ : /编辑注解成功/);
await expectSuccessMessage(page, successMsg);
await expect(page.locator('.label_item').getByText(`${key}${value}`)).toBeVisible({ timeout: 5000 });
};
export const expectNoChangeLabelAnnotationMessage = async (page: Page, kind: LabelAnnotationKind) => {
await confirmLabelAnnotationDialog(page);
await expect(page.getByText(/未进行修改/)).toBeVisible({ timeout: 5000 });
};
export const expectEmptyLabelAnnotationError = async (page: Page, kind: LabelAnnotationKind) => {
await page.getByRole('button', { name: 'plus-circle' }).click();
await confirmLabelAnnotationDialog(page);
const emptyErrorMsg = (kind === 'label' ? /标签键不能为空/ : /注解键不能为空/);
await expect(page.getByText(emptyErrorMsg)).toBeVisible({ timeout: 5000 });
await cancelLabelAnnotationDialog(page);
};
export const deleteLabelAnnotationAndExpectSuccess = async (
page: Page,
kind: LabelAnnotationKind,
key: string,
value: string = 'test-value',
) => {
await clickDeleteLabelAnnotation(page, key);
const successMsg = (kind === 'label' ? /编辑标签成功/ : /编辑注解成功/);
await expectSuccessMessage(page, successMsg);
await expect(page.locator('.label_item').getByText(`${key}${value}`)).not.toBeVisible({ timeout: 5000 });
};
export const clickDeleteInDetail = async (page: Page) => {
await page.getByRole('button', { name: '操作 down' }).click();
await page.getByRole('button', { name: '删除' }).click();
};
export const clickLogInContainer = async (page: Page) => {
await page.locator('[aria-label=calendar]').click();
};
export const clickTerminalInContainer = async (page: Page) => {
await page.getByRole('table').locator('[aria-label="code"]').first().click();
};
export const closeTerminal = async (page: Page) => {
await page.locator('#root_container div').filter({ hasText: /容器:/ }).locator('svg').nth(2).click();
await page.getByRole('button', { name: /确\s*定/ }).click();
};
export const clickMonitorDashboard = async (page: Page) => {
await page.getByRole('link', { name: /监控大屏/ }).or(page.getByRole('button', { name: /监控大屏/ })).first().click();
};
export const clickExportYaml = async (page: Page) => {
await page.getByText('导出').click();
};
export const clickFindInYaml = async (page: Page) => {
const editor = page.locator('.cm-editor').first();
await editor.click();
await editor.press('ControlOrMeta+f');
};
export const clickCopyYaml = async (page: Page) => {
await page.getByText('复制').click();
};
export const eventFilterQuery = async (page: Page, option: string) => {
await page.locator('.container-platform-select').last().click();
await page.getByTitle(option).locator('div').click();
await page.getByRole('button', { name: /查\s*询/ }).click();
};
export const eventFilterReset = async (page: Page) => {
await page.getByRole('button', { name: /重\s*置/ }).click();
};
export const clickEditResourceInList = async (page: Page, name: string) => {
const row = page.getByRole('row', { name: new RegExp(name) }).first();
await row.locator('svg').click();
await page.getByRole('button', { name: /修\s*改/ }).click();
};
export const clickEditCustomResourceInList = async (page: Page, name: string) => {
const row = page.getByRole('row', { name: new RegExp(name) }).first();
await row.locator('svg').click();
await page.getByText(/修\s*改/).click();
};
export const expectSuccessMessage = async (page: Page, pattern?: RegExp, timeout = 10000) => {
const patternText = pattern || /创建成功|保存成功|修改成功|删除成功/;
await expect(page.getByText(patternText)).toBeVisible({ timeout });
};
export const expectErrorMessage = async (page: Page, pattern?: string | RegExp, timeout = 10000) => {
const patternText = pattern || /创建失败|保存失败|修改失败|删除失败/;
await expect(page.getByText(patternText)).toBeVisible({ timeout });
};
* 编辑器替换文本
* always use string as find */
export const replaceTextInEditor = async (page: Page, find: string, replace: string) => {
const editor = page.locator('.cm-editor').first();
await editor.click();
await editor.press('ControlOrMeta+f');
await editor.getByRole('checkbox', { name: 'regexp' }).check();
await editor.getByRole('textbox', { name: 'Find' }).fill(find);
await editor.getByRole('textbox', { name: 'Replace' }).fill(replace);
await editor.getByRole('button', { name: 'replace all' }).click();
};
export const expectResourceStatus = async (
page: Page, name: string, status: string, maxRetry = 10) => {
for (let i = 0; i < maxRetry; i++) {
await clickRefresh(page);
if (await page.getByRole('row', { name: new RegExp(`${name}.*${status}`) }).first().isVisible()) {
return;
}
await page.waitForTimeout(3000);
}
throw new Error(`Resource ${name} not found with status: ${status} after ${maxRetry} attempts`);
};
export const expectResourceDeleted = async (page: Page, name: string, maxRetry = 10) => {
for (let i = 0; i < maxRetry; i++) {
await clickRefresh(page);
if (await page.getByText(/暂无数据/).isVisible()) {
return;
}
await page.waitForTimeout(3000);
}
throw new Error(`Resource ${name} not deleted after ${maxRetry} attempts`);
};
export const encodeCrdNameForUrl = (crdName: string): string => crdName.replace(/\./g, '_');