blob: 36d09a8b7ed3e0b8de7045b6253f8918caafa736 [file]
/*
* 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.
*/
import { expect, test, Page, TestInfo } from '@playwright/test';
import { LoginTestUtil } from './models/login-page.util';
import { NotebookUtil } from './models/notebook.util';
export const PAGES = {
// Main App
APP: 'src/app/app.component',
// Core
CORE: {
DESTROY_HOOK: 'src/app/core/destroy-hook/destroy-hook.component'
},
// Pages
PAGES: {
LOGIN: 'src/app/pages/login/login.component'
},
// Pages - Workspace
WORKSPACE: {
MAIN: 'src/app/pages/workspace/workspace.component',
CONFIGURATION: 'src/app/pages/workspace/configuration/configuration.component',
CREDENTIAL: 'src/app/pages/workspace/credential/credential.component',
HOME: 'src/app/pages/workspace/home/home.component',
INTERPRETER: 'src/app/pages/workspace/interpreter/interpreter.component',
INTERPRETER_CREATE_REPO:
'src/app/pages/workspace/interpreter/create-repository-modal/create-repository-modal.component',
INTERPRETER_ITEM: 'src/app/pages/workspace/interpreter/item/item.component',
JOB_MANAGER: 'src/app/pages/workspace/job-manager/job-manager.component',
JOB_STATUS: 'src/app/pages/workspace/job-manager/job-status/job-status.component',
JOB: 'src/app/pages/workspace/job-manager/job/job.component',
NOTEBOOK_REPOS: 'src/app/pages/workspace/notebook-repos/notebook-repos.component',
NOTEBOOK_REPOS_ITEM: 'src/app/pages/workspace/notebook-repos/item/item.component',
NOTEBOOK_SEARCH: 'src/app/pages/workspace/notebook-search/notebook-search.component',
NOTEBOOK_SEARCH_RESULT: 'src/app/pages/workspace/notebook-search/result-item/result-item.component',
NOTEBOOK: 'src/app/pages/workspace/notebook/notebook.component',
NOTEBOOK_ACTION_BAR: 'src/app/pages/workspace/notebook/action-bar/action-bar.component',
NOTEBOOK_ADD_PARAGRAPH: 'src/app/pages/workspace/notebook/add-paragraph/add-paragraph.component',
NOTEBOOK_INTERPRETER_BINDING: 'src/app/pages/workspace/notebook/interpreter-binding/interpreter-binding.component',
NOTEBOOK_NOTE_FORM: 'src/app/pages/workspace/notebook/note-form-block/note-form-block.component',
NOTEBOOK_PERMISSIONS: 'src/app/pages/workspace/notebook/permissions/permissions.component',
NOTEBOOK_REVISIONS: 'src/app/pages/workspace/notebook/revisions-comparator/revisions-comparator.component',
NOTEBOOK_ELASTIC_INPUT: 'src/app/pages/workspace/notebook/share/elastic-input/elastic-input.component',
NOTEBOOK_SIDEBAR: 'src/app/pages/workspace/notebook/sidebar/sidebar.component',
NOTEBOOK_PARAGRAPH: 'src/app/pages/workspace/notebook/paragraph/paragraph.component',
NOTEBOOK_PARAGRAPH_CODE_EDITOR: 'src/app/pages/workspace/notebook/paragraph/code-editor/code-editor.component',
NOTEBOOK_PARAGRAPH_CONTROL: 'src/app/pages/workspace/notebook/paragraph/control/control.component',
NOTEBOOK_PARAGRAPH_FOOTER: 'src/app/pages/workspace/notebook/paragraph/footer/footer.component',
NOTEBOOK_PARAGRAPH_PROGRESS: 'src/app/pages/workspace/notebook/paragraph/progress/progress.component',
PUBLISHED_PARAGRAPH: 'src/app/pages/workspace/published/paragraph/paragraph.component',
SHARE_DYNAMIC_FORMS: 'src/app/pages/workspace/share/dynamic-forms/dynamic-forms.component',
SHARE_RESULT: 'src/app/pages/workspace/share/result/result.component'
},
// Share
SHARE: {
ABOUT_ZEPPELIN: 'src/app/share/about-zeppelin/about-zeppelin.component',
CODE_EDITOR: 'src/app/share/code-editor/code-editor.component',
FOLDER_RENAME: 'src/app/share/folder-rename/folder-rename.component',
HEADER: 'src/app/share/header/header.component',
NODE_LIST: 'src/app/share/node-list/node-list.component',
NOTE_CREATE: 'src/app/share/note-create/note-create.component',
NOTE_IMPORT: 'src/app/share/note-import/note-import.component',
NOTE_RENAME: 'src/app/share/note-rename/note-rename.component',
NOTE_TOC: 'src/app/share/note-toc/note-toc.component',
PAGE_HEADER: 'src/app/share/page-header/page-header.component',
RESIZE_HANDLE: 'src/app/share/resize-handle/resize-handle.component',
SHORTCUT: 'src/app/share/shortcut/shortcut.component',
SPIN: 'src/app/share/spin/spin.component',
THEME_TOGGLE: 'src/app/share/theme-toggle/theme-toggle.component'
},
// Visualizations
VISUALIZATIONS: {
AREA_CHART: 'src/app/visualizations/area-chart/area-chart-visualization.component',
BAR_CHART: 'src/app/visualizations/bar-chart/bar-chart-visualization.component',
LINE_CHART: 'src/app/visualizations/line-chart/line-chart-visualization.component',
PIE_CHART: 'src/app/visualizations/pie-chart/pie-chart-visualization.component',
SCATTER_CHART: 'src/app/visualizations/scatter-chart/scatter-chart-visualization.component',
TABLE: 'src/app/visualizations/table/table-visualization.component',
COMMON: {
PIVOT_SETTING: 'src/app/visualizations/common/pivot-setting/pivot-setting.component',
SCATTER_SETTING: 'src/app/visualizations/common/scatter-setting/scatter-setting.component',
X_AXIS_SETTING: 'src/app/visualizations/common/x-axis-setting/x-axis-setting.component'
}
}
} as const;
export function addPageAnnotation(pageName: string, testInfo: TestInfo) {
testInfo.annotations.push({
type: 'page',
description: pageName
});
}
export function addPageAnnotationBeforeEach(pageName: string) {
test.beforeEach(async ({}, testInfo) => {
addPageAnnotation(pageName, testInfo);
});
}
interface PageStructureType {
[key: string]: string | PageStructureType;
}
export function flattenPageComponents(pages: PageStructureType): string[] {
const result: string[] = [];
function flatten(obj: PageStructureType) {
for (const value of Object.values(obj)) {
if (typeof value === 'string') {
result.push(value);
} else if (typeof value === 'object' && value !== null) {
flatten(value);
}
}
}
flatten(pages);
return result.sort();
}
export function getCoverageTransformPaths(): string[] {
return flattenPageComponents(PAGES);
}
export async function waitForUrlNotContaining(page: Page, fragment: string) {
await page.waitForURL(url => !url.toString().includes(fragment));
}
export function getCurrentPath(page: Page): string {
const url = new URL(page.url());
return url.hash || url.pathname;
}
export async function getBasicPageMetadata(page: Page): Promise<{
title: string;
path: string;
}> {
return {
title: await page.title(),
path: getCurrentPath(page)
};
}
export async function performLoginIfRequired(page: Page): Promise<boolean> {
const isShiroEnabled = await LoginTestUtil.isShiroEnabled();
if (!isShiroEnabled) {
return false;
}
const credentials = await LoginTestUtil.getTestCredentials();
const validUsers = Object.values(credentials).filter(
cred => cred.username && cred.password && cred.username !== 'INVALID_USER' && cred.username !== 'EMPTY_CREDENTIALS'
);
if (validUsers.length === 0) {
return false;
}
const testUser = validUsers[0];
const isLoginVisible = await page.locator('zeppelin-login').isVisible();
if (isLoginVisible) {
const userNameInput = page.getByRole('textbox', { name: 'User Name' });
const passwordInput = page.getByRole('textbox', { name: 'Password' });
const loginButton = page.getByRole('button', { name: 'Login' });
await userNameInput.fill(testUser.username);
await passwordInput.fill(testUser.password);
await loginButton.click();
await page.waitForSelector('text=Welcome to Zeppelin!', { timeout: 5000 });
return true;
}
return false;
}
export async function waitForZeppelinReady(page: Page): Promise<void> {
try {
await page.waitForLoadState('networkidle', { timeout: 30000 });
await page.waitForFunction(
() => {
const hasAngular = document.querySelector('[ng-version]') !== null;
const hasZeppelinContent =
document.body.textContent?.includes('Zeppelin') ||
document.body.textContent?.includes('Notebook') ||
document.body.textContent?.includes('Welcome');
const hasZeppelinRoot = document.querySelector('zeppelin-root') !== null;
return hasAngular && (hasZeppelinContent || hasZeppelinRoot);
},
{ timeout: 60 * 1000 }
);
} catch (error) {
throw error instanceof Error ? error : new Error(`Zeppelin loading failed: ${String(error)}`);
}
}