mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-05-31 14:11:58 +05:30
Cover oauth with e2e tests and fix some old and newly introduced bugs
This commit is contained in:
@@ -5,15 +5,16 @@ import {
|
||||
requestToken,
|
||||
logout,
|
||||
} from 'app/services/api/authentication';
|
||||
import { relogin as navigateToLogin } from 'app/components/auth/actions';
|
||||
import {
|
||||
relogin as navigateToLogin,
|
||||
setAccountSwitcher,
|
||||
} from 'app/components/auth/actions';
|
||||
import { updateUser, setGuest } from 'app/components/user/actions';
|
||||
import { setLocale } from 'app/components/i18n/actions';
|
||||
import { setAccountSwitcher } from 'app/components/auth/actions';
|
||||
import { getActiveAccount } from 'app/components/accounts/reducer';
|
||||
import logger from 'app/services/logger';
|
||||
import { ThunkAction } from 'app/reducers';
|
||||
|
||||
import { Account } from './reducer';
|
||||
import { getActiveAccount, Account } from './reducer';
|
||||
import {
|
||||
add,
|
||||
remove,
|
||||
|
@@ -296,7 +296,10 @@ class PanelTransition extends React.PureComponent<Props, State> {
|
||||
</MeasureHeight>
|
||||
</div>
|
||||
</Panel>
|
||||
<div className={helpLinksStyles}>
|
||||
<div
|
||||
className={helpLinksStyles}
|
||||
data-testid="auth-secondary-controls"
|
||||
>
|
||||
{panels.map(config => this.getLinks(config))}
|
||||
</div>
|
||||
</Form>
|
||||
|
@@ -20,6 +20,7 @@ import signup from 'app/services/api/signup';
|
||||
import dispatchBsod from 'app/components/ui/bsod/dispatchBsod';
|
||||
import { create as createPopup } from 'app/components/ui/popup/actions';
|
||||
import ContactForm from 'app/components/contact/ContactForm';
|
||||
import { Account } from 'app/components/accounts/reducer';
|
||||
import { ThunkAction, Dispatch } from 'app/reducers';
|
||||
|
||||
import { getCredentials } from './reducer';
|
||||
@@ -31,17 +32,8 @@ type ValidationError =
|
||||
payload: { [key: string]: any };
|
||||
};
|
||||
|
||||
export { updateUser } from 'app/components/user/actions';
|
||||
export {
|
||||
authenticate,
|
||||
logoutAll as logout,
|
||||
remove as removeAccount,
|
||||
activate as activateAccount,
|
||||
} from 'app/components/accounts/actions';
|
||||
import { Account } from 'app/components/accounts/reducer';
|
||||
|
||||
/**
|
||||
* Reoutes user to the previous page if it is possible
|
||||
* Routes user to the previous page if it is possible
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {string} options.fallbackUrl - an url to route user to if goBack is not possible
|
||||
@@ -427,7 +419,7 @@ export function oAuthComplete(params: { accept?: boolean } = {}) {
|
||||
localStorage.removeItem('oauthData');
|
||||
|
||||
if (resp.redirectUri.startsWith('static_page')) {
|
||||
const displayCode = resp.redirectUri === 'static_page_with_code';
|
||||
const displayCode = /static_page_with_code/.test(resp.redirectUri);
|
||||
|
||||
const [, code] = resp.redirectUri.match(/code=(.+)&/) || [];
|
||||
[, resp.redirectUri] = resp.redirectUri.match(/^(.+)\?/) || [];
|
||||
@@ -516,7 +508,7 @@ export function resetAuth(): ThunkAction {
|
||||
}
|
||||
|
||||
export const SET_OAUTH = 'set_oauth';
|
||||
export function setOAuthRequest(oauth: {
|
||||
export function setOAuthRequest(data: {
|
||||
client_id?: string;
|
||||
redirect_uri?: string;
|
||||
response_type?: string;
|
||||
@@ -528,19 +520,19 @@ export function setOAuthRequest(oauth: {
|
||||
return {
|
||||
type: SET_OAUTH,
|
||||
payload: {
|
||||
clientId: oauth.client_id,
|
||||
redirectUrl: oauth.redirect_uri,
|
||||
responseType: oauth.response_type,
|
||||
scope: oauth.scope,
|
||||
prompt: oauth.prompt,
|
||||
loginHint: oauth.loginHint,
|
||||
state: oauth.state,
|
||||
clientId: data.client_id,
|
||||
redirectUrl: data.redirect_uri,
|
||||
responseType: data.response_type,
|
||||
scope: data.scope,
|
||||
prompt: data.prompt,
|
||||
loginHint: data.loginHint,
|
||||
state: data.state,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const SET_OAUTH_RESULT = 'set_oauth_result';
|
||||
export function setOAuthCode(oauth: {
|
||||
export function setOAuthCode(data: {
|
||||
success: boolean;
|
||||
code: string;
|
||||
displayCode: boolean;
|
||||
@@ -548,9 +540,9 @@ export function setOAuthCode(oauth: {
|
||||
return {
|
||||
type: SET_OAUTH_RESULT,
|
||||
payload: {
|
||||
success: oauth.success,
|
||||
code: oauth.code,
|
||||
displayCode: oauth.displayCode,
|
||||
success: data.success,
|
||||
code: data.code,
|
||||
displayCode: data.displayCode,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -564,7 +556,7 @@ export function requirePermissionsAccept() {
|
||||
|
||||
export const SET_SCOPES = 'set_scopes';
|
||||
export function setScopes(scopes: Scope[]) {
|
||||
if (!(scopes instanceof Array)) {
|
||||
if (!Array.isArray(scopes)) {
|
||||
throw new Error('Scopes must be array');
|
||||
}
|
||||
|
||||
@@ -610,11 +602,11 @@ function needActivation() {
|
||||
}
|
||||
|
||||
function authHandler(dispatch: Dispatch) {
|
||||
return (resp: OAuthResponse): Promise<Account> =>
|
||||
return (oAuthResp: OAuthResponse): Promise<Account> =>
|
||||
dispatch(
|
||||
authenticate({
|
||||
token: resp.access_token,
|
||||
refreshToken: resp.refresh_token || null,
|
||||
token: oAuthResp.access_token,
|
||||
refreshToken: oAuthResp.refresh_token || null,
|
||||
}),
|
||||
).then(resp => {
|
||||
dispatch(setLogin(null));
|
||||
@@ -626,7 +618,7 @@ function authHandler(dispatch: Dispatch) {
|
||||
function validationErrorsHandler(dispatch: Dispatch, repeatUrl?: string) {
|
||||
return resp => {
|
||||
if (resp.errors) {
|
||||
const firstError = Object.keys(resp.errors)[0];
|
||||
const [firstError] = Object.keys(resp.errors);
|
||||
const error = {
|
||||
type: resp.errors[firstError],
|
||||
payload: {
|
||||
|
@@ -44,7 +44,7 @@ class Finish extends React.Component<Props> {
|
||||
/>
|
||||
</div>
|
||||
{displayCode ? (
|
||||
<div>
|
||||
<div data-testid="oauth-code-container">
|
||||
<div className={styles.description}>
|
||||
<Message {...messages.passCodeToApp} values={{ appName }} />
|
||||
</div>
|
||||
|
@@ -42,7 +42,7 @@ export function Panel(props: {
|
||||
|
||||
export function PanelHeader(props: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className={styles.header} {...props}>
|
||||
<div className={styles.header} {...props} data-testid="auth-header">
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
@@ -50,7 +50,7 @@ export function PanelHeader(props: { children: React.ReactNode }) {
|
||||
|
||||
export function PanelBody(props: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className={styles.body} {...props}>
|
||||
<div className={styles.body} {...props} data-testid="auth-body">
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
@@ -58,7 +58,7 @@ export function PanelBody(props: { children: React.ReactNode }) {
|
||||
|
||||
export function PanelFooter(props: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className={styles.footer} {...props}>
|
||||
<div className={styles.footer} {...props} data-testid="auth-controls">
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import { IntlProvider } from 'app/components/i18n';
|
||||
import logger from 'app/services/logger';
|
||||
import appInfo from 'app/components/auth/appInfo/AppInfo.intl.json';
|
||||
|
||||
@@ -42,32 +41,30 @@ class BSoD extends React.Component<{}, State> {
|
||||
}
|
||||
|
||||
return (
|
||||
<IntlProvider>
|
||||
<div className={styles.body}>
|
||||
<canvas
|
||||
className={styles.canvas}
|
||||
ref={(el: HTMLCanvasElement | null) => el && new BoxesField(el)}
|
||||
/>
|
||||
<div className={styles.body}>
|
||||
<canvas
|
||||
className={styles.canvas}
|
||||
ref={(el: HTMLCanvasElement | null) => el && new BoxesField(el)}
|
||||
/>
|
||||
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.title}>
|
||||
<Message {...appInfo.appName} />
|
||||
</div>
|
||||
<div className={styles.lineWithMargin}>
|
||||
<Message {...messages.criticalErrorHappened} />
|
||||
</div>
|
||||
<div className={styles.line}>
|
||||
<Message {...messages.reloadPageOrContactUs} />
|
||||
</div>
|
||||
<a href={emailUrl} className={styles.support}>
|
||||
support@ely.by
|
||||
</a>
|
||||
<div className={styles.easterEgg}>
|
||||
<Message {...messages.alsoYouCanInteractWithBackground} />
|
||||
</div>
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.title}>
|
||||
<Message {...appInfo.appName} />
|
||||
</div>
|
||||
<div className={styles.lineWithMargin}>
|
||||
<Message {...messages.criticalErrorHappened} />
|
||||
</div>
|
||||
<div className={styles.line}>
|
||||
<Message {...messages.reloadPageOrContactUs} />
|
||||
</div>
|
||||
<a href={emailUrl} className={styles.support}>
|
||||
support@ely.by
|
||||
</a>
|
||||
<div className={styles.easterEgg}>
|
||||
<Message {...messages.alsoYouCanInteractWithBackground} />
|
||||
</div>
|
||||
</div>
|
||||
</IntlProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { bsod } from './actions';
|
||||
import BSoD from 'app/components/ui/bsod/BSoD';
|
||||
|
||||
let injectedStore;
|
||||
let onBsod;
|
||||
|
||||
export default function dispatchBsod(store = injectedStore) {
|
||||
store.dispatch(bsod());
|
||||
onBsod && onBsod();
|
||||
|
||||
ReactDOM.render(<BSoD />, document.getElementById('app'));
|
||||
}
|
||||
|
||||
export function inject(store, stopLoading) {
|
||||
injectedStore = store;
|
||||
onBsod = stopLoading;
|
||||
}
|
41
packages/app/components/ui/bsod/dispatchBsod.tsx
Normal file
41
packages/app/components/ui/bsod/dispatchBsod.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { ContextProvider } from 'app/shell';
|
||||
import { Store } from 'app/reducers';
|
||||
import { History } from 'history';
|
||||
|
||||
import { bsod } from './actions';
|
||||
import BSoD from './BSoD';
|
||||
|
||||
let injectedStore: Store;
|
||||
let injectedHistory: History<any>;
|
||||
let onBsod: undefined | (() => void);
|
||||
|
||||
export default function dispatchBsod(
|
||||
store = injectedStore,
|
||||
history = injectedHistory,
|
||||
) {
|
||||
store.dispatch(bsod());
|
||||
onBsod && onBsod();
|
||||
|
||||
ReactDOM.render(
|
||||
<ContextProvider store={store} history={history}>
|
||||
<BSoD />
|
||||
</ContextProvider>,
|
||||
document.getElementById('app'),
|
||||
);
|
||||
}
|
||||
|
||||
export function inject({
|
||||
store,
|
||||
history,
|
||||
stopLoading,
|
||||
}: {
|
||||
store: Store;
|
||||
history: History<any>;
|
||||
stopLoading: () => void;
|
||||
}) {
|
||||
injectedStore = store;
|
||||
injectedHistory = history;
|
||||
onBsod = stopLoading;
|
||||
}
|
@@ -1,11 +1,21 @@
|
||||
import request from 'app/services/request';
|
||||
import logger from 'app/services/logger';
|
||||
import { Store } from 'app/reducers';
|
||||
import { History } from 'history';
|
||||
|
||||
import dispatchBsod, { inject } from './dispatchBsod';
|
||||
import BsodMiddleware from './BsodMiddleware';
|
||||
|
||||
export default function factory(store, stopLoading) {
|
||||
inject(store, stopLoading);
|
||||
export default function factory({
|
||||
store,
|
||||
history,
|
||||
stopLoading,
|
||||
}: {
|
||||
store: Store;
|
||||
history: History<any>;
|
||||
stopLoading: () => void;
|
||||
}) {
|
||||
inject({ store, history, stopLoading });
|
||||
|
||||
// do bsod for 500/404 errors
|
||||
request.addMiddleware(new BsodMiddleware(dispatchBsod, logger));
|
Reference in New Issue
Block a user