-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8457 from marmelab/login-callback
Add login-callback hook and authProvider optional new `handleLoginCalback` method
- Loading branch information
Showing
19 changed files
with
548 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 167 additions & 0 deletions
167
packages/ra-core/src/auth/useHandleAuthCallback.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import * as React from 'react'; | ||
import expect from 'expect'; | ||
import { render, screen, waitFor } from '@testing-library/react'; | ||
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom'; | ||
import { QueryClientProvider, QueryClient } from 'react-query'; | ||
import { createMemoryHistory } from 'history'; | ||
|
||
import { useHandleAuthCallback } from './useHandleAuthCallback'; | ||
import AuthContext from './AuthContext'; | ||
import { useRedirect } from '../routing/useRedirect'; | ||
import useLogout from './useLogout'; | ||
import { AuthProvider } from '../types'; | ||
|
||
jest.mock('../routing/useRedirect'); | ||
jest.mock('./useLogout'); | ||
|
||
const redirect = jest.fn(); | ||
// @ts-ignore | ||
useRedirect.mockImplementation(() => redirect); | ||
|
||
const logout = jest.fn(); | ||
// @ts-ignore | ||
useLogout.mockImplementation(() => logout); | ||
|
||
const TestComponent = ({ customError }: { customError?: boolean }) => { | ||
const [error, setError] = React.useState<string>(); | ||
useHandleAuthCallback( | ||
customError | ||
? { | ||
onError: error => { | ||
setError((error as Error).message); | ||
}, | ||
} | ||
: undefined | ||
); | ||
return error ? <>{error}</> : null; | ||
}; | ||
|
||
const authProvider: AuthProvider = { | ||
login: () => Promise.reject('bad method'), | ||
logout: () => { | ||
return Promise.resolve(); | ||
}, | ||
checkAuth: params => (params.token ? Promise.resolve() : Promise.reject()), | ||
checkError: params => { | ||
if (params instanceof Error && params.message === 'denied') { | ||
return Promise.reject(new Error('Custom Error')); | ||
} | ||
return Promise.resolve(); | ||
}, | ||
getPermissions: () => Promise.reject('not authenticated'), | ||
handleCallback: () => Promise.resolve(), | ||
}; | ||
|
||
const queryClient = new QueryClient(); | ||
|
||
describe('useHandleAuthCallback', () => { | ||
afterEach(() => { | ||
redirect.mockClear(); | ||
}); | ||
|
||
it('should redirect to the home route by default when the callback was successfully handled', async () => { | ||
const history = createMemoryHistory({ initialEntries: ['/'] }); | ||
render( | ||
<HistoryRouter history={history}> | ||
<AuthContext.Provider value={authProvider}> | ||
<QueryClientProvider client={queryClient}> | ||
<TestComponent /> | ||
</QueryClientProvider> | ||
</AuthContext.Provider> | ||
</HistoryRouter> | ||
); | ||
await waitFor(() => { | ||
expect(redirect).toHaveBeenCalledWith('/'); | ||
}); | ||
}); | ||
|
||
it('should redirect to the provided route when the callback was successfully handled', async () => { | ||
const history = createMemoryHistory({ initialEntries: ['/'] }); | ||
render( | ||
<HistoryRouter history={history}> | ||
<AuthContext.Provider | ||
value={{ | ||
...authProvider, | ||
handleCallback() { | ||
return Promise.resolve({ redirectTo: '/test' }); | ||
}, | ||
}} | ||
> | ||
<QueryClientProvider client={queryClient}> | ||
<TestComponent /> | ||
</QueryClientProvider> | ||
</AuthContext.Provider> | ||
</HistoryRouter> | ||
); | ||
await waitFor(() => { | ||
expect(redirect).toHaveBeenCalledWith('/test'); | ||
}); | ||
}); | ||
|
||
it('should logout and not redirect to any page when the callback was not successfully handled', async () => { | ||
const history = createMemoryHistory({ initialEntries: ['/'] }); | ||
render( | ||
<HistoryRouter history={history}> | ||
<AuthContext.Provider | ||
value={{ | ||
...authProvider, | ||
handleCallback: () => Promise.reject(), | ||
}} | ||
> | ||
<QueryClientProvider client={queryClient}> | ||
<TestComponent /> | ||
</QueryClientProvider> | ||
</AuthContext.Provider> | ||
</HistoryRouter> | ||
); | ||
await waitFor(() => { | ||
expect(logout).toHaveBeenCalled(); | ||
expect(redirect).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
it('should redirect to the provided route when the callback was not successfully handled', async () => { | ||
const history = createMemoryHistory({ initialEntries: ['/'] }); | ||
render( | ||
<HistoryRouter history={history}> | ||
<AuthContext.Provider | ||
value={{ | ||
...authProvider, | ||
handleCallback: () => | ||
Promise.reject({ redirectTo: '/test' }), | ||
}} | ||
> | ||
<QueryClientProvider client={queryClient}> | ||
<TestComponent /> | ||
</QueryClientProvider> | ||
</AuthContext.Provider> | ||
</HistoryRouter> | ||
); | ||
await waitFor(() => { | ||
expect(redirect).toHaveBeenCalledWith('/test'); | ||
}); | ||
}); | ||
|
||
it('should use custom useQuery options such as onError', async () => { | ||
const history = createMemoryHistory({ initialEntries: ['/'] }); | ||
render( | ||
<HistoryRouter history={history}> | ||
<AuthContext.Provider | ||
value={{ | ||
...authProvider, | ||
handleCallback: () => | ||
Promise.reject(new Error('Custom Error')), | ||
}} | ||
> | ||
<QueryClientProvider client={queryClient}> | ||
<TestComponent customError /> | ||
</QueryClientProvider> | ||
</AuthContext.Provider> | ||
</HistoryRouter> | ||
); | ||
await waitFor(() => { | ||
screen.getByText('Custom Error'); | ||
}); | ||
expect(redirect).not.toHaveBeenCalledWith('/test'); | ||
}); | ||
}); |
Oops, something went wrong.