mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-26 18:06:03 +00:00
236 lines
7.3 KiB
TypeScript
236 lines
7.3 KiB
TypeScript
/*
|
|
* Copyright 2024 Collate.
|
|
* 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 { Browser, expect, Page, request } from '@playwright/test';
|
|
import { randomUUID } from 'crypto';
|
|
import { SidebarItem } from '../constant/sidebar';
|
|
import { adjectives, nouns } from '../constant/user';
|
|
import { Domain } from '../support/domain/Domain';
|
|
import { sidebarClick } from './sidebar';
|
|
|
|
export const uuid = () => randomUUID().split('-')[0];
|
|
|
|
export const descriptionBox =
|
|
'.toastui-editor-md-container > .toastui-editor > .ProseMirror';
|
|
export const INVALID_NAMES = {
|
|
MAX_LENGTH:
|
|
'a87439625b1c2d3e4f5061728394a5b6c7d8e90a1b2c3d4e5f67890aba87439625b1c2d3e4f5061728394a5b6c7d8e90a1b2c3d4e5f67890abName can be a maximum of 128 characters',
|
|
WITH_SPECIAL_CHARS: '::normalName::',
|
|
};
|
|
|
|
export const NAME_VALIDATION_ERROR =
|
|
'Name must contain only letters, numbers, underscores, hyphens, periods, parenthesis, and ampersands.';
|
|
|
|
export const NAME_MIN_MAX_LENGTH_VALIDATION_ERROR =
|
|
'Name size must be between 2 and 64';
|
|
|
|
export const NAME_MAX_LENGTH_VALIDATION_ERROR =
|
|
'Name can be a maximum of 128 characters';
|
|
|
|
export const getToken = async (page: Page) => {
|
|
return page.evaluate(
|
|
() =>
|
|
JSON.parse(localStorage.getItem('om-session') ?? '{}')?.state
|
|
?.oidcIdToken ?? ''
|
|
);
|
|
};
|
|
|
|
export const getAuthContext = async (token: string) => {
|
|
return await request.newContext({
|
|
extraHTTPHeaders: {
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
});
|
|
};
|
|
|
|
export const redirectToHomePage = async (page: Page) => {
|
|
await page.goto('/');
|
|
await page.waitForURL('**/my-data');
|
|
};
|
|
|
|
export const createNewPage = async (browser: Browser) => {
|
|
// create a new page
|
|
const page = await browser.newPage();
|
|
await redirectToHomePage(page);
|
|
|
|
// get the token from localStorage
|
|
const token = await getToken(page);
|
|
|
|
// create a new context with the token
|
|
const apiContext = await getAuthContext(token);
|
|
|
|
const afterAction = async () => {
|
|
await apiContext.dispose();
|
|
await page.close();
|
|
};
|
|
|
|
return { page, apiContext, afterAction };
|
|
};
|
|
|
|
/**
|
|
* Retrieves the API context for the given page.
|
|
* @param page The Playwright page object.
|
|
* @returns An object containing the API context and a cleanup function.
|
|
*/
|
|
export const getApiContext = async (page: Page) => {
|
|
const token = await getToken(page);
|
|
const apiContext = await getAuthContext(token);
|
|
const afterAction = async () => await apiContext.dispose();
|
|
|
|
return { apiContext, afterAction };
|
|
};
|
|
|
|
export const getEntityTypeSearchIndexMapping = (entityType: string) => {
|
|
const entityMapping = {
|
|
Table: 'table_search_index',
|
|
Topic: 'topic_search_index',
|
|
Dashboard: 'dashboard_search_index',
|
|
Pipeline: 'pipeline_search_index',
|
|
MlModel: 'mlmodel_search_index',
|
|
Container: 'container_search_index',
|
|
SearchIndex: 'search_entity_search_index',
|
|
ApiEndpoint: 'api_endpoint_search_index',
|
|
};
|
|
|
|
return entityMapping[entityType];
|
|
};
|
|
|
|
export const toastNotification = async (
|
|
page: Page,
|
|
message: string | RegExp
|
|
) => {
|
|
await expect(page.getByRole('alert').first()).toHaveText(message);
|
|
|
|
await page.getByLabel('close', { exact: true }).first().click();
|
|
};
|
|
|
|
export const clickOutside = async (page: Page) => {
|
|
await page.locator('body').click({
|
|
position: {
|
|
x: 0,
|
|
y: 0,
|
|
},
|
|
}); // with this action left menu bar is getting opened
|
|
await page.mouse.move(1280, 0); // moving out side left menu bar to avoid random failure due to left menu bar
|
|
};
|
|
|
|
export const visitUserProfilePage = async (page: Page) => {
|
|
await page.locator('[data-testid="dropdown-profile"] svg').click();
|
|
await page.waitForSelector('[role="menu"].profile-dropdown', {
|
|
state: 'visible',
|
|
});
|
|
const userResponse = page.waitForResponse(
|
|
'/api/v1/users/name/*?fields=*&include=all'
|
|
);
|
|
await page.getByTestId('user-name').click();
|
|
await userResponse;
|
|
await clickOutside(page);
|
|
};
|
|
|
|
export const assignDomain = async (
|
|
page: Page,
|
|
domain: { name: string; displayName: string }
|
|
) => {
|
|
await page.getByTestId('add-domain').click();
|
|
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
|
const searchDomain = page.waitForResponse(
|
|
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
|
|
);
|
|
await page
|
|
.getByTestId('selectable-list')
|
|
.getByTestId('searchbar')
|
|
.fill(domain.name);
|
|
await searchDomain;
|
|
await page.getByRole('listitem', { name: domain.displayName }).click();
|
|
|
|
await expect(page.getByTestId('domain-link')).toContainText(
|
|
domain.displayName
|
|
);
|
|
};
|
|
|
|
export const updateDomain = async (
|
|
page: Page,
|
|
domain: { name: string; displayName: string }
|
|
) => {
|
|
await page.getByTestId('add-domain').click();
|
|
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
|
await page.getByTestId('selectable-list').getByTestId('searchbar').clear();
|
|
const searchDomain = page.waitForResponse(
|
|
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
|
|
);
|
|
await page
|
|
.getByTestId('selectable-list')
|
|
.getByTestId('searchbar')
|
|
.fill(domain.name);
|
|
await searchDomain;
|
|
await page.getByRole('listitem', { name: domain.displayName }).click();
|
|
|
|
await expect(page.getByTestId('domain-link')).toContainText(
|
|
domain.displayName
|
|
);
|
|
};
|
|
|
|
export const removeDomain = async (page: Page) => {
|
|
await page.getByTestId('add-domain').click();
|
|
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
|
|
|
await expect(page.getByTestId('remove-owner').locator('path')).toBeVisible();
|
|
|
|
await page.getByTestId('remove-owner').locator('svg').click();
|
|
|
|
await expect(page.getByTestId('no-domain-text')).toContainText('No Domain');
|
|
};
|
|
|
|
export const visitGlossaryPage = async (page: Page, glossaryName: string) => {
|
|
await redirectToHomePage(page);
|
|
const glossaryResponse = page.waitForResponse('/api/v1/glossaries?fields=*');
|
|
await sidebarClick(page, SidebarItem.GLOSSARY);
|
|
await glossaryResponse;
|
|
await page.getByRole('menuitem', { name: glossaryName }).click();
|
|
};
|
|
|
|
export const getRandomFirstName = () => {
|
|
return `${
|
|
adjectives[Math.floor(Math.random() * adjectives.length)]
|
|
}${uuid()}`;
|
|
};
|
|
export const getRandomLastName = () => {
|
|
return `${nouns[Math.floor(Math.random() * nouns.length)]}${uuid()}`;
|
|
};
|
|
|
|
export const generateRandomUsername = (prefix = '') => {
|
|
const firstName = `${prefix}${getRandomFirstName()}`;
|
|
const lastName = `${prefix}${getRandomLastName()}`;
|
|
|
|
return {
|
|
firstName,
|
|
lastName,
|
|
email: `${firstName}.${lastName}@example.com`,
|
|
password: 'User@OMD123',
|
|
};
|
|
};
|
|
|
|
export const verifyDomainPropagation = async (
|
|
page: Page,
|
|
domain: Domain['responseData'],
|
|
childFqnSearchTerm: string
|
|
) => {
|
|
await page.getByTestId('searchBox').fill(childFqnSearchTerm);
|
|
await page.getByTestId('searchBox').press('Enter');
|
|
|
|
await expect(
|
|
page
|
|
.getByTestId(`table-data-card_${childFqnSearchTerm}`)
|
|
.getByTestId('domain-link')
|
|
).toContainText(domain.displayName);
|
|
};
|