fix(ui): tour page issue clicking on tour from welcome screen (#20038)

* fix(ui): tour page issue clicking on tour from welcome screen

* fix infinite login for unauthenticated router

* improve tour tests
This commit is contained in:
Chirag Madlani 2025-03-02 13:00:33 +05:30 committed by GitHub
parent 1cbbe9a7c0
commit 9331a526ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 117 additions and 112 deletions

View File

@ -10,13 +10,102 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { expect, test } from '@playwright/test'; import { expect, Page, test } from '@playwright/test';
import { UserClass } from '../../support/user/UserClass'; import { UserClass } from '../../support/user/UserClass';
import { performAdminLogin } from '../../utils/admin'; import { performAdminLogin } from '../../utils/admin';
import { redirectToHomePage } from '../../utils/common'; import { redirectToHomePage } from '../../utils/common';
const user = new UserClass(); const user = new UserClass();
const validateTourSteps = async (page: Page) => {
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('1');
// step 1
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('2');
// step 2
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('3');
await page.getByTestId('searchBox').fill('dim_a');
await page.getByTestId('searchBox').press('Enter');
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('4');
// step 3
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('5');
await expect(
page.getByTestId('sample_data.ecommerce_db.shopify.dim_address')
).toBeVisible();
// step 4
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('6');
// step 5
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('7');
// step 6
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('8');
// step 7
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('9');
// step 8
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('10');
await expect(
page.getByTestId('sample_data').getByText('Sample Data')
).toBeVisible();
// step 9
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('11');
// step 10
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('12');
await expect(page.getByText('Data Observability')).toBeVisible();
// step 11
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('13');
// step 12
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('14');
await expect(page.getByTestId('lineage').getByText('Lineage')).toBeVisible();
// step 13
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('15');
await page.getByTestId('last-step-button').click();
await page.getByTestId('saveButton').click();
};
test.describe('Tour should work properly', () => { test.describe('Tour should work properly', () => {
test.beforeAll(async ({ browser }) => { test.beforeAll(async ({ browser }) => {
const { apiContext, afterAction } = await performAdminLogin(browser); const { apiContext, afterAction } = await performAdminLogin(browser);
@ -35,98 +124,23 @@ test.describe('Tour should work properly', () => {
await redirectToHomePage(page); await redirectToHomePage(page);
}); });
test('All tour steps should work', async ({ page }) => { test('Tour should work from help section', async ({ page }) => {
await page.locator('[data-testid="help-icon"]').click(); await page.locator('[data-testid="help-icon"]').click();
await page.getByRole('link', { name: 'Tour', exact: true }).click(); await page.getByRole('link', { name: 'Tour', exact: true }).click();
await page.waitForURL('**/tour'); await page.waitForURL('**/tour');
await validateTourSteps(page);
});
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('1'); test('Tour should work from welcome screen', async ({ page }) => {
await page.getByText('Take a product tour to get started!').click();
await page.waitForURL('**/tour');
// step 1 await validateTourSteps(page);
await page.locator('[data-tour-elem="right-arrow"]').click(); });
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('2'); test('Tour should work from URL directly', async ({ page }) => {
await page.goto('/tour');
// step 2 await page.waitForURL('**/tour');
await page.locator('[data-tour-elem="right-arrow"]').click(); await validateTourSteps(page);
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('3');
await page.getByTestId('searchBox').fill('dim_a');
await page.getByTestId('searchBox').press('Enter');
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('4');
// step 3
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('5');
await expect(
page.getByTestId('sample_data.ecommerce_db.shopify.dim_address')
).toBeVisible();
// step 4
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('6');
// step 5
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('7');
// step 6
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('8');
// step 7
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('9');
// step 8
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('10');
await expect(
page.getByTestId('sample_data').getByText('Sample Data')
).toBeVisible();
// step 9
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('11');
// step 10
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('12');
await expect(page.getByText('Data Observability')).toBeVisible();
// step 11
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('13');
// step 12
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('14');
await expect(
page.getByTestId('lineage').getByText('Lineage')
).toBeVisible();
// step 13
await page.locator('[data-tour-elem="right-arrow"]').click();
await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('15');
await page.getByTestId('last-step-button').click();
await page.getByTestId('saveButton').click();
}); });
}); });

View File

@ -14,5 +14,4 @@ import { App } from '../../../../generated/entity/applications/app';
export type ApplicationsContextType = { export type ApplicationsContextType = {
applications: App[]; applications: App[];
loading: boolean;
}; };

View File

@ -24,13 +24,14 @@ import { usePermissionProvider } from '../../../../context/PermissionProvider/Pe
import { App } from '../../../../generated/entity/applications/app'; import { App } from '../../../../generated/entity/applications/app';
import { useApplicationStore } from '../../../../hooks/useApplicationStore'; import { useApplicationStore } from '../../../../hooks/useApplicationStore';
import { getApplicationList } from '../../../../rest/applicationAPI'; import { getApplicationList } from '../../../../rest/applicationAPI';
import Loader from '../../../common/Loader/Loader';
import { ApplicationsContextType } from './ApplicationsProvider.interface'; import { ApplicationsContextType } from './ApplicationsProvider.interface';
export const ApplicationsContext = createContext({} as ApplicationsContextType); export const ApplicationsContext = createContext({} as ApplicationsContextType);
export const ApplicationsProvider = ({ children }: { children: ReactNode }) => { export const ApplicationsProvider = ({ children }: { children: ReactNode }) => {
const [applications, setApplications] = useState<App[]>([]); const [applications, setApplications] = useState<App[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(true);
const { permissions } = usePermissionProvider(); const { permissions } = usePermissionProvider();
const { setApplicationsName } = useApplicationStore(); const { setApplicationsName } = useApplicationStore();
@ -54,16 +55,18 @@ export const ApplicationsProvider = ({ children }: { children: ReactNode }) => {
useEffect(() => { useEffect(() => {
if (!isEmpty(permissions)) { if (!isEmpty(permissions)) {
fetchApplicationList(); fetchApplicationList();
} else {
setLoading(false);
} }
}, [permissions]); }, [permissions]);
const appContext = useMemo(() => { const appContext = useMemo(() => {
return { applications, loading }; return { applications };
}, [applications, loading]); }, [applications]);
return ( return (
<ApplicationsContext.Provider value={appContext}> <ApplicationsContext.Provider value={appContext}>
{children} {loading ? <Loader /> : children}
</ApplicationsContext.Provider> </ApplicationsContext.Provider>
); );
}; };

View File

@ -98,7 +98,7 @@ const PermissionProvider: FC<PermissionProviderProps> = ({ children }) => {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [setLoading, setPermissions, redirectToStoredPath]); }, [redirectToStoredPath]);
const fetchEntityPermission = useCallback( const fetchEntityPermission = useCallback(
async (resource: ResourceEntity, entityId: string) => { async (resource: ResourceEntity, entityId: string) => {

View File

@ -19,7 +19,6 @@ import { useHistory } from 'react-router-dom';
import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder';
import PageHeader from '../../components/PageHeader/PageHeader.component'; import PageHeader from '../../components/PageHeader/PageHeader.component';
import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1'; import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
import { useApplicationsProvider } from '../../components/Settings/Applications/ApplicationsProvider/ApplicationsProvider';
import SettingItemCard from '../../components/Settings/SettingItemCard/SettingItemCard.component'; import SettingItemCard from '../../components/Settings/SettingItemCard/SettingItemCard.component';
import { PAGE_HEADERS } from '../../constants/PageHeaders.constant'; import { PAGE_HEADERS } from '../../constants/PageHeaders.constant';
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider'; import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
@ -39,7 +38,6 @@ const GlobalSettingPage = () => {
const { permissions } = usePermissionProvider(); const { permissions } = usePermissionProvider();
const { isAdminUser } = useAuth(); const { isAdminUser } = useAuth();
const { loading } = useApplicationsProvider();
const settingItems = useMemo( const settingItems = useMemo(
() => () =>
@ -58,7 +56,7 @@ const GlobalSettingPage = () => {
return false; return false;
}), }),
[permissions, isAdminUser, loading] [permissions, isAdminUser]
); );
const handleSettingItemClick = useCallback((category: string) => { const handleSettingItemClick = useCallback((category: string) => {

View File

@ -13,20 +13,13 @@
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import React, { import React, { useCallback, useEffect, useMemo, useState } from 'react';
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import RGL, { WidthProvider } from 'react-grid-layout'; import RGL, { WidthProvider } from 'react-grid-layout';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed';
import Loader from '../../components/common/Loader/Loader'; import Loader from '../../components/common/Loader/Loader';
import WelcomeScreen from '../../components/MyData/WelcomeScreen/WelcomeScreen.component'; import WelcomeScreen from '../../components/MyData/WelcomeScreen/WelcomeScreen.component';
import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1'; import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
import { useApplicationsProvider } from '../../components/Settings/Applications/ApplicationsProvider/ApplicationsProvider';
import { import {
KNOWLEDGE_LIST_LENGTH, KNOWLEDGE_LIST_LENGTH,
LOGGED_IN_USER_STORAGE_KEY, LOGGED_IN_USER_STORAGE_KEY,
@ -54,14 +47,13 @@ const ReactGridLayout = WidthProvider(RGL);
const MyDataPage = () => { const MyDataPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentUser, selectedPersona } = useApplicationStore(); const { currentUser, selectedPersona } = useApplicationStore();
const { loading: applicationsLoading } = useApplicationsProvider();
const { isWelcomeVisible } = useWelcomeStore(); const { isWelcomeVisible } = useWelcomeStore();
const [followedData, setFollowedData] = useState<Array<EntityReference>>([]); const [followedData, setFollowedData] = useState<Array<EntityReference>>([]);
const [followedDataCount, setFollowedDataCount] = useState(0); const [followedDataCount, setFollowedDataCount] = useState(0);
const [isLoadingOwnedData, setIsLoadingOwnedData] = useState<boolean>(false); const [isLoadingOwnedData, setIsLoadingOwnedData] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [layout, setLayout] = useState<Array<WidgetConfig>>([]); const [layout, setLayout] = useState<Array<WidgetConfig>>([]);
const isMounted = useRef(false);
const [showWelcomeScreen, setShowWelcomeScreen] = useState(false); const [showWelcomeScreen, setShowWelcomeScreen] = useState(false);
const [isAnnouncementLoading, setIsAnnouncementLoading] = const [isAnnouncementLoading, setIsAnnouncementLoading] =
useState<boolean>(true); useState<boolean>(true);
@ -116,15 +108,14 @@ const MyDataPage = () => {
}; };
useEffect(() => { useEffect(() => {
!applicationsLoading && fetchDocument(); fetchDocument();
}, [selectedPersona, applicationsLoading]); }, [selectedPersona]);
useEffect(() => { useEffect(() => {
isMounted.current = true;
updateWelcomeScreen(!usernameExistsInCookie && isWelcomeVisible); updateWelcomeScreen(!usernameExistsInCookie && isWelcomeVisible);
return () => updateWelcomeScreen(false); return () => updateWelcomeScreen(false);
}, [isWelcomeVisible]); }, []);
const fetchUserFollowedData = async () => { const fetchUserFollowedData = async () => {
if (!currentUser?.id) { if (!currentUser?.id) {
@ -205,7 +196,7 @@ const MyDataPage = () => {
// call the hook to set the direction of the grid layout // call the hook to set the direction of the grid layout
useGridLayoutDirection(isLoading); useGridLayoutDirection(isLoading);
if (isLoading || applicationsLoading) { if (isLoading) {
return <Loader fullScreen />; return <Loader fullScreen />;
} }