Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore tree feedbacks #17078

Merged
merged 10 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ const postRequisitesForTests = () => {
});
};

describe(
// migrated to playwright
describe.skip(
`Advanced search quick filters should work properly for assets`,
{ tags: 'DataAssets' },
() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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 test from '@playwright/test';
import { SidebarItem } from '../../constant/sidebar';
import { Domain } from '../../support/domain/Domain';
import { TableClass } from '../../support/entity/TableClass';
import { createNewPage, redirectToHomePage } from '../../utils/common';
import { assignDomain } from '../../utils/domain';
import { assignTag } from '../../utils/entity';
import { searchAndClickOnOption, selectNullOption } from '../../utils/explore';
import { sidebarClick } from '../../utils/sidebar';

// use the admin user to login
test.use({ storageState: 'playwright/.auth/admin.json' });

const domain = new Domain();
const table = new TableClass();

test.beforeAll('Setup pre-requests', async ({ browser }) => {
const { page, apiContext, afterAction } = await createNewPage(browser);
await table.create(apiContext);
await domain.create(apiContext);
await table.visitEntityPage(page);
await assignDomain(page, domain.data);
await assignTag(page, 'PersonalData.Personal');
await afterAction();
});

test.afterAll('Cleanup', async ({ browser }) => {
const { apiContext, afterAction } = await createNewPage(browser);
await table.delete(apiContext);
await domain.delete(apiContext);
await afterAction();
});

test.beforeEach(async ({ page }) => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.EXPLORE);
});

test('search dropdown should work properly for quick filters', async ({
page,
}) => {
const items = [
{
label: 'Domain',
key: 'domain.displayName.keyword',
value: domain.responseData.displayName,
},
{ label: 'Tag', key: 'tags.tagFQN', value: 'PersonalData.Personal' },
];

for (const filter of items) {
await page.click(`[data-testid="search-dropdown-${filter.label}"]`);
await searchAndClickOnOption(page, filter, true);

const querySearchURL = `/api/v1/search/query?*index=dataAsset*query_filter=*should*${
filter.key
}*${(filter.value ?? '').replace(/ /g, '+').toLowerCase()}*`;

const queryRes = page.waitForResponse(querySearchURL);
await page.click('[data-testid="update-btn"]');
await queryRes;
await page.click('[data-testid="clear-filters"]');
}
});

test('should search for empty or null filters', async ({ page }) => {
const items = [
{ label: 'Owner', key: 'owner.displayName.keyword' },
{ label: 'Tag', key: 'tags.tagFQN' },
{ label: 'Domain', key: 'domain.displayName.keyword' },
{ label: 'Tier', key: 'tier.tagFQN' },
];

for (const filter of items) {
await selectNullOption(page, filter);
}
});

test('should search for multiple values alongwith null filters', async ({
page,
}) => {
const items = [
{
label: 'Tag',
key: 'tags.tagFQN',
value: 'PersonalData.Personal',
},
{
label: 'Domain',
key: 'domain.displayName.keyword',
value: domain.responseData.displayName,
},
];

for (const filter of items) {
await selectNullOption(page, filter);
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,65 +23,65 @@ test.beforeEach(async ({ page }) => {
await sidebarClick(page, SidebarItem.EXPLORE);
});

test('Explore Tree', async ({ page }) => {
await page.getByTestId('explore-tree-tab').getByText('Tree').click();
test.describe('Explore Tree scenarios ', () => {
test('Explore Tree', async ({ page }) => {
await test.step('Check the explore tree', async () => {
await expect(page.getByRole('tree')).toContainText('Databases');
await expect(page.getByRole('tree')).toContainText('Dashboards');
await expect(page.getByRole('tree')).toContainText('Pipelines');
await expect(page.getByRole('tree')).toContainText('Topics');
await expect(page.getByRole('tree')).toContainText('ML Models');
await expect(page.getByRole('tree')).toContainText('Containers');
await expect(page.getByRole('tree')).toContainText('Search Indexes');
await expect(page.getByRole('tree')).toContainText('Governance');

await test.step('Check the explore tree', async () => {
await expect(page.getByRole('tree')).toContainText('Databases');
await expect(page.getByRole('tree')).toContainText('Dashboards');
await expect(page.getByRole('tree')).toContainText('Pipelines');
await expect(page.getByRole('tree')).toContainText('Topics');
await expect(page.getByRole('tree')).toContainText('ML Models');
await expect(page.getByRole('tree')).toContainText('Containers');
await expect(page.getByRole('tree')).toContainText('Search Indexes');
await expect(page.getByRole('tree')).toContainText('Governance');
await page
.locator('div')
.filter({ hasText: /^Governance$/ })
.locator('svg')
.first()
.click();

await page
.locator('div')
.filter({ hasText: /^Governance$/ })
.locator('svg')
.first()
.click();
await expect(page.getByRole('tree')).toContainText('Glossaries');
await expect(page.getByRole('tree')).toContainText('Tags');
});

await expect(page.getByRole('tree')).toContainText('Glossaries');
await expect(page.getByRole('tree')).toContainText('Tags');
});

await test.step('Check the quick filters', async () => {
await expect(
page.getByTestId('search-dropdown-Domain').locator('span')
).toContainText('Domain');
await expect(page.getByTestId('search-dropdown-Owner')).toContainText(
'Owner'
);
await expect(
page.getByTestId('search-dropdown-Tag').locator('span')
).toContainText('Tag');
await test.step('Check the quick filters', async () => {
await expect(
page.getByTestId('search-dropdown-Domain').locator('span')
).toContainText('Domain');
await expect(page.getByTestId('search-dropdown-Owner')).toContainText(
'Owner'
);
await expect(
page.getByTestId('search-dropdown-Tag').locator('span')
).toContainText('Tag');

await page.getByRole('button', { name: 'Tier' }).click();
await page.getByRole('button', { name: 'Tier' }).click();

await expect(
page.getByTestId('search-dropdown-Tier').locator('span')
).toContainText('Tier');
await expect(
page.getByTestId('search-dropdown-Service').locator('span')
).toContainText('Service');
await expect(
page.getByTestId('search-dropdown-Service Type').locator('span')
).toContainText('Service Type');
});
await expect(
page.getByTestId('search-dropdown-Tier').locator('span')
).toContainText('Tier');
await expect(
page.getByTestId('search-dropdown-Service').locator('span')
).toContainText('Service');
await expect(
page.getByTestId('search-dropdown-Service Type').locator('span')
).toContainText('Service Type');
});

await test.step('Click on tree item and check quick filter', async () => {
await page.getByTestId('explore-tree-title-Glossaries').click();
await test.step('Click on tree item and check quick filter', async () => {
await page.getByTestId('explore-tree-title-Glossaries').click();

await expect(page.getByTestId('search-dropdown-Data Assets')).toContainText(
'Data Assets: glossaryTerm'
);
await expect(
page.getByTestId('search-dropdown-Data Assets')
).toContainText('Data Assets: glossaryTerm');

await page.getByTestId('explore-tree-title-Tags').click();
await page.getByTestId('explore-tree-title-Tags').click();

await expect(page.getByTestId('search-dropdown-Data Assets')).toContainText(
'Data Assets: tag'
);
await expect(
page.getByTestId('search-dropdown-Data Assets')
).toContainText('Data Assets: tag');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ export const checkDataAssetWidget = async (
await page.click('[data-testid="welcome-screen-close-btn"]');

const quickFilterResponse = page.waitForResponse(
`/api/v1/search/query?q=&index=${index}*${serviceType}*`
`/api/v1/search/query?q=&index=dataAsset*${serviceType}*`
);

await page
Expand All @@ -897,13 +897,13 @@ export const checkDataAssetWidget = async (
page.locator('[data-testid="search-dropdown-Service Type"]')
).toContainText(serviceType);

const isSelected = await page
.getByRole('menuitem', { name: type })
.evaluate((element) => {
return element.classList.contains('ant-menu-item-selected');
});

expect(isSelected).toBe(true);
await expect(
page
.getByTestId('explore-tree')
.locator('span')
.filter({ hasText: serviceType })
.first()
).toHaveClass(/ant-tree-node-selected/);
};

export const escapeESReservedCharacters = (text?: string) => {
Expand Down
109 changes: 109 additions & 0 deletions openmetadata-ui/src/main/resources/ui/playwright/utils/explore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* 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 { expect } from '@playwright/test';
import { Page } from 'playwright';

export const searchAndClickOnOption = async (
page: Page,
filter: { key: string; label: string; value?: string },
checkedAfterClick: boolean
) => {
let testId = (filter.value ?? '').toLowerCase();
// Filtering for tiers is done on client side, so no API call will be triggered
if (filter.key !== 'tier.tagFQN') {
const searchRes = page.waitForResponse(
`/api/v1/search/aggregate?index=dataAsset&field=${filter.key}**`
);

await page.fill('[data-testid="search-input"]', filter.value ?? '');
await searchRes;
} else {
testId = filter.value ?? '';
}

await page.waitForSelector(`[data-testid="${testId}"]`);
await page.click(`[data-testid="${testId}"]`);
await checkCheckboxStatus(page, `${testId}-checkbox`, checkedAfterClick);
};

export const selectNullOption = async (
page: Page,
filter: { key: string; label: string; value?: string }
) => {
const queryFilter = JSON.stringify({
query: {
bool: {
must: [
{
bool: {
should: [
{
bool: {
must_not: {
exists: { field: `${filter.key}` },
},
},
},
...(filter.value
? [
{
term: {
[filter.key]:
filter.key === 'tier.tagFQN'
? filter.value
: filter.value.toLowerCase(),
},
},
]
: []),
],
},
},
],
},
},
});

const querySearchURL = `/api/v1/search/query?*index=dataAsset*`;
await page.click(`[data-testid="search-dropdown-${filter.label}"]`);
await page.click(`[data-testid="no-option-checkbox"]`);
if (filter.value) {
await searchAndClickOnOption(page, filter, true);
}

const queryRes = page.waitForResponse(querySearchURL);
await page.click('[data-testid="update-btn"]');
const queryResponseData = await queryRes;
const request = await queryResponseData.request();

const queryParams = request.url().split('?')[1];
const queryParamsObj = new URLSearchParams(queryParams);

const queryParamValue = queryParamsObj.get('query_filter');
const isQueryFilterPresent = queryParamValue === queryFilter;

expect(isQueryFilterPresent).toBeTruthy();

await page.click(`[data-testid="clear-filters"]`);
};

export const checkCheckboxStatus = async (
page: Page,
boxId: string,
isChecked: boolean
) => {
const checkbox = await page.getByTestId(boxId);
const isCheckedOnPage = await checkbox.isChecked();

await expect(isCheckedOnPage).toEqual(isChecked);
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ export type ExploreTreeProps = {

export type TreeNodeData = {
isRoot?: boolean;
isStatic?: boolean;
currentBucketKey?: string;
currentBucketValue?: string;
filterField?: ExploreQuickFilterField[];
parentSearchIndex?: string;
rootIndex?: string;
entityType?: EntityType;
dataId?: string;
};

export type DatabaseFields =
Expand Down
Loading
Loading