mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-06 04:25:20 +00:00
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:
parent
1cbbe9a7c0
commit
9331a526ea
@ -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();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -14,5 +14,4 @@ import { App } from '../../../../generated/entity/applications/app';
|
|||||||
|
|
||||||
export type ApplicationsContextType = {
|
export type ApplicationsContextType = {
|
||||||
applications: App[];
|
applications: App[];
|
||||||
loading: boolean;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
@ -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 />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user