0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

refactor: fix test and components

This commit is contained in:
Gao Sun 2023-06-23 01:07:33 +08:00
parent c4e13ff525
commit a3e3363b10
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
8 changed files with 52 additions and 62 deletions

View file

@ -1,41 +1,17 @@
import { useLogto } from '@logto/react';
import { trySafe } from '@silverhand/essentials';
import { useContext, useEffect } from 'react';
import { useHref } from 'react-router-dom';
import AppLoading from '@/components/AppLoading';
import { TenantsContext } from '@/contexts/TenantsProvider';
function Redirect() {
const { getAccessToken, signIn } = useLogto();
const { navigateTenant, tenants, currentTenantId } = useContext(TenantsContext);
const tenant = tenants.find(({ id }) => id === currentTenantId);
const href = useHref(currentTenantId + '/callback');
const { navigateTenant, tenants, currentTenant } = useContext(TenantsContext);
useEffect(() => {
const validate = async (indicator: string) => {
// Test fetching an access token for the current Tenant ID.
// If failed, it means the user finishes the first auth, ands still needs to auth again to
// fetch the full-scoped (with all available tenants) token.
if (await trySafe(getAccessToken(indicator))) {
navigateTenant(currentTenantId);
} else {
void signIn(new URL(href, window.location.origin).toString());
}
};
if (tenant) {
void validate(tenant.indicator);
}
}, [currentTenantId, getAccessToken, href, navigateTenant, signIn, tenant]);
useEffect(() => {
if (!tenant) {
if (!currentTenant) {
/** Fallback to another available tenant instead of showing `Forbidden`. */
navigateTenant(tenants[0]?.id ?? '');
}
}, [navigateTenant, tenant, tenants]);
}, [navigateTenant, currentTenant, tenants]);
return <AppLoading />;
}

View file

@ -1,4 +1,5 @@
import { defaultTenantId, ossConsolePath } from '@logto/schemas';
import { conditionalArray } from '@silverhand/essentials';
import { CloudRoute } from '@/cloud/types';
@ -36,6 +37,9 @@ export const getUserTenantId = () => {
export const getBasename = () => (isCloud ? '/' + getUserTenantId() : ossConsolePath);
export const getCallbackUrl = (tenantId?: string) =>
new URL('/' + conditionalArray(tenantId, 'callback').join('/'), window.location.origin);
export const getSignOutRedirectPathname = () => (isCloud ? '/' : ossConsolePath);
export const maxFreeTenantNumbers = 3;

View file

@ -25,6 +25,7 @@ export default function TenantSelector() {
appendTenant,
currentTenant: currentTenantInfo,
currentTenantId,
navigateTenant,
} = useContext(TenantsContext);
const isCreateButtonDisabled = useMemo(
@ -75,7 +76,8 @@ export default function TenantSelector() {
key={id}
className={styles.dropdownItem}
onClick={() => {
window.open(new URL(`/${id}`, window.location.origin).toString(), '_self');
navigateTenant(id);
setShowDropdown(false);
}}
>
<div className={styles.dropdownName}>{name}</div>
@ -110,7 +112,7 @@ export default function TenantSelector() {
onClose={async (tenant?: TenantInfo) => {
if (tenant) {
appendTenant(tenant);
window.location.assign(new URL(`/${tenant.id}`, window.location.origin).toString());
navigateTenant(tenant.id);
}
setShowCreateTenantModal(false);
}}

View file

@ -1,16 +1,38 @@
import { useContext } from 'react';
import { useLogto } from '@logto/react';
import { type TenantInfo } from '@logto/schemas/lib/models/tenants.js';
import { trySafe } from '@silverhand/essentials';
import { useContext, useEffect } from 'react';
import AppLoading from '@/components/AppLoading';
import { getCallbackUrl } from '@/consts';
import { isCloud } from '@/consts/env';
import { AppEndpointsContext } from '@/contexts/AppEndpointsProvider';
import { TenantsContext } from '@/contexts/TenantsProvider';
import useTrackUserId from '@/hooks/use-track-user-id';
import OnboardingApp from '@/onboarding/App';
import useUserOnboardingData from '@/onboarding/hooks/use-user-onboarding-data';
import ConsoleApp from '@/pages/Main';
function TenantAppContainer() {
const { getAccessToken, signIn } = useLogto();
const { userEndpoint } = useContext(AppEndpointsContext);
const { isOnboarding, isLoaded } = useUserOnboardingData();
const { currentTenant } = useContext(TenantsContext);
useEffect(() => {
const validate = async ({ indicator, id }: TenantInfo) => {
// Test fetching an access token for the current Tenant ID.
// If failed, it means the user finishes the first auth, ands still needs to auth again to
// fetch the full-scoped (with all available tenants) token.
if (!(await trySafe(getAccessToken(indicator)))) {
void signIn(getCallbackUrl(id).href);
}
};
if (currentTenant) {
void validate(currentTenant);
}
}, [currentTenant, getAccessToken, signIn]);
useTrackUserId();

View file

@ -76,19 +76,10 @@ function TenantsProvider({ children }: Props) {
const [isInitComplete, setIsInitComplete] = useState(!isCloud);
const [currentTenantId, setCurrentTenantId] = useState(getUserTenantId());
const navigateTenant = useCallback((tenantId: string, options?: NavigateOptions) => {
const params = [
options?.state ?? {},
'',
new URL(`/${tenantId}`, window.location.origin).toString(),
] satisfies Parameters<typeof window.history.pushState>;
if (options?.replace) {
window.history.replaceState(...params);
return;
}
window.history.pushState(...params);
const navigateTenant = useCallback((tenantId: string) => {
// Use `window.open()` to force page reload since we use `basename` for the router
// which will not re-create the router instance when the URL changes.
window.open(`/${tenantId}`, '_self');
setCurrentTenantId(tenantId);
}, []);

View file

@ -22,19 +22,28 @@ import HandleSocialCallback from '../Profile/containers/HandleSocialCallback';
function Main() {
const swrOptions = useSwrOptions();
const router = useMemo(
() =>
createBrowserRouter(
createRoutesFromElements(
<>
<Route path="/*">
<Route path="callback" element={<Callback />} />
<Route path="welcome" element={<Welcome />} />
<Route path="handle-social" element={<HandleSocialCallback />} />
<Route element={<AppContent />}>
<Route path="/*" element={<ConsoleContent />} />
<Route path="*" element={<ConsoleContent />} />
</Route>
</Route>
</>
),
// Currently we use `window.open()` to navigate between tenants so the `useMemo` hook
// can have no dependency and the router will be created anyway. Consider integrating the
// tenant ID into the router and remove basename here if we want to use `history.pushState()`
// to navigate.
//
// Caveat: To use `history.pushState()`, we'd better to create a browser router in the upper
// level of the component tree to make the tenant ID a part of the URL. Otherwise, we need
// to handle `popstate` event to update the tenant ID when the user navigates back.
{ basename: getBasename() }
),
[]

View file

@ -101,20 +101,6 @@ function TenantBasicSettings() {
}
};
useEffect(() => {
/**
* Redirect to the first tenant if the current tenant is deleted;
* Redirect to Cloud console landing page if there is no tenant.
*/
if (!tenants.some(({ id }) => id === currentTenantId)) {
window.location.assign(
tenants[0]?.id
? new URL(`/${tenants[0]?.id}`, window.location.origin).toString()
: new URL(window.location.origin).toString()
);
}
}, [currentTenantId, tenants]);
if (error) {
return <AppError errorMessage={error.message} callStack={error.stack} />;
}

View file

@ -75,7 +75,7 @@ describe('smoke testing for cloud', () => {
await expect(page).toClick('div[role=button][class$=item]');
// Click the next button
await expect(page).toClick('div[class$=actions] button:first-child');
await expect(page).toClick('div[class$=actions] button', { text: 'Next' });
// Wait for the next page to load
await expect(page).toMatchElement('div[class$=config] div[class$=title]', {
@ -127,7 +127,7 @@ describe('smoke testing for cloud', () => {
await page.waitForTimeout(500);
const createTenantButton = await page.waitForSelector(
'div[class$=ReactModalPortal] div[class$=dropdownContainer] > div[class$=dropdown] > div[class$=createTenantButton][role=button]:has(div)'
'div[class$=ReactModalPortal] div[class$=dropdownContainer] > div[class$=dropdown] > button[class$=createTenantButton]:has(div)'
);
await expect(createTenantButton).toMatchElement('div', { text: 'Create tenant' });
await createTenantButton.click();