mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-03 12:08:31 +00:00
fix(ui): tour functionality (#22711)
* fix: hide scrollbar from sidebar * fix feed id * fix tour * fix tour test * monir fix * remove redundunt myData tests * fix tourpage useEffect * fix curated asset test
This commit is contained in:
parent
c7f50c9376
commit
802c922ec8
@ -35,6 +35,8 @@ const test = base.extend<{ page: Page }>({
|
||||
});
|
||||
|
||||
base.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
test.slow(true);
|
||||
|
||||
const { afterAction, apiContext } = await performAdminLogin(browser);
|
||||
await adminUser.create(apiContext);
|
||||
await adminUser.setAdminRole(apiContext);
|
||||
@ -43,6 +45,8 @@ base.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
});
|
||||
|
||||
base.afterAll('Cleanup', async ({ browser }) => {
|
||||
test.slow(true);
|
||||
|
||||
const { afterAction, apiContext } = await performAdminLogin(browser);
|
||||
await adminUser.delete(apiContext);
|
||||
await persona.delete(apiContext);
|
||||
@ -50,7 +54,7 @@ base.afterAll('Cleanup', async ({ browser }) => {
|
||||
});
|
||||
|
||||
test.describe('Curated Assets', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
test.beforeAll(async ({ page }) => {
|
||||
test.slow(true);
|
||||
|
||||
await redirectToHomePage(page);
|
||||
@ -93,7 +97,7 @@ test.describe('Curated Assets', () => {
|
||||
await selectOption(
|
||||
page,
|
||||
ruleLocator.locator('.rule--operator .ant-select'),
|
||||
'!='
|
||||
'Not in'
|
||||
);
|
||||
|
||||
await selectOption(
|
||||
|
||||
@ -18,6 +18,9 @@ import { redirectToHomePage } from '../../utils/common';
|
||||
const user = new UserClass();
|
||||
|
||||
const validateTourSteps = async (page: Page) => {
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForSelector(`[data-tour-elem="badge"]`);
|
||||
|
||||
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('1');
|
||||
|
||||
// step 1
|
||||
@ -30,8 +33,8 @@ const validateTourSteps = async (page: Page) => {
|
||||
|
||||
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('3');
|
||||
|
||||
await page.getByTestId('customise-searchbox').fill('dim_a');
|
||||
await page.getByTestId('customise-searchbox').press('Enter');
|
||||
await page.getByTestId('searchBox').fill('dim_a');
|
||||
await page.getByTestId('searchBox').press('Enter');
|
||||
|
||||
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('4');
|
||||
|
||||
@ -108,7 +111,7 @@ const validateTourSteps = async (page: Page) => {
|
||||
await page.getByTestId('saveButton').click();
|
||||
};
|
||||
|
||||
test.describe.skip('Tour should work properly', () => {
|
||||
test.describe('Tour should work properly', () => {
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await user.create(apiContext);
|
||||
@ -130,6 +133,9 @@ test.describe.skip('Tour should work properly', () => {
|
||||
await page.locator('[data-testid="help-icon"]').click();
|
||||
await page.getByRole('link', { name: 'Tour', exact: true }).click();
|
||||
await page.waitForURL('**/tour');
|
||||
|
||||
await page.waitForSelector('#feedWidgetData');
|
||||
|
||||
await validateTourSteps(page);
|
||||
});
|
||||
|
||||
@ -141,14 +147,17 @@ test.describe.skip('Tour should work properly', () => {
|
||||
await page.getByText('Take a product tour to get started!').click();
|
||||
await page.waitForURL('**/tour');
|
||||
|
||||
await page.waitForSelector('#feedWidgetData');
|
||||
|
||||
await validateTourSteps(page);
|
||||
});
|
||||
|
||||
test('Tour should work from URL directly', async ({ page }) => {
|
||||
await expect(page.getByTestId('global-search-selector')).toBeVisible();
|
||||
|
||||
await page.goto('/tour');
|
||||
await page.waitForURL('**/tour');
|
||||
|
||||
await page.waitForSelector('#feedWidgetData');
|
||||
|
||||
await validateTourSteps(page);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* 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, Page, test as base } from '@playwright/test';
|
||||
import { TableClass } from '../../support/entity/TableClass';
|
||||
import { UserClass } from '../../support/user/UserClass';
|
||||
import { performAdminLogin } from '../../utils/admin';
|
||||
import { redirectToHomePage, removeLandingBanner } from '../../utils/common';
|
||||
import { verifyEntities } from '../../utils/myData';
|
||||
|
||||
const user = new UserClass();
|
||||
const TableEntities = Array(20)
|
||||
.fill(undefined)
|
||||
.map(() => new TableClass());
|
||||
|
||||
const test = base.extend<{ page: Page }>({
|
||||
page: async ({ browser }, use) => {
|
||||
const Page = await browser.newPage();
|
||||
await user.login(Page);
|
||||
await use(Page);
|
||||
await Page.close();
|
||||
},
|
||||
});
|
||||
|
||||
test.describe.serial('My Data page', () => {
|
||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await user.create(apiContext);
|
||||
const tablePromises = TableEntities.map(async (table) => {
|
||||
await table.create(apiContext);
|
||||
await table.patch({
|
||||
apiContext,
|
||||
patchData: [
|
||||
{
|
||||
op: 'add',
|
||||
path: '/owners/0',
|
||||
value: {
|
||||
id: user.responseData.id,
|
||||
type: 'user',
|
||||
deleted: false,
|
||||
displayName: user.responseData.displayName,
|
||||
fullyQualifiedName: user.responseData.fullyQualifiedName,
|
||||
href: user.responseData['href'] ?? '',
|
||||
name: user.responseData.name,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
await table.followTable(apiContext, user.responseData.id);
|
||||
});
|
||||
|
||||
await Promise.all(tablePromises);
|
||||
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.afterAll('Cleanup', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await user.delete(apiContext);
|
||||
|
||||
await Promise.all(TableEntities.map((table) => table.delete(apiContext)));
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.beforeEach('Visit entity details page', async ({ page }) => {
|
||||
await redirectToHomePage(page);
|
||||
await removeLandingBanner(page);
|
||||
});
|
||||
|
||||
test.skip('Verify my data widget', async ({ page }) => {
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForSelector('[data-testid="loader"]', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
// Verify total count
|
||||
await expect(
|
||||
page.locator('[data-testid="my-data-total-count"]')
|
||||
).toContainText('(20)');
|
||||
|
||||
await page
|
||||
.locator('[data-testid="my-data-widget"] [data-testid="view-all-link"]')
|
||||
.click();
|
||||
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForSelector('[data-testid="loader"]', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
// Verify entities
|
||||
await verifyEntities(
|
||||
page,
|
||||
'/api/v1/search/query?q=*&index=all&from=0&size=25*',
|
||||
TableEntities
|
||||
);
|
||||
});
|
||||
|
||||
test.skip('Verify following widget', async ({ page }) => {
|
||||
// Verify total count
|
||||
await expect(
|
||||
page.locator('[data-testid="following-data-total-count"]')
|
||||
).toContainText('(20)');
|
||||
|
||||
await page.locator('[data-testid="following-data"]').click();
|
||||
|
||||
// Verify entities
|
||||
await verifyEntities(
|
||||
page,
|
||||
'/api/v1/search/query?q=*followers:*&index=all&from=0&size=25*',
|
||||
TableEntities
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -178,7 +178,7 @@ const MyFeedWidgetInternal = ({
|
||||
dataTestId="KnowledgePanel.ActivityFeed"
|
||||
header={widgetHeader}
|
||||
loading={loading}>
|
||||
<div className="feed-widget-container">
|
||||
<div className="feed-widget-container" id="feedWidgetData">
|
||||
<div className="feed-content flex-1">
|
||||
{widgetBody}
|
||||
<WidgetFooter
|
||||
|
||||
@ -312,6 +312,11 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.top-menu {
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Tour from '../../components/AppTour/Tour';
|
||||
import { TOUR_SEARCH_TERM } from '../../constants/constants';
|
||||
@ -31,6 +31,7 @@ const TourPage = () => {
|
||||
updateTourSearch,
|
||||
} = useTourProvider();
|
||||
const { t } = useTranslation();
|
||||
const [isTourReady, setIsTourReady] = useState(false);
|
||||
|
||||
const clearSearchTerm = () => {
|
||||
updateTourSearch('');
|
||||
@ -38,6 +39,21 @@ const TourPage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
updateIsTourOpen(true);
|
||||
|
||||
let attempts = 0;
|
||||
const maxAttempts = 10;
|
||||
|
||||
const waitForElement = () => {
|
||||
const el = document.querySelector('#feedWidgetData');
|
||||
if (el) {
|
||||
setIsTourReady(true);
|
||||
} else if (attempts < maxAttempts) {
|
||||
attempts++;
|
||||
setTimeout(waitForElement, 100);
|
||||
}
|
||||
};
|
||||
|
||||
waitForElement();
|
||||
}, []);
|
||||
|
||||
const currentPageComponent = useMemo(() => {
|
||||
@ -58,15 +74,17 @@ const TourPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tour
|
||||
steps={getTourSteps({
|
||||
searchTerm: TOUR_SEARCH_TERM,
|
||||
clearSearchTerm,
|
||||
updateActiveTab,
|
||||
updateTourPage,
|
||||
})}
|
||||
/>
|
||||
{currentPageComponent}
|
||||
{isTourReady && (
|
||||
<Tour
|
||||
steps={getTourSteps({
|
||||
searchTerm: TOUR_SEARCH_TERM,
|
||||
clearSearchTerm,
|
||||
updateActiveTab,
|
||||
updateTourPage,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -17,11 +17,18 @@ import TourPage from './TourPage.component';
|
||||
|
||||
const mockUseTourProvider = {
|
||||
updateIsTourOpen: jest.fn(),
|
||||
currentTourPage: '',
|
||||
currentTourPage: CurrentTourPageType.MY_DATA_PAGE,
|
||||
updateActiveTab: jest.fn(),
|
||||
updateTourPage: jest.fn(),
|
||||
updateTourSearch: jest.fn(),
|
||||
};
|
||||
|
||||
const mockQuerySelector = jest.fn();
|
||||
Object.defineProperty(document, 'querySelector', {
|
||||
value: mockQuerySelector,
|
||||
writable: true,
|
||||
});
|
||||
|
||||
jest.mock('../../context/TourProvider/TourProvider', () => ({
|
||||
useTourProvider: jest.fn().mockImplementation(() => mockUseTourProvider),
|
||||
}));
|
||||
@ -53,6 +60,22 @@ jest.mock('../../utils/TourUtils', () => ({
|
||||
}));
|
||||
|
||||
describe('TourPage component', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
mockQuerySelector.mockImplementation((selector) => {
|
||||
if (selector === '#feedWidgetData') {
|
||||
return document.createElement('div');
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should render correctly', async () => {
|
||||
render(<TourPage />);
|
||||
|
||||
@ -69,7 +92,8 @@ describe('TourPage component', () => {
|
||||
});
|
||||
|
||||
it('MyDataPage Component should be visible, if currentTourPage is myDataPage', async () => {
|
||||
(useTourProvider as jest.Mock).mockImplementationOnce(() => ({
|
||||
(useTourProvider as jest.Mock).mockReset();
|
||||
(useTourProvider as jest.Mock).mockImplementation(() => ({
|
||||
...mockUseTourProvider,
|
||||
currentTourPage: CurrentTourPageType.MY_DATA_PAGE,
|
||||
}));
|
||||
@ -79,7 +103,8 @@ describe('TourPage component', () => {
|
||||
});
|
||||
|
||||
it('ExplorePage Component should be visible, if currentTourPage is explorePage', async () => {
|
||||
(useTourProvider as jest.Mock).mockImplementationOnce(() => ({
|
||||
(useTourProvider as jest.Mock).mockReset();
|
||||
(useTourProvider as jest.Mock).mockImplementation(() => ({
|
||||
...mockUseTourProvider,
|
||||
currentTourPage: CurrentTourPageType.EXPLORE_PAGE,
|
||||
}));
|
||||
@ -91,7 +116,8 @@ describe('TourPage component', () => {
|
||||
});
|
||||
|
||||
it('TableDetailsPage Component should be visible, if currentTourPage is datasetPage', async () => {
|
||||
(useTourProvider as jest.Mock).mockImplementationOnce(() => ({
|
||||
(useTourProvider as jest.Mock).mockReset();
|
||||
(useTourProvider as jest.Mock).mockImplementation(() => ({
|
||||
...mockUseTourProvider,
|
||||
currentTourPage: CurrentTourPageType.DATASET_PAGE,
|
||||
}));
|
||||
|
||||
@ -41,7 +41,7 @@ export const getTourSteps = ({
|
||||
/>
|
||||
</p>
|
||||
),
|
||||
selector: '#feedData',
|
||||
selector: '#feedWidgetData',
|
||||
stepInteraction: false,
|
||||
},
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user