From b633e2f4700dec0dce3ce458d10aef61b4fd50fe Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Mon, 13 Jul 2020 16:53:48 +0200 Subject: [PATCH 01/23] Create mutation hooks for auth provider --- src/auth/AuthProvider.tsx | 38 +++++++++++++++---------------------- src/auth/mutations.ts | 38 ++++++++++++++++++------------------- src/auth/types/TokenAuth.ts | 2 +- 3 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index 8905a0fcb..0056dbc35 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -13,9 +13,9 @@ import { useIntl } from "react-intl"; import { UserContext } from "./"; import { - TokenRefreshMutation, - TypedTokenAuthMutation, - TypedVerifyTokenMutation + useTokenAuthMutation, + useTokenRefreshMutation, + useTokenVerifyMutation } from "./mutations"; import { RefreshToken, RefreshTokenVariables } from "./types/RefreshToken"; import { TokenAuth, TokenAuthVariables } from "./types/TokenAuth"; @@ -42,6 +42,10 @@ const AuthProviderOperations: React.FC = ({ const intl = useIntl(); const notify = useNotifier(); + const tokenAuth = useTokenAuthMutation({}); + const tokenRefresh = useTokenRefreshMutation({}); + const tokenVerify = useTokenVerifyMutation({}); + const handleLogin = () => { if (DEMO_MODE) { displayDemoMessage(intl, notify); @@ -49,26 +53,14 @@ const AuthProviderOperations: React.FC = ({ }; return ( - - {(...tokenAuth) => ( - - {(...tokenVerify) => ( - - {(...tokenRefresh) => ( - - {children} - - )} - - )} - - )} - + + {children} + ); }; diff --git a/src/auth/mutations.ts b/src/auth/mutations.ts index 41778f090..5b42493ea 100644 --- a/src/auth/mutations.ts +++ b/src/auth/mutations.ts @@ -1,5 +1,6 @@ import { fragmentUser } from "@saleor/fragments/auth"; import { accountErrorFragment } from "@saleor/fragments/errors"; +import makeMutation from "@saleor/hooks/makeMutation"; import gql from "graphql-tag"; import { TypedMutation } from "../mutations"; @@ -17,7 +18,7 @@ export const tokenAuthMutation = gql` mutation TokenAuth($email: String!, $password: String!) { tokenCreate(email: $email, password: $password) { token - errors { + errors: accountErrors { field message } @@ -27,11 +28,9 @@ export const tokenAuthMutation = gql` } } `; - -export const TypedTokenAuthMutation = TypedMutation< - TokenAuth, - TokenAuthVariables ->(tokenAuthMutation); +export const useTokenAuthMutation = makeMutation( + tokenAuthMutation +); export const tokenVerifyMutation = gql` ${fragmentUser} @@ -44,12 +43,23 @@ export const tokenVerifyMutation = gql` } } `; - -export const TypedVerifyTokenMutation = TypedMutation< +export const useTokenVerifyMutation = makeMutation< VerifyToken, VerifyTokenVariables >(tokenVerifyMutation); +const refreshToken = gql` + mutation RefreshToken($token: String!) { + tokenRefresh(csrfToken: $token) { + token + } + } +`; +export const useTokenRefreshMutation = makeMutation< + RefreshToken, + RefreshTokenVariables +>(refreshToken); + export const requestPasswordReset = gql` ${accountErrorFragment} mutation RequestPasswordReset($email: String!, $redirectUrl: String!) { @@ -84,15 +94,3 @@ export const SetPasswordMutation = TypedMutation< SetPassword, SetPasswordVariables >(setPassword); - -const refreshToken = gql` - mutation RefreshToken($token: String!) { - tokenRefresh(csrfToken: $token) { - token - } - } -`; -export const TokenRefreshMutation = TypedMutation< - RefreshToken, - RefreshTokenVariables ->(refreshToken); diff --git a/src/auth/types/TokenAuth.ts b/src/auth/types/TokenAuth.ts index 8d72009a9..7df055cfc 100644 --- a/src/auth/types/TokenAuth.ts +++ b/src/auth/types/TokenAuth.ts @@ -9,7 +9,7 @@ import { PermissionEnum } from "./../../types/globalTypes"; // ==================================================== export interface TokenAuth_tokenCreate_errors { - __typename: "Error"; + __typename: "AccountError"; field: string | null; message: string | null; } From 03a2fff2a5acfe03b22687398664dd691af1962e Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Mon, 13 Jul 2020 17:44:02 +0200 Subject: [PATCH 02/23] Refactor AuthProvider --- src/auth/AuthProvider.tsx | 209 ++++++++++++-------------------------- 1 file changed, 66 insertions(+), 143 deletions(-) diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index 0056dbc35..5db93208c 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -7,8 +7,7 @@ import { login as loginWithCredentialsManagementAPI, saveCredentials } from "@saleor/utils/credentialsManagement"; -import React from "react"; -import { MutationFunction, MutationResult } from "react-apollo"; +import React, { useContext, useEffect, useState } from "react"; import { useIntl } from "react-intl"; import { UserContext } from "./"; @@ -17,9 +16,6 @@ import { useTokenRefreshMutation, useTokenVerifyMutation } from "./mutations"; -import { RefreshToken, RefreshTokenVariables } from "./types/RefreshToken"; -import { TokenAuth, TokenAuthVariables } from "./types/TokenAuth"; -import { VerifyToken, VerifyTokenVariables } from "./types/VerifyToken"; import { displayDemoMessage, getAuthToken, @@ -27,126 +23,61 @@ import { setAuthToken } from "./utils"; -interface AuthProviderOperationsProps { - children: (props: { - hasToken: boolean; - isAuthenticated: boolean; - tokenAuthLoading: boolean; - tokenVerifyLoading: boolean; - user: User; - }) => React.ReactNode; +interface AuthProviderProps { + children: React.ReactNode; } -const AuthProviderOperations: React.FC = ({ - children -}) => { +const AuthProvider: React.FC = ({ children }) => { const intl = useIntl(); const notify = useNotifier(); - const tokenAuth = useTokenAuthMutation({}); - const tokenRefresh = useTokenRefreshMutation({}); - const tokenVerify = useTokenVerifyMutation({}); + const [tokenAuth, tokenAuthOpts] = useTokenAuthMutation({}); + const [tokenRefresh] = useTokenRefreshMutation({}); + const [tokenVerify, tokenVerifyOpts] = useTokenVerifyMutation({}); - const handleLogin = () => { + const [userContext, setUserContext] = useState(undefined); + const [persistToken] = useState(false); + + const onLogin = () => { if (DEMO_MODE) { displayDemoMessage(intl, notify); } }; - return ( - - {children} - - ); -}; - -interface AuthProviderProps { - children: (props: { - hasToken: boolean; - isAuthenticated: boolean; - tokenAuthLoading: boolean; - tokenVerifyLoading: boolean; - user: User; - }) => React.ReactNode; - tokenAuth: [ - MutationFunction, - MutationResult - ]; - tokenVerify: [ - MutationFunction, - MutationResult - ]; - tokenRefresh: [ - MutationFunction, - MutationResult - ]; - onLogin?: () => void; -} - -interface AuthProviderState { - user: User; - persistToken: boolean; -} - -class AuthProvider extends React.Component< - AuthProviderProps, - AuthProviderState -> { - constructor(props) { - super(props); - this.state = { persistToken: false, user: undefined }; - } - - componentWillReceiveProps(props: AuthProviderProps) { - const { tokenAuth, tokenVerify } = props; - const tokenAuthOpts = tokenAuth[1]; - const tokenVerifyOpts = tokenVerify[1]; + useEffect(() => { + const token = getAuthToken(); + if (!!token && !userContext) { + verifyToken(token); + } else { + loginWithCredentialsManagementAPI(login); + } + }, []); + useEffect(() => { if (tokenAuthOpts.error || tokenVerifyOpts.error) { - this.logout(); + logout(); } if (tokenAuthOpts.data) { const user = tokenAuthOpts.data.tokenCreate.user; // FIXME: Now we set state also when auth fails and returned user is // `null`, because the LoginView uses this `null` to display error. - this.setState({ user }); + setUserContext(user); if (user) { - setAuthToken( - tokenAuthOpts.data.tokenCreate.token, - this.state.persistToken - ); + setAuthToken(tokenAuthOpts.data.tokenCreate.token, persistToken); } } else { if (maybe(() => tokenVerifyOpts.data.tokenVerify === null)) { - this.logout(); + logout(); } else { const user = maybe(() => tokenVerifyOpts.data.tokenVerify.user); if (!!user) { - this.setState({ user }); + setUserContext(user); } } } - } + }, [tokenAuthOpts, tokenVerifyOpts]); - componentDidMount() { - const { user } = this.state; - const token = getAuthToken(); - if (!!token && !user) { - this.verifyToken(token); - } else { - loginWithCredentialsManagementAPI(this.login); - } - } - - login = async (email: string, password: string) => { - const { tokenAuth, onLogin } = this.props; - const [tokenAuthFn] = tokenAuth; - - tokenAuthFn({ variables: { email, password } }).then(result => { + const login = async (email: string, password: string) => { + tokenAuth({ variables: { email, password } }).then(result => { if (result && !result.data.tokenCreate.errors.length) { if (!!onLogin) { onLogin(); @@ -156,65 +87,57 @@ class AuthProvider extends React.Component< }); }; - loginByToken = (token: string, user: User) => { - this.setState({ user }); - setAuthToken(token, this.state.persistToken); + const loginByToken = (token: string, user: User) => { + setUserContext(user); + setAuthToken(token, persistToken); }; - logout = () => { - this.setState({ user: undefined }); + const logout = () => { + setUserContext(undefined); if (isCredentialsManagementAPISupported) { navigator.credentials.preventSilentAccess(); } removeAuthToken(); }; - verifyToken = (token: string) => { - const { tokenVerify } = this.props; - const [tokenVerifyFn] = tokenVerify; + const verifyToken = (token: string) => tokenVerify({ variables: { token } }); - return tokenVerifyFn({ variables: { token } }); - }; - - refreshToken = async () => { - const { tokenRefresh } = this.props; - const [tokenRefreshFn] = tokenRefresh; + const refreshToken = async () => { const token = getAuthToken(); - const refreshData = await tokenRefreshFn({ variables: { token } }); + const refreshData = await tokenRefresh({ variables: { token } }); - setAuthToken(refreshData.data.tokenRefresh.token, this.state.persistToken); + setAuthToken(refreshData.data.tokenRefresh.token, persistToken); }; - render() { - const { children, tokenAuth, tokenVerify } = this.props; - const tokenAuthOpts = tokenAuth[1]; - const tokenVerifyOpts = tokenVerify[1]; - const { user } = this.state; - const isAuthenticated = !!user; + return ( + + {children} + + ); +}; - return ( - - {children({ - hasToken: !!getAuthToken(), - isAuthenticated, - tokenAuthLoading: tokenAuthOpts.loading, - tokenVerifyLoading: tokenVerifyOpts.loading, - user - })} - - ); - } -} +export const useAuth = () => { + const user = useContext(UserContext); + const isAuthenticated = !!user; -export default AuthProviderOperations; + return { + hasToken: !!getAuthToken(), + isAuthenticated, + tokenAuthLoading: user.tokenAuthLoading, + tokenVerifyLoading: user.tokenVerifyLoading, + user + }; +}; + +export default AuthProvider; From 9b97e3279bb4ae83cb0f2d30029f0f184f90349a Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 14 Jul 2020 10:11:43 +0200 Subject: [PATCH 03/23] Use useAuth hook --- src/auth/AuthProvider.tsx | 3 +- src/index.tsx | 281 +++++++++++++++++++------------------- 2 files changed, 141 insertions(+), 143 deletions(-) diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index 5db93208c..05b0f8236 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -26,6 +26,7 @@ import { interface AuthProviderProps { children: React.ReactNode; } + const AuthProvider: React.FC = ({ children }) => { const intl = useIntl(); const notify = useNotifier(); @@ -136,7 +137,7 @@ export const useAuth = () => { isAuthenticated, tokenAuthLoading: user.tokenAuthLoading, tokenVerifyLoading: user.tokenVerifyLoading, - user + user: user.user }; }; diff --git a/src/index.tsx b/src/index.tsx index 15250b8cd..4bf643631 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -20,7 +20,7 @@ import { appsSection } from "./apps/urls"; import AttributeSection from "./attributes"; import { attributeSection } from "./attributes/urls"; import Auth, { getAuthToken, removeAuthToken } from "./auth"; -import AuthProvider from "./auth/AuthProvider"; +import AuthProvider, { useAuth } from "./auth/AuthProvider"; import LoginLoading from "./auth/components/LoginLoading/LoginLoading"; import SectionRoute from "./auth/components/SectionRoute"; import { isJwtError } from "./auth/errors"; @@ -138,7 +138,9 @@ const App: React.FC = () => { - + + + @@ -154,150 +156,145 @@ const App: React.FC = () => { const Routes: React.FC = () => { const intl = useIntl(); const [, dispatchAppState] = useAppState(); + const { + hasToken, + isAuthenticated, + tokenAuthLoading, + tokenVerifyLoading, + user + } = useAuth(); return ( <> - - {({ - hasToken, - isAuthenticated, - tokenAuthLoading, - tokenVerifyLoading, - user - }) => - isAuthenticated && !tokenAuthLoading && !tokenVerifyLoading ? ( - - - - dispatchAppState({ - payload: { - error: "unhandled" - }, - type: "displayError" - }) - } - > - - - - - - - - - - - - - - - - - - - - - - {createConfigurationMenu(intl).filter(menu => - menu.menuItems.map(item => - hasPermission(item.permission, user) - ) - ).length > 0 && ( - - )} - - - - - ) : hasToken && tokenVerifyLoading ? ( - - ) : ( - - ) - } - + {isAuthenticated && !tokenAuthLoading && !tokenVerifyLoading ? ( + + + + dispatchAppState({ + payload: { + error: "unhandled" + }, + type: "displayError" + }) + } + > + + + + + + + + + + + + + + + + + + + + + + {createConfigurationMenu(intl).filter(menu => + menu.menuItems.map(item => hasPermission(item.permission, user)) + ).length > 0 && ( + + )} + + + + + ) : hasToken && tokenVerifyLoading ? ( + + ) : ( + + )} ); }; From dd4022dcd7b33a20752640bc0a7e0713f22b2fe2 Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Tue, 14 Jul 2020 16:31:41 +0200 Subject: [PATCH 04/23] use Apollo useMutation instead of custom hook --- src/auth/AuthProvider.tsx | 37 +++++++++++++++++++++++++++++-------- src/auth/mutations.ts | 17 +---------------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index 05b0f8236..e9f1cb38d 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -1,21 +1,25 @@ import { DEMO_MODE } from "@saleor/config"; import { User } from "@saleor/fragments/types/User"; import useNotifier from "@saleor/hooks/useNotifier"; -import { maybe } from "@saleor/misc"; +import { getMutationStatus, maybe } from "@saleor/misc"; import { isSupported as isCredentialsManagementAPISupported, login as loginWithCredentialsManagementAPI, saveCredentials } from "@saleor/utils/credentialsManagement"; import React, { useContext, useEffect, useState } from "react"; +import { useMutation } from "react-apollo"; import { useIntl } from "react-intl"; import { UserContext } from "./"; import { - useTokenAuthMutation, - useTokenRefreshMutation, - useTokenVerifyMutation + tokenAuthMutation, + tokenRefreshMutation, + tokenVerifyMutation } from "./mutations"; +import { RefreshToken, RefreshTokenVariables } from "./types/RefreshToken"; +import { TokenAuth, TokenAuthVariables } from "./types/TokenAuth"; +import { VerifyToken, VerifyTokenVariables } from "./types/VerifyToken"; import { displayDemoMessage, getAuthToken, @@ -31,9 +35,26 @@ const AuthProvider: React.FC = ({ children }) => { const intl = useIntl(); const notify = useNotifier(); - const [tokenAuth, tokenAuthOpts] = useTokenAuthMutation({}); - const [tokenRefresh] = useTokenRefreshMutation({}); - const [tokenVerify, tokenVerifyOpts] = useTokenVerifyMutation({}); + const [tokenAuth, tokenAuthResult] = useMutation< + TokenAuth, + TokenAuthVariables + >(tokenAuthMutation); + const [tokenRefresh] = useMutation( + tokenRefreshMutation + ); + const [tokenVerify, tokenVerifyResult] = useMutation< + VerifyToken, + VerifyTokenVariables + >(tokenVerifyMutation); + + const tokenAuthOpts = { + ...tokenAuthResult, + status: getMutationStatus(tokenAuthResult) + }; + const tokenVerifyOpts = { + ...tokenVerifyResult, + status: getMutationStatus(tokenVerifyResult) + }; const [userContext, setUserContext] = useState(undefined); const [persistToken] = useState(false); @@ -130,7 +151,7 @@ const AuthProvider: React.FC = ({ children }) => { export const useAuth = () => { const user = useContext(UserContext); - const isAuthenticated = !!user; + const isAuthenticated = !!user.user; return { hasToken: !!getAuthToken(), diff --git a/src/auth/mutations.ts b/src/auth/mutations.ts index 5b42493ea..3ae53b360 100644 --- a/src/auth/mutations.ts +++ b/src/auth/mutations.ts @@ -1,17 +1,13 @@ import { fragmentUser } from "@saleor/fragments/auth"; import { accountErrorFragment } from "@saleor/fragments/errors"; -import makeMutation from "@saleor/hooks/makeMutation"; import gql from "graphql-tag"; import { TypedMutation } from "../mutations"; -import { RefreshToken, RefreshTokenVariables } from "./types/RefreshToken"; import { RequestPasswordReset, RequestPasswordResetVariables } from "./types/RequestPasswordReset"; import { SetPassword, SetPasswordVariables } from "./types/SetPassword"; -import { TokenAuth, TokenAuthVariables } from "./types/TokenAuth"; -import { VerifyToken, VerifyTokenVariables } from "./types/VerifyToken"; export const tokenAuthMutation = gql` ${fragmentUser} @@ -28,9 +24,6 @@ export const tokenAuthMutation = gql` } } `; -export const useTokenAuthMutation = makeMutation( - tokenAuthMutation -); export const tokenVerifyMutation = gql` ${fragmentUser} @@ -43,22 +36,14 @@ export const tokenVerifyMutation = gql` } } `; -export const useTokenVerifyMutation = makeMutation< - VerifyToken, - VerifyTokenVariables ->(tokenVerifyMutation); -const refreshToken = gql` +export const tokenRefreshMutation = gql` mutation RefreshToken($token: String!) { tokenRefresh(csrfToken: $token) { token } } `; -export const useTokenRefreshMutation = makeMutation< - RefreshToken, - RefreshTokenVariables ->(refreshToken); export const requestPasswordReset = gql` ${accountErrorFragment} From d203a26bdec665655a89fff33fe894fbe0dfe91c Mon Sep 17 00:00:00 2001 From: Dawid Tarasiuk Date: Wed, 15 Jul 2020 16:09:12 +0200 Subject: [PATCH 05/23] Update auth provider --- src/auth/AuthProvider.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index e9f1cb38d..ba65ea14f 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -65,15 +65,6 @@ const AuthProvider: React.FC = ({ children }) => { } }; - useEffect(() => { - const token = getAuthToken(); - if (!!token && !userContext) { - verifyToken(token); - } else { - loginWithCredentialsManagementAPI(login); - } - }, []); - useEffect(() => { if (tokenAuthOpts.error || tokenVerifyOpts.error) { logout(); @@ -98,6 +89,15 @@ const AuthProvider: React.FC = ({ children }) => { } }, [tokenAuthOpts, tokenVerifyOpts]); + useEffect(() => { + const token = getAuthToken(); + if (!!token && !userContext) { + verifyToken(token); + } else { + loginWithCredentialsManagementAPI(login); + } + }, []); + const login = async (email: string, password: string) => { tokenAuth({ variables: { email, password } }).then(result => { if (result && !result.data.tokenCreate.errors.length) { From 4de1a6682c082c92809437b5a663e4e060e132b8 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Mon, 20 Jul 2020 11:46:59 +0200 Subject: [PATCH 06/23] Fix logout --- src/auth/AuthProvider.tsx | 83 +++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index ba65ea14f..cd0868dda 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -1,7 +1,7 @@ import { DEMO_MODE } from "@saleor/config"; import { User } from "@saleor/fragments/types/User"; import useNotifier from "@saleor/hooks/useNotifier"; -import { getMutationStatus, maybe } from "@saleor/misc"; +import { getMutationStatus } from "@saleor/misc"; import { isSupported as isCredentialsManagementAPISupported, login as loginWithCredentialsManagementAPI, @@ -27,6 +27,8 @@ import { setAuthToken } from "./utils"; +const persistToken = false; + interface AuthProviderProps { children: React.ReactNode; } @@ -35,17 +37,55 @@ const AuthProvider: React.FC = ({ children }) => { const intl = useIntl(); const notify = useNotifier(); + const [userContext, setUserContext] = useState(undefined); + + const logout = () => { + setUserContext(undefined); + if (isCredentialsManagementAPISupported) { + navigator.credentials.preventSilentAccess(); + } + removeAuthToken(); + }; + const [tokenAuth, tokenAuthResult] = useMutation< TokenAuth, TokenAuthVariables - >(tokenAuthMutation); + >(tokenAuthMutation, { + onCompleted: result => { + const user = result.tokenCreate.user; + + // FIXME: Now we set state also when auth fails and returned user is + // `null`, because the LoginView uses this `null` to display error. + setUserContext(user); + if (user) { + setAuthToken(result.tokenCreate.token, persistToken); + } + }, + onError: logout + }); const [tokenRefresh] = useMutation( - tokenRefreshMutation + tokenRefreshMutation, + { + onError: logout + } ); const [tokenVerify, tokenVerifyResult] = useMutation< VerifyToken, VerifyTokenVariables - >(tokenVerifyMutation); + >(tokenVerifyMutation, { + onCompleted: result => { + if (result.tokenVerify === null) { + logout(); + } else { + const user = result.tokenVerify?.user; + + if (!!user) { + setUserContext(user); + } + } + }, + onError: logout + }); const tokenAuthOpts = { ...tokenAuthResult, @@ -56,39 +96,12 @@ const AuthProvider: React.FC = ({ children }) => { status: getMutationStatus(tokenVerifyResult) }; - const [userContext, setUserContext] = useState(undefined); - const [persistToken] = useState(false); - const onLogin = () => { if (DEMO_MODE) { displayDemoMessage(intl, notify); } }; - useEffect(() => { - if (tokenAuthOpts.error || tokenVerifyOpts.error) { - logout(); - } - if (tokenAuthOpts.data) { - const user = tokenAuthOpts.data.tokenCreate.user; - // FIXME: Now we set state also when auth fails and returned user is - // `null`, because the LoginView uses this `null` to display error. - setUserContext(user); - if (user) { - setAuthToken(tokenAuthOpts.data.tokenCreate.token, persistToken); - } - } else { - if (maybe(() => tokenVerifyOpts.data.tokenVerify === null)) { - logout(); - } else { - const user = maybe(() => tokenVerifyOpts.data.tokenVerify.user); - if (!!user) { - setUserContext(user); - } - } - } - }, [tokenAuthOpts, tokenVerifyOpts]); - useEffect(() => { const token = getAuthToken(); if (!!token && !userContext) { @@ -114,14 +127,6 @@ const AuthProvider: React.FC = ({ children }) => { setAuthToken(token, persistToken); }; - const logout = () => { - setUserContext(undefined); - if (isCredentialsManagementAPISupported) { - navigator.credentials.preventSilentAccess(); - } - removeAuthToken(); - }; - const verifyToken = (token: string) => tokenVerify({ variables: { token } }); const refreshToken = async () => { From 816e21e4b7b5bf1ccc63f8885765626cbf9d4014 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Mon, 20 Jul 2020 11:58:39 +0200 Subject: [PATCH 07/23] Fail if user errors --- src/auth/AuthProvider.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index cd0868dda..87260a107 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -52,6 +52,10 @@ const AuthProvider: React.FC = ({ children }) => { TokenAuthVariables >(tokenAuthMutation, { onCompleted: result => { + if (result.tokenCreate.errors.length > 0) { + logout(); + } + const user = result.tokenCreate.user; // FIXME: Now we set state also when auth fails and returned user is From 24f4aecd970ae1b7c1077bbd0dcfbc2e15464f80 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Mon, 20 Jul 2020 12:12:14 +0200 Subject: [PATCH 08/23] Separate hook from component --- src/auth/AuthProvider.tsx | 52 +++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index 87260a107..57e695345 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -1,3 +1,4 @@ +import { IMessageContext } from "@saleor/components/messages"; import { DEMO_MODE } from "@saleor/config"; import { User } from "@saleor/fragments/types/User"; import useNotifier from "@saleor/hooks/useNotifier"; @@ -7,9 +8,10 @@ import { login as loginWithCredentialsManagementAPI, saveCredentials } from "@saleor/utils/credentialsManagement"; +import ApolloClient from "apollo-client"; import React, { useContext, useEffect, useState } from "react"; -import { useMutation } from "react-apollo"; -import { useIntl } from "react-intl"; +import { useApolloClient, useMutation } from "react-apollo"; +import { IntlShape, useIntl } from "react-intl"; import { UserContext } from "./"; import { @@ -29,14 +31,11 @@ import { const persistToken = false; -interface AuthProviderProps { - children: React.ReactNode; -} - -const AuthProvider: React.FC = ({ children }) => { - const intl = useIntl(); - const notify = useNotifier(); - +function useAuthProvider( + intl: IntlShape, + notify: IMessageContext, + apolloClient: ApolloClient +) { const [userContext, setUserContext] = useState(undefined); const logout = () => { @@ -51,6 +50,7 @@ const AuthProvider: React.FC = ({ children }) => { TokenAuth, TokenAuthVariables >(tokenAuthMutation, { + client: apolloClient, onCompleted: result => { if (result.tokenCreate.errors.length > 0) { logout(); @@ -70,6 +70,7 @@ const AuthProvider: React.FC = ({ children }) => { const [tokenRefresh] = useMutation( tokenRefreshMutation, { + client: apolloClient, onError: logout } ); @@ -77,6 +78,7 @@ const AuthProvider: React.FC = ({ children }) => { VerifyToken, VerifyTokenVariables >(tokenVerifyMutation, { + client: apolloClient, onCompleted: result => { if (result.tokenVerify === null) { logout(); @@ -141,6 +143,36 @@ const AuthProvider: React.FC = ({ children }) => { setAuthToken(refreshData.data.tokenRefresh.token, persistToken); }; + return { + login, + loginByToken, + logout, + refreshToken, + tokenAuthOpts, + tokenVerifyOpts, + userContext + }; +} + +interface AuthProviderProps { + children: React.ReactNode; +} + +const AuthProvider: React.FC = ({ children }) => { + const apolloClient = useApolloClient(); + const intl = useIntl(); + const notify = useNotifier(); + + const { + login, + loginByToken, + logout, + tokenAuthOpts, + refreshToken, + tokenVerifyOpts, + userContext + } = useAuthProvider(intl, notify, apolloClient); + return ( Date: Mon, 20 Jul 2020 12:05:47 +0200 Subject: [PATCH 09/23] wip --- package-lock.json | 2507 +++++++++++++++++++++++++++++++++ package.json | 8 + src/auth/AuthProvider.test.ts | 0 testUtils/api.ts | 43 + 4 files changed, 2558 insertions(+) create mode 100644 src/auth/AuthProvider.test.ts create mode 100644 testUtils/api.ts diff --git a/package-lock.json b/package-lock.json index 1d772264e..a2c287562 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2722,6 +2722,106 @@ "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-1.0.4.tgz", "integrity": "sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw==" }, + "@pollyjs/adapter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/adapter/-/adapter-5.0.0.tgz", + "integrity": "sha512-GaJUp9hKKKbRbh1FDbjVfxNGIYs8/1QQI5SR+zjz+OjpjV2btRPFCq1cqO4ORrHA2pTIC8IvEL3lgPSG4v/cPg==", + "dev": true, + "requires": { + "@pollyjs/utils": "^5.0.0" + } + }, + "@pollyjs/adapter-node-http": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/adapter-node-http/-/adapter-node-http-5.0.0.tgz", + "integrity": "sha512-9WjV6UVHWpcDZc1Dt5Slxn191iN55RpCirEAdt+h5wvPgRJmsLuXMI1vT3r9va8Z+a3qCqM458waNfr5us2Wuw==", + "dev": true, + "requires": { + "@pollyjs/adapter": "^5.0.0", + "@pollyjs/utils": "^5.0.0", + "lodash-es": "^4.17.11", + "nock": "^12.0.3" + } + }, + "@pollyjs/core": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/core/-/core-5.0.0.tgz", + "integrity": "sha512-f/v5z7aKSWdeBCZQFiQGL4aZmdWAuQWV+U/fMbAIEYjkX8Av0gWPhkFyeMfeFriYMg7Ts1XxPNI7LYroCnjn7w==", + "dev": true, + "requires": { + "@pollyjs/utils": "^5.0.0", + "@sindresorhus/fnv1a": "^1.2.0", + "blueimp-md5": "^2.10.0", + "fast-json-stable-stringify": "^2.0.0", + "is-absolute-url": "^3.0.0", + "lodash-es": "^4.17.11", + "route-recognizer": "^0.3.4", + "slugify": "^1.3.4" + } + }, + "@pollyjs/node-server": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/node-server/-/node-server-5.0.0.tgz", + "integrity": "sha512-DK1hqsZnIU7TXECNPI6E+ojaSpv62lt3+8eGn3qFbsa61ToTFQLFEMNfcTWA6ZXArrpdzYAdKVTt8BsdEujHkg==", + "dev": true, + "requires": { + "@pollyjs/utils": "^5.0.0", + "body-parser": "^1.19.0", + "cors": "^2.8.5", + "express": "^4.17.1", + "fs-extra": "^8.0.1", + "http-graceful-shutdown": "^2.3.1", + "morgan": "^1.9.1", + "nocache": "^2.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "@pollyjs/persister": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/persister/-/persister-5.0.0.tgz", + "integrity": "sha512-bTJes0c2/y4xrXi1vPypJhup/N/VPC1jY7maOuSv4pOFo6VAvoVJGT2sgMMnPU7qHSRn9Y6v+S7QqGtLOZCyAA==", + "dev": true, + "requires": { + "@pollyjs/utils": "^5.0.0", + "bowser": "^2.4.0", + "fast-json-stable-stringify": "^2.0.0", + "lodash-es": "^4.17.11", + "set-cookie-parser": "^2.3.5", + "utf8-byte-length": "^1.0.4" + } + }, + "@pollyjs/persister-fs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/persister-fs/-/persister-fs-5.0.0.tgz", + "integrity": "sha512-ju75JJ6sSJ/q68rB74wxIlvXCq/riuCqW3pS/Ucd4SFfks/HxXxM3yGTHn0Qq5SrmY9cYbQFfpOz0WCNDIqr+A==", + "dev": true, + "requires": { + "@pollyjs/node-server": "^5.0.0", + "@pollyjs/persister": "^5.0.0" + } + }, + "@pollyjs/utils": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/utils/-/utils-5.0.0.tgz", + "integrity": "sha512-zXoR13NGR1fVoUAcKR9/8AYCXZsMkG5EdvTZbR1nk5hCYJN1AR73HAKXMPsk/2vkLnBr6LWoAr3f9LjwqJOehQ==", + "dev": true, + "requires": { + "qs": "^6.7.0", + "url-parse": "^1.4.7" + } + }, "@reach/router": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@reach/router/-/router-1.2.1.tgz", @@ -2768,6 +2868,12 @@ "any-observable": "^0.3.0" } }, + "@sindresorhus/fnv1a": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/fnv1a/-/fnv1a-1.2.0.tgz", + "integrity": "sha512-5ezb/dBSTWtKQ4sLQwMgOJyREXJcZZkTMbendMwKrXTghUhWjZhstzkkmt4/WkFy/GSTSGzfJOKU7dEXv3C/XQ==", + "dev": true + }, "@storybook/addon-storyshots": { "version": "5.2.8", "resolved": "https://registry.npmjs.org/@storybook/addon-storyshots/-/addon-storyshots-5.2.8.tgz", @@ -3601,12 +3707,24 @@ "@types/react": "*" } }, + "@types/error-stack-parser": { + "version": "1.3.18", + "resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz", + "integrity": "sha1-4ByfjIXKg7YQMgxiJYsMkCat4Pc=", + "dev": true + }, "@types/eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", "dev": true }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -3743,6 +3861,49 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "@types/pollyjs__adapter": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@types/pollyjs__adapter/-/pollyjs__adapter-4.3.0.tgz", + "integrity": "sha512-aJ4+ianTKgbdZRnBjcB9D1Kn4ALn3YatGlAzgEdGHyv63aV+gzukKH8xqdrRB76pwrXXCQwLosNBFNTSEmRA9A==", + "dev": true, + "requires": { + "@types/pollyjs__core": "*" + } + }, + "@types/pollyjs__adapter-node-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/pollyjs__adapter-node-http/-/pollyjs__adapter-node-http-2.0.0.tgz", + "integrity": "sha512-v8g65xEnADlb1UZe4fNPO9KpU04uY6HAPns307RWqePlVtftctD73FA++74PbO17PNgMbi/7IzrZatiEEn9CPQ==", + "dev": true, + "requires": { + "@types/pollyjs__adapter": "*" + } + }, + "@types/pollyjs__core": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@types/pollyjs__core/-/pollyjs__core-4.3.0.tgz", + "integrity": "sha512-gloFb5e36G8Tice0J2GhdzqAAongh2ALBel5QjvjmTk4Vv0+mD3xkE3O50XHdRfbJwCOo7A/rpH+6J+kuKXdZg==", + "dev": true, + "requires": { + "@types/pollyjs__adapter": "*", + "@types/pollyjs__persister": "*" + } + }, + "@types/pollyjs__persister": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@types/pollyjs__persister/-/pollyjs__persister-4.3.0.tgz", + "integrity": "sha512-gEwKh9XQSHo+1iBTHKqBIa/BHfG0cLidgryU6jEWzQ9NTtYOGb2V5tf3qb0ddihO7kJW/d0MCKTN28hDS+XCDg==", + "dev": true + }, + "@types/pollyjs__persister-fs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/pollyjs__persister-fs/-/pollyjs__persister-fs-2.0.0.tgz", + "integrity": "sha512-m788FLVtzE+qqrNeotBel2S1EhNMkkNRVJa9XQqeNdwvaJvOp7h/EWacKCrdHLK406LBo7vhL8dpWldkRBm0lg==", + "dev": true, + "requires": { + "@types/pollyjs__persister": "*" + } + }, "@types/prop-types": { "version": "15.7.3", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", @@ -4429,6 +4590,15 @@ "acorn-walk": "^6.0.1" } }, + "acorn-hammerhead": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/acorn-hammerhead/-/acorn-hammerhead-0.3.0.tgz", + "integrity": "sha512-Izrr9mXONhWc7q8fqUe6ijQy+KjmyQlgdWARgaCVjds+nPpoSS298FY8uSVN/to8nKVTtkJpafNUlACWxwZS5w==", + "dev": true, + "requires": { + "@types/estree": "^0.0.39" + } + }, "acorn-jsx": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", @@ -4546,6 +4716,12 @@ "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", "dev": true }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, "ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -5171,6 +5347,12 @@ "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", "dev": true }, + "array-find": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz", + "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", + "dev": true + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -5251,6 +5433,22 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, + "asar": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/asar/-/asar-2.1.0.tgz", + "integrity": "sha512-d2Ovma+bfqNpvBzY/KU8oPY67ZworixTpkjSx0PCXnQi67c2cXmssaTxpFDUM0ttopXoGx/KRxNg/GDThYbXQA==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "chromium-pickle-js": "^0.2.0", + "commander": "^2.20.0", + "cuint": "^0.2.2", + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "tmp-promise": "^1.0.5" + } + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -5304,6 +5502,12 @@ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -5335,6 +5539,12 @@ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, + "async-exit-hook": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-1.1.2.tgz", + "integrity": "sha1-gJXXXkiMKazuBVH+hyUhadeJz7o=", + "dev": true + }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", @@ -5489,18 +5699,131 @@ } } }, + "babel-helper-bindify-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", + "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-builder-react-jsx": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", + "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "esutils": "^2.0.2" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, "babel-helper-evaluate-path": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.5.0.tgz", "integrity": "sha512-mUh0UhS607bGh5wUMAQfOpt2JX2ThXMtppHRdRU1kL7ZLRWIXxoV2UIV1r2cAeeNeU1M5SB5/RSUgUxrK8yOkA==", "dev": true }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-explode-class": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", + "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", + "dev": true, + "requires": { + "babel-helper-bindify-decorators": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, "babel-helper-flip-expressions": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz", "integrity": "sha1-NpZzahKKwYvCUlS19AoizrPB0/0=", "dev": true }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, "babel-helper-is-nodes-equiv": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz", @@ -5519,18 +5842,76 @@ "integrity": "sha1-0kSjvvmESHJgP/tG4izorN9VFWI=", "dev": true }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, "babel-helper-remove-or-void": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz", "integrity": "sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA=", "dev": true }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, "babel-helper-to-multiple-sequence-expressions": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz", "integrity": "sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA==", "dev": true }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, "babel-jest": { "version": "23.6.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-23.6.0.tgz", @@ -5866,6 +6247,15 @@ "integrity": "sha1-M51M3be2X9YtHfnbn+BN4TQSK9U=", "dev": true }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, "babel-plugin-dynamic-import-node": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", @@ -6133,6 +6523,48 @@ "murmurhash3js": "^3.0.1" } }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-async-generators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", + "dev": true + }, + "babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", + "dev": true + }, + "babel-plugin-syntax-decorators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", + "dev": true + }, + "babel-plugin-syntax-dynamic-import": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-flow": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", + "dev": true + }, "babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", @@ -6145,6 +6577,354 @@ "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", "dev": true }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-generator-functions": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", + "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-generators": "^6.5.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", + "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", + "dev": true, + "requires": { + "babel-helper-explode-class": "^6.24.1", + "babel-plugin-syntax-decorators": "^6.13.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-flow-strip-types": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", + "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", + "dev": true, + "requires": { + "babel-plugin-syntax-flow": "^6.18.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-for-of-as-array": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-for-of-as-array/-/babel-plugin-transform-for-of-as-array-1.1.1.tgz", + "integrity": "sha512-eE4hZJhOUKpX0q/X3adR8B4hLox+t8oe4ZqmhANUmv4cds07AbWt6O0rtFXK7PKFPPnW4nz/5mpbkPMkflyGeg==", + "dev": true + }, "babel-plugin-transform-inline-consecutive-adds": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz", @@ -6169,6 +6949,16 @@ "integrity": "sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg=", "dev": true }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, "babel-plugin-transform-property-literals": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz", @@ -6178,12 +6968,74 @@ "esutils": "^2.0.2" } }, + "babel-plugin-transform-react-display-name": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", + "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", + "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", + "dev": true, + "requires": { + "babel-helper-builder-react-jsx": "^6.24.1", + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx-self": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz", + "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=", + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx-source": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", + "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, "babel-plugin-transform-react-remove-prop-types": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", "dev": true }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "^0.10.0" + }, + "dependencies": { + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + } + } + }, "babel-plugin-transform-regexp-constructors": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz", @@ -6211,12 +7063,31 @@ "babel-helper-evaluate-path": "^0.5.0" } }, + "babel-plugin-transform-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", + "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, "babel-plugin-transform-simplify-comparison-operators": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz", "integrity": "sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk=", "dev": true }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, "babel-plugin-transform-undefined-to-void": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz", @@ -6245,6 +7116,65 @@ } } }, + "babel-preset-env": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", + "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^3.2.6", + "invariant": "^2.2.2", + "semver": "^5.3.0" + }, + "dependencies": { + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + } + } + }, + "babel-preset-flow": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", + "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", + "dev": true, + "requires": { + "babel-plugin-transform-flow-strip-types": "^6.22.0" + } + }, "babel-preset-jest": { "version": "23.2.0", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz", @@ -6286,6 +7216,20 @@ "lodash": "^4.17.11" } }, + "babel-preset-react": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", + "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=", + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "^6.3.13", + "babel-plugin-transform-react-display-name": "^6.23.0", + "babel-plugin-transform-react-jsx": "^6.24.1", + "babel-plugin-transform-react-jsx-self": "^6.22.0", + "babel-plugin-transform-react-jsx-source": "^6.22.0", + "babel-preset-flow": "^6.23.0" + } + }, "babel-preset-react-app": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-9.0.2.tgz", @@ -6471,6 +7415,117 @@ } } }, + "babel-preset-stage-2": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", + "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", + "dev": true, + "requires": { + "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-decorators": "^6.24.1", + "babel-preset-stage-3": "^6.24.1" + } + }, + "babel-preset-stage-3": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", + "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", + "dev": true, + "requires": { + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-generator-functions": "^6.24.1", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-exponentiation-operator": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.22.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + }, + "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + } + } + }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", @@ -6637,6 +7692,23 @@ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", "dev": true }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -6664,6 +7736,12 @@ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, + "bin-v8-flags-filter": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bin-v8-flags-filter/-/bin-v8-flags-filter-1.2.0.tgz", + "integrity": "sha512-g8aeYkY7GhyyKRvQMBsJQZjhm2iCX3dKYvfrMpwVR8IxmUGrkpCBFoKbB9Rh0o3sTLCjU/1tFpZ4C7j3f+D+3g==", + "dev": true + }, "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", @@ -6676,6 +7754,12 @@ "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", "dev": true }, + "blueimp-md5": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.17.0.tgz", + "integrity": "sha512-x5PKJHY5rHQYaADj6NwPUR2QRCUVSggPzrUKkeENpj871o9l9IefJbO2jkT5UvYykeOK9dx0VmkIo6dZ+vThYw==", + "dev": true + }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", @@ -6751,6 +7835,12 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, + "bowser": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.10.0.tgz", + "integrity": "sha512-OCsqTQboTEWWsUjcp5jLSw2ZHsBiv2C105iFs61bOT0Hnwi9p7/uuXdd7mu8RYcarREfdjNN+8LitmEHATsLYg==", + "dev": true + }, "boxen": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-3.2.0.tgz", @@ -6824,6 +7914,15 @@ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, + "brotli": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.2.tgz", + "integrity": "sha1-UlqcrU/LqWR119OI9q7LE+7VL0Y=", + "dev": true, + "requires": { + "base64-js": "^1.1.2" + } + }, "browser-process-hrtime": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", @@ -7085,6 +8184,28 @@ "caller-callsite": "^2.0.0" } }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "callsite-record": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/callsite-record/-/callsite-record-4.1.3.tgz", + "integrity": "sha512-otAcPmu8TiHZ38cIL3NjQa1nGoSQRRe8WDDUgj5ZUwJWn1wzOYBwVSJbpVyzZ0sesQeKlYsPu9DG70fhh6AK9g==", + "dev": true, + "requires": { + "@types/error-stack-parser": "^1.3.18", + "@types/lodash": "^4.14.72", + "callsite": "^1.0.0", + "chalk": "^2.4.0", + "error-stack-parser": "^1.3.3", + "highlight-es": "^1.0.0", + "lodash": "4.6.1 || ^4.16.1", + "pinkie-promise": "^2.0.0" + } + }, "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", @@ -7147,6 +8268,20 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -7203,6 +8338,12 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, "check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -7261,6 +8402,41 @@ "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true }, + "chrome-remote-interface": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.25.7.tgz", + "integrity": "sha512-6zI6LbR2IiGmduFZededaerEr9hHXabxT/L+fRrdq65a0CfyLMzpq0BKuZiqN0Upqcacsb6q2POj7fmobwBsEA==", + "dev": true, + "requires": { + "commander": "2.11.x", + "ws": "3.3.x" + }, + "dependencies": { + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, "chrome-trace-event": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", @@ -7270,6 +8446,12 @@ "tslib": "^1.9.0" } }, + "chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=", + "dev": true + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -7564,6 +8746,12 @@ "urlgrey": "0.4.4" } }, + "coffeescript": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.5.1.tgz", + "integrity": "sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ==", + "dev": true + }, "collapse-white-space": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", @@ -7901,6 +9089,16 @@ } } }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", @@ -8051,6 +9249,35 @@ "randomfill": "^1.0.3" } }, + "crypto-md5": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-md5/-/crypto-md5-1.0.0.tgz", + "integrity": "sha1-zMjadQx1PH7curxUKWdHKjhOhrs=", + "dev": true + }, + "css": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.3.tgz", + "integrity": "sha512-0W171WccAjQGGTKLhw4m2nnl0zPHUlTO/I8td4XzJgIB8Hg3ZZx71qT4G4eX8OVsSiaAKiUMy73E3nsbPlg2DQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "source-map": "^0.1.38", + "source-map-resolve": "^0.5.1", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, "css-jss": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/css-jss/-/css-jss-10.0.0.tgz", @@ -8192,6 +9419,12 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz", "integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==" }, + "cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", + "dev": true + }, "cyclist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", @@ -8391,6 +9624,15 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -8727,6 +9969,12 @@ } } }, + "device-specs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/device-specs/-/device-specs-1.0.0.tgz", + "integrity": "sha512-fYXbFSeilT7bnKWFi4OERSPHdtaEoDGn4aUhV5Nly6/I+Tp6JZ/6Icmd7LVIF5euyodGpxz2e/bfUmDnIdSIDw==", + "dev": true + }, "diff": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", @@ -9067,6 +10315,12 @@ "minimalistic-crypto-utils": "^1.0.0" } }, + "emittery": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.4.1.tgz", + "integrity": "sha512-r4eRSeStEGf6M5SKdrQhhLK5bOwOBxQhIE3YSTnZE3GpKiLfnnhE+tPtrJE79+eDJgm39BM6LSoI8SCx4HbwlQ==", + "dev": true + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -9111,6 +10365,33 @@ "once": "^1.4.0" } }, + "endpoint-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/endpoint-utils/-/endpoint-utils-1.0.2.tgz", + "integrity": "sha1-CAjDNppyfNeWejn/NOvJJriBRqg=", + "dev": true, + "requires": { + "ip": "^1.1.3", + "pinkie-promise": "^1.0.0" + }, + "dependencies": { + "pinkie": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-1.0.0.tgz", + "integrity": "sha1-Wkfyi6EBXQIBvae/DzWOR77Ix+Q=", + "dev": true + }, + "pinkie-promise": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-1.0.0.tgz", + "integrity": "sha1-0dpn9UglY7t89X8oauKCLs+/NnA=", + "dev": true, + "requires": { + "pinkie": "^1.0.0" + } + } + } + }, "enhanced-resolve": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", @@ -9245,6 +10526,15 @@ "is-arrayish": "^0.2.1" } }, + "error-stack-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-1.3.6.tgz", + "integrity": "sha1-4Oc7k+QXE40c18C3RrGkoUhUwpI=", + "dev": true, + "requires": { + "stackframe": "^0.3.1" + } + }, "es-abstract": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", @@ -9964,6 +11254,15 @@ "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", "dev": true }, + "esotope-hammerhead": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/esotope-hammerhead/-/esotope-hammerhead-0.5.5.tgz", + "integrity": "sha512-EuSYJDtF8gLMB24lzjHw2KotauPsVJybFrtGfQyMm48oC7sTkspA26DqcqcbnRl4GC6sPVKWEx+ex72eqopX9Q==", + "dev": true, + "requires": { + "@types/estree": "^0.0.39" + } + }, "espree": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", @@ -11751,6 +13050,12 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, "get-own-enumerable-property-symbols": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz", @@ -12016,6 +13321,15 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" }, + "graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, "graphql": { "version": "14.5.8", "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.5.8.tgz", @@ -12196,6 +13510,25 @@ "upper-case": "^1.1.3" } }, + "highlight-es": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/highlight-es/-/highlight-es-1.0.3.tgz", + "integrity": "sha512-s/SIX6yp/5S1p8aC/NRDC1fwEb+myGIfp8/TzZz0rtAv8fzsdX7vGl3Q1TrXCsczFq8DI3CBFBCySPClfBSdbg==", + "dev": true, + "requires": { + "chalk": "^2.4.0", + "is-es2016-keyword": "^1.0.0", + "js-tokens": "^3.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + } + } + }, "highlight.js": { "version": "9.12.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", @@ -12234,6 +13567,16 @@ "react-is": "^16.7.0" } }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -12521,6 +13864,15 @@ } } }, + "http-graceful-shutdown": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/http-graceful-shutdown/-/http-graceful-shutdown-2.3.2.tgz", + "integrity": "sha512-Dn7fJjHWboN7WjNDuo7d7ZISdUlbnyQEtOjBwMGJig45ZztHQxCsnW9N89Pr3gb6VzvZy1HySgAu2Q98j6S17w==", + "dev": true, + "requires": { + "debug": "^4.1.1" + } + }, "http-parser-js": { "version": "0.4.10", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", @@ -12804,6 +14156,12 @@ "resolve-from": "^3.0.0" } }, + "import-lazy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz", + "integrity": "sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==", + "dev": true + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -13104,6 +14462,12 @@ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" }, + "is-docker": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", + "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "dev": true + }, "is-dotfile": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", @@ -13119,6 +14483,12 @@ "is-primitive": "^2.0.0" } }, + "is-es2016-keyword": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-es2016-keyword/-/is-es2016-keyword-1.0.0.tgz", + "integrity": "sha1-9uVOEQxeT40mXmnS7Q6vjPX0dxg=", + "dev": true + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -13186,6 +14556,12 @@ "is-path-inside": "^3.0.1" } }, + "is-jquery-obj": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-jquery-obj/-/is-jquery-obj-0.1.1.tgz", + "integrity": "sha512-18toSebUVF7y717dgw/Dzn6djOCqrkiDp3MhB8P6TdKyCVkbD1ZwE7Uz8Hwx6hUPTvKjbyYH9ncXT4ts4qLaSA==", + "dev": true + }, "is-lower-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", @@ -13240,6 +14616,26 @@ "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + }, + "dependencies": { + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + } + } + }, "is-path-inside": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", @@ -14746,6 +16142,15 @@ } } }, + "linux-platform-info": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/linux-platform-info/-/linux-platform-info-0.0.3.tgz", + "integrity": "sha1-La4yQ4Xmbj11W+yD+Gx77qYc64M=", + "dev": true, + "requires": { + "os-family": "^1.0.0" + } + }, "listr": { "version": "0.14.3", "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", @@ -15158,6 +16563,71 @@ } } }, + "log-update-async-hook": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/log-update-async-hook/-/log-update-async-hook-2.0.2.tgz", + "integrity": "sha512-HQwkKFTZeUOrDi1Duf2CSUa/pSpcaCHKLdx3D/Z16DsipzByOBffcg5y0JZA1q0n80dYgLXe2hFM9JGNgBsTDw==", + "dev": true, + "requires": { + "ansi-escapes": "^2.0.0", + "async-exit-hook": "^1.1.2", + "onetime": "^2.0.1", + "wrap-ansi": "^2.1.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-2.0.0.tgz", + "integrity": "sha1-W65SvkJIeN2Xg+iRDj/Cki6DyBs=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + } + } + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -15242,6 +16712,12 @@ "integrity": "sha1-beJlMXSt+12e3DPGnT6Sobdvrwg=", "dev": true }, + "map-reverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-reverse/-/map-reverse-1.0.1.tgz", + "integrity": "sha1-J06fUAphEVMYO1uNhJCpwcI+4xA=", + "dev": true + }, "map-stream": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", @@ -15272,6 +16748,15 @@ "unquote": "^1.1.0" } }, + "match-url-wildcard": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/match-url-wildcard/-/match-url-wildcard-0.0.4.tgz", + "integrity": "sha512-R1XhQaamUZPWLOPtp4ig5j+3jctN+skhgRmEQTUamMzmNtRG69QEirQs0NZKLtHMR7tzWpmtnS4Eqv65DcgXUA==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, "math-random": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", @@ -15590,6 +17075,12 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" }, + "moment-duration-format-commonjs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/moment-duration-format-commonjs/-/moment-duration-format-commonjs-1.0.0.tgz", + "integrity": "sha512-MVFR4hIh4jfuwSCPBEE5CCwn3refvTsxK/Yv/DpKJ6YcNnCimlVJ6DQeTJG1KVQPw1o8m3tkbHE9gVjivyv9iA==", + "dev": true + }, "moment-timezone": { "version": "0.5.27", "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz", @@ -15604,6 +17095,42 @@ "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==", "dev": true }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dev": true, + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -15656,6 +17183,12 @@ "integrity": "sha1-Ppg+W0fCoG9DpxMXTn5DXKBEuZg=", "dev": true }, + "mustache": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz", + "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==", + "dev": true + }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", @@ -15668,6 +17201,12 @@ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "optional": true }, + "nanoid": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz", + "integrity": "sha512-4ug4BsuHxiVHoRUe1ud6rUFT3WUMmjXt1W0quL0CviZQANdan7D8kqN5/maw53hmAApY/jfzMRkC57BNNs60ZQ==", + "dev": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -15736,6 +17275,24 @@ "lower-case": "^1.1.1" } }, + "nocache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", + "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==", + "dev": true + }, + "nock": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/nock/-/nock-12.0.3.tgz", + "integrity": "sha512-QNb/j8kbFnKCiyqi9C5DD0jH/FubFGj5rt9NQFONXwQm3IPB0CULECg/eS3AU1KgZb/6SwUa4/DTRKhVxkGABw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.13", + "propagate": "^2.0.0" + } + }, "node-dir": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", @@ -15837,6 +17394,12 @@ } } }, + "node-version": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.2.0.tgz", + "integrity": "sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ==", + "dev": true + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -16178,6 +17741,18 @@ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "dev": true }, + "os-family": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/os-family/-/os-family-1.1.0.tgz", + "integrity": "sha512-E3Orl5pvDJXnVmpaAA2TeNNpNhTMl4o5HghuWhOivBjEiTnJSrMYSa5uZMek1lBEvu8kKEsa2YgVcGFVDqX/9w==", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, "os-locale": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", @@ -16495,6 +18070,12 @@ } } }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, "pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -16641,6 +18222,12 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, + "pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true + }, "pnp-webpack-plugin": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.4.3.tgz", @@ -16940,6 +18527,15 @@ "function-bind": "^1.1.1" } }, + "promisify-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/promisify-event/-/promisify-event-1.0.0.tgz", + "integrity": "sha1-vXUj6ga3AWLzcJeQFrU6aGxg6Q8=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, "prompts": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.2.1.tgz", @@ -16980,6 +18576,12 @@ "warning": "^3.0.0" } }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, "property-information": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.3.0.tgz", @@ -17091,6 +18693,12 @@ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", "dev": true }, + "qrcode-terminal": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.10.0.tgz", + "integrity": "sha1-p2pI4mEKGPl/o6K9UytoKs/4bFM=", + "dev": true + }, "qs": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.0.tgz", @@ -17868,6 +19476,15 @@ } } }, + "read-file-relative": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/read-file-relative/-/read-file-relative-1.2.0.tgz", + "integrity": "sha1-mPfZbqoh0rTHov69Y9L8jPNen5s=", + "dev": true, + "requires": { + "callsite": "^1.0.0" + } + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -18298,6 +19915,12 @@ "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" }, + "replicator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/replicator/-/replicator-1.0.3.tgz", + "integrity": "sha512-WsKsraaM0x0QHy5CtzdgFXUxyowoBhyNkmPqmZShW6h+rOWnyT6Od3zRdTX9r616rAA6kDC9MKQGnSM/CJKfVQ==", + "dev": true + }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", @@ -18527,6 +20150,12 @@ "inherits": "^2.0.1" } }, + "route-recognizer": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/route-recognizer/-/route-recognizer-0.3.4.tgz", + "integrity": "sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g==", + "dev": true + }, "rst-selector-parser": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", @@ -18617,6 +20246,15 @@ "walker": "~1.0.5" } }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -18831,6 +20469,12 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "set-cookie-parser": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.6.tgz", + "integrity": "sha512-mNCnTUF0OYPwYzSHbdRdCfNNHqrne+HS5tS5xNb6yJbdP9wInV0q5xPLE0EyfV/Q3tImo3y/OXpD8Jn0Jtnjrg==", + "dev": true + }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", @@ -18865,6 +20509,12 @@ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, + "setup-polly-jest": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/setup-polly-jest/-/setup-polly-jest-0.9.1.tgz", + "integrity": "sha512-BbOxCvMnDHAPG9e3Qw3Ol4/ZWLwnDnhODqrfiIpAtF7Azu0O3PI4svZIfsh+/EV9xNj15LMkvchaWsDvF4A3hQ==", + "dev": true + }, "sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -19385,6 +21035,12 @@ "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", "dev": true }, + "stackframe": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", + "integrity": "sha1-M6qE8Rd6VUjIk1Uzy/6zQgl19aQ=", + "dev": true + }, "start-server-and-test": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.11.0.tgz", @@ -20168,6 +21824,748 @@ } } }, + "testcafe": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.8.8.tgz", + "integrity": "sha512-e8yLVU76fEWwh/UIkbTUSDa/e+J8BryErXqNf8itKHA/s9B6Mt9IfsVCCSYXUfx9DIruzcLNTsJ2q1xxc2KO+A==", + "dev": true, + "requires": { + "@types/node": "^10.12.19", + "async-exit-hook": "^1.1.2", + "babel-core": "^6.22.1", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-for-of-as-array": "^1.1.1", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-preset-env": "^1.1.8", + "babel-preset-flow": "^6.23.0", + "babel-preset-react": "^6.24.1", + "babel-preset-stage-2": "^6.22.0", + "babel-runtime": "^6.22.0", + "bin-v8-flags-filter": "^1.1.2", + "bowser": "^2.8.1", + "callsite": "^1.0.0", + "callsite-record": "^4.0.0", + "chai": "^4.1.2", + "chalk": "^2.3.0", + "chrome-remote-interface": "^0.25.3", + "coffeescript": "^2.3.1", + "commander": "^2.8.1", + "debug": "^2.2.0", + "dedent": "^0.4.0", + "del": "^3.0.0", + "device-specs": "^1.0.0", + "elegant-spinner": "^1.0.1", + "emittery": "^0.4.1", + "endpoint-utils": "^1.0.2", + "error-stack-parser": "^1.3.6", + "globby": "^9.2.0", + "graceful-fs": "^4.1.11", + "graphlib": "^2.1.5", + "import-lazy": "^3.1.0", + "indent-string": "^1.2.2", + "is-ci": "^1.0.10", + "is-docker": "^2.0.0", + "is-glob": "^2.0.1", + "is-stream": "^1.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "log-update-async-hook": "^2.0.2", + "make-dir": "^3.0.0", + "map-reverse": "^1.0.1", + "mime-db": "^1.41.0", + "moment": "^2.10.3", + "moment-duration-format-commonjs": "^1.0.0", + "mustache": "^2.1.2", + "nanoid": "^1.0.1", + "node-version": "^1.0.0", + "os-family": "^1.0.0", + "parse5": "^1.5.0", + "pify": "^2.3.0", + "pinkie": "^2.0.4", + "pngjs": "^3.3.1", + "promisify-event": "^1.0.0", + "qrcode-terminal": "^0.10.0", + "read-file-relative": "^1.2.0", + "replicator": "^1.0.3", + "resolve-cwd": "^1.0.0", + "resolve-from": "^4.0.0", + "sanitize-filename": "^1.6.0", + "source-map-support": "^0.5.16", + "strip-bom": "^2.0.0", + "testcafe-browser-tools": "2.0.12", + "testcafe-hammerhead": "17.1.9", + "testcafe-legacy-api": "4.0.0", + "testcafe-reporter-json": "^2.1.0", + "testcafe-reporter-list": "^2.1.0", + "testcafe-reporter-minimal": "^2.1.0", + "testcafe-reporter-spec": "^2.1.1", + "testcafe-reporter-xunit": "^2.1.0", + "time-limit-promise": "^1.0.2", + "tmp": "0.0.28", + "tree-kill": "^1.1.0", + "typescript": "^3.3.3" + }, + "dependencies": { + "@types/node": { + "version": "10.17.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.28.tgz", + "integrity": "sha512-dzjES1Egb4c1a89C7lKwQh8pwjYmlOAG9dW1pBgxEk57tMrLnssOfEthz8kdkNaBd7lIqQx7APm5+mZ619IiCQ==", + "dev": true + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + }, + "dependencies": { + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + } + } + }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "dedent": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.4.0.tgz", + "integrity": "sha1-h979BAvUwVldljKC7FfzwqhSVkI=", + "dev": true + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + } + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "indent-string": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "integrity": "sha1-25m8xYPrarux5I3LsZmamGBBy2s=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "minimist": "^1.1.0", + "repeating": "^1.1.0" + } + }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "parse5": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", + "integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "repeating": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "integrity": "sha1-PUEUIYh3U3SU+X93+Xhfq4EPpKw=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-1.0.0.tgz", + "integrity": "sha1-Tq7qQe0EDRcCRX32SkKysH0kb58=", + "dev": true, + "requires": { + "resolve-from": "^2.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", + "dev": true + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "tmp": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz", + "integrity": "sha1-Fyc1t/YU6nrzlmT6hM8N5OUV0SA=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "testcafe-browser-tools": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/testcafe-browser-tools/-/testcafe-browser-tools-2.0.12.tgz", + "integrity": "sha512-5oNNYlcZiDspqJB6L8CfI4vxjzkvARSZv3pa+JrFAoqYmEA3VPiAvzrn+P0zi0D5jEPkKngW2KTpq6r3GfdDNw==", + "dev": true, + "requires": { + "array-find": "^1.0.0", + "dedent": "^0.7.0", + "del": "^5.1.0", + "execa": "^3.3.0", + "graceful-fs": "^4.1.11", + "linux-platform-info": "^0.0.3", + "lodash": "^4.17.15", + "mkdirp": "^0.5.1", + "mustache": "^2.1.2", + "nanoid": "^2.1.3", + "os-family": "^1.0.0", + "pify": "^2.3.0", + "pinkie": "^2.0.1", + "read-file-relative": "^1.2.0", + "which-promise": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "nanoid": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "testcafe-hammerhead": { + "version": "17.1.9", + "resolved": "https://registry.npmjs.org/testcafe-hammerhead/-/testcafe-hammerhead-17.1.9.tgz", + "integrity": "sha512-RxlUsCkspdrWEWb9S2n9hwqX2RwRp5CFjRTN7Ijx+vnCcHKn5VPBhDlSrZHRiQT1yfa9+FudNsjmmrmlAGvCSg==", + "dev": true, + "requires": { + "acorn-hammerhead": "^0.3.0", + "asar": "^2.0.1", + "bowser": "1.6.0", + "brotli": "^1.3.1", + "crypto-md5": "^1.0.0", + "css": "2.2.3", + "debug": "4.1.1", + "esotope-hammerhead": "0.5.5", + "iconv-lite": "0.5.1", + "lodash": "^4.17.13", + "lru-cache": "2.6.3", + "match-url-wildcard": "0.0.4", + "merge-stream": "^1.0.1", + "mime": "~1.4.1", + "mustache": "^2.1.1", + "nanoid": "^0.2.2", + "os-family": "^1.0.0", + "parse5": "2.2.3", + "pinkie": "1.0.0", + "read-file-relative": "^1.2.0", + "semver": "5.5.0", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "webauth": "^1.1.0" + }, + "dependencies": { + "bowser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.6.0.tgz", + "integrity": "sha1-N/w4e2Fstq7zcNq01r1AK3TFxU0=", + "dev": true + }, + "iconv-lite": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.1.tgz", + "integrity": "sha512-ONHr16SQvKZNSqjQT9gy5z24Jw+uqfO02/ngBSBoqChZ+W8qXX7GPRa1RoUnzGADw8K63R1BXUMzarCVQBpY8Q==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "lru-cache": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.3.tgz", + "integrity": "sha1-UczQtPwMhDWH16VwnOTTt2Kb7cU=", + "dev": true + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + }, + "nanoid": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-0.2.2.tgz", + "integrity": "sha512-GHoRrvNEKiwdkwQ/enKL8AhQkkrBC/2KxMZkDvQzp8OtkpX8ZAmoYJWFVl7l8F2+HcEJUfdg21Ab2wXXfrvACQ==", + "dev": true + }, + "parse5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-2.2.3.tgz", + "integrity": "sha1-DE/EHBAAxea5PUiwP4CDg3g06fY=", + "dev": true + }, + "pinkie": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-1.0.0.tgz", + "integrity": "sha1-Wkfyi6EBXQIBvae/DzWOR77Ix+Q=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "dev": true, + "requires": { + "punycode": "^1.4.1" + } + } + } + }, + "testcafe-legacy-api": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/testcafe-legacy-api/-/testcafe-legacy-api-4.0.0.tgz", + "integrity": "sha512-Tn+YEH8hqDPQs/1/d+A9G+FdfejougtoWX0wRxrLq5ECYy2qxwH8p9EGDUNatLm0IFIumVpcz2tSZkvRpfKLSg==", + "dev": true, + "requires": { + "async": "0.2.6", + "babel-runtime": "^5.8.34", + "dedent": "^0.6.0", + "highlight-es": "^1.0.0", + "is-jquery-obj": "^0.1.0", + "lodash": "^4.14.0", + "moment": "^2.14.1", + "mustache": "^2.2.1", + "os-family": "^1.0.0", + "parse5": "^2.1.5", + "pify": "^2.3.0", + "pinkie": "^2.0.1", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "async": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.6.tgz", + "integrity": "sha1-rT83PZJJrjJIgVZVgryQ4VKrvWg=", + "dev": true + }, + "babel-runtime": { + "version": "5.8.38", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.38.tgz", + "integrity": "sha1-HAsC62MxL18If/IEUIJ7QlydTBk=", + "dev": true, + "requires": { + "core-js": "^1.0.0" + } + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", + "dev": true + }, + "dedent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.6.0.tgz", + "integrity": "sha1-Dm2o8M5Sg471zsXI+TlrDBtko8s=", + "dev": true + }, + "parse5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-2.2.3.tgz", + "integrity": "sha1-DE/EHBAAxea5PUiwP4CDg3g06fY=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "testcafe-reporter-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/testcafe-reporter-json/-/testcafe-reporter-json-2.2.0.tgz", + "integrity": "sha512-wfpNaZgGP2WoqdmnIXOyxcpwSzdH1HvzXSN397lJkXOrQrwhuGUThPDvyzPnZqxZSzXdDUvIPJm55tCMWbfymQ==", + "dev": true + }, + "testcafe-reporter-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/testcafe-reporter-list/-/testcafe-reporter-list-2.1.0.tgz", + "integrity": "sha1-n6ifcbl9Pf5ktDAtXiJ97mmuxrk=", + "dev": true + }, + "testcafe-reporter-minimal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/testcafe-reporter-minimal/-/testcafe-reporter-minimal-2.1.0.tgz", + "integrity": "sha1-Z28DVHY0FDxurzq1KGgnOkvr9CE=", + "dev": true + }, + "testcafe-reporter-spec": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/testcafe-reporter-spec/-/testcafe-reporter-spec-2.1.1.tgz", + "integrity": "sha1-gVb87Q9RMkhlWa1WC8gGdkaSdew=", + "dev": true + }, + "testcafe-reporter-xunit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/testcafe-reporter-xunit/-/testcafe-reporter-xunit-2.1.0.tgz", + "integrity": "sha1-5tZsVyzhWvJmcGrw/WELKoQd1EM=", + "dev": true + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -20233,6 +22631,12 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "time-limit-promise": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/time-limit-promise/-/time-limit-promise-1.0.4.tgz", + "integrity": "sha512-FLHDDsIDducw7MBcRWlFtW2Tm50DoKOSFf0Nzx17qwXj8REXCte0eUkHrJl9QU3Bl9arG3XNYX0PcHpZ9xyuLw==", + "dev": true + }, "timers-browserify": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", @@ -20277,6 +22681,36 @@ "os-tmpdir": "~1.0.2" } }, + "tmp-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-1.1.0.tgz", + "integrity": "sha512-8+Ah9aB1IRXCnIOxXZ0uFozV1nMU5xiu7hhFVUSxZ3bYu+psD4TzagCzVbexUCgNNGJnsmNDQlS4nG3mTyoNkw==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "tmp": "0.1.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dev": true, + "requires": { + "rimraf": "^2.6.3" + } + } + } + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -20373,6 +22807,12 @@ "punycode": "^2.1.0" } }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, "treeify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", @@ -20399,6 +22839,15 @@ "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "dev": true, + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, "ts-dedent": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-1.1.0.tgz", @@ -20548,6 +22997,12 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", @@ -20603,6 +23058,12 @@ } } }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, "unfetch": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz", @@ -20952,6 +23413,12 @@ "use-force-update": "^1.0.5" } }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", + "dev": true + }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -21305,6 +23772,12 @@ "minimalistic-assert": "^1.0.0" } }, + "webauth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/webauth/-/webauth-1.1.0.tgz", + "integrity": "sha1-ZHBPa4AmmGYFvDymKZUubib90QA=", + "dev": true + }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -21768,6 +24241,40 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-promise": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-promise/-/which-promise-1.0.0.tgz", + "integrity": "sha1-ILch3wWzW3Bhdv+hCwkJq6RgMDU=", + "dev": true, + "requires": { + "pify": "^2.2.0", + "pinkie-promise": "^1.0.0", + "which": "^1.1.2" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-1.0.0.tgz", + "integrity": "sha1-Wkfyi6EBXQIBvae/DzWOR77Ix+Q=", + "dev": true + }, + "pinkie-promise": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-1.0.0.tgz", + "integrity": "sha1-0dpn9UglY7t89X8oauKCLs+/NnA=", + "dev": true, + "requires": { + "pinkie": "^1.0.0" + } + } + } + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", diff --git a/package.json b/package.json index 2e2542371..a03c260ba 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,9 @@ "@babel/preset-react": "^7.7.4", "@babel/preset-typescript": "^7.7.4", "@babel/runtime": "^7.7.6", + "@pollyjs/adapter-node-http": "^5.0.0", + "@pollyjs/core": "^5.0.0", + "@pollyjs/persister-fs": "^5.0.0", "@storybook/addon-storyshots": "^5.2.8", "@storybook/react": "^5.1.9", "@testing-library/react-hooks": "^1.1.0", @@ -93,6 +96,8 @@ "@types/jest": "^24.0.24", "@types/lodash-es": "^4.17.3", "@types/moment-timezone": "^0.5.12", + "@types/pollyjs__adapter-node-http": "^2.0.0", + "@types/pollyjs__persister-fs": "^2.0.0", "@types/react": "^16.9.16", "@types/react-dom": "^16.8.5", "@types/react-dropzone": "^4.2.2", @@ -135,6 +140,7 @@ "jest-file": "^1.0.0", "lint-staged": "^9.4.2", "mock-apollo-client": "^0.4.0", + "node-fetch": "^2.6.0", "prettier": "^1.19.1", "react-intl-translations-manager": "^5.0.3", "react-test-renderer": "^16.12.0", @@ -142,6 +148,8 @@ "require-context.macro": "^1.1.1", "rimraf": "^3.0.0", "start-server-and-test": "^1.11.0", + "setup-polly-jest": "^0.9.0", + "testcafe": "^1.3.3", "ts-jest": "^24.2.0", "tsconfig-paths-webpack-plugin": "^3.2.0", "webpack": "^4.35.3", diff --git a/src/auth/AuthProvider.test.ts b/src/auth/AuthProvider.test.ts new file mode 100644 index 000000000..e69de29bb diff --git a/testUtils/api.ts b/testUtils/api.ts new file mode 100644 index 000000000..6792ef5ea --- /dev/null +++ b/testUtils/api.ts @@ -0,0 +1,43 @@ +import NodeHttpAdapter from "@pollyjs/adapter-node-http"; +import { Polly } from "@pollyjs/core"; +import FSPersister from "@pollyjs/persister-fs"; +import { InMemoryCache } from "apollo-cache-inmemory"; +import ApolloClient from "apollo-client"; +import { BatchHttpLink } from "apollo-link-batch-http"; +import fetch from "node-fetch"; +import path from "path"; +import { setupPolly } from "setup-polly-jest"; + +function setupApi() { + Polly.register(NodeHttpAdapter); + Polly.register(FSPersister); + setupPolly({ + adapterOptions: { + fetch: { + context: global + } + }, + adapters: ["node-http"], + persister: "fs", + persisterOptions: { + fs: { + recordingsDir: path.resolve(__dirname, "../recordings") + } + }, + recordIfMissing: true + }); + const cache = new InMemoryCache(); + const link = new BatchHttpLink({ + // @ts-ignore + fetch, + uri: "http://localhost:8000/graphql/" + }); + const apolloClient = new ApolloClient({ + cache, + link + }); + + return apolloClient; +} + +export default setupApi; From 41e60b6418f6dbd24a03df3248d035c4595fbbfa Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Tue, 21 Jul 2020 15:55:50 +0200 Subject: [PATCH 10/23] Add tests to auth --- package-lock.json | 102 +++++++++----- package.json | 14 +- .../recording.har | 128 ++++++++++++++++++ .../recording.har | 128 ++++++++++++++++++ .../recording.har | 128 ++++++++++++++++++ .../recording.har | 128 ++++++++++++++++++ src/auth/AuthProvider.test.ts | 76 +++++++++++ src/auth/AuthProvider.tsx | 30 ++-- src/utils/credentialsManagement.ts | 6 +- testUtils/api.ts | 13 +- 10 files changed, 693 insertions(+), 60 deletions(-) create mode 100644 recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har create mode 100644 recordings/User_3768991250/will-not-be-logged-if-has-expired-token_544348836/recording.har create mode 100644 recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har create mode 100644 recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har diff --git a/package-lock.json b/package-lock.json index a2c287562..863477392 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2723,33 +2723,33 @@ "integrity": "sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw==" }, "@pollyjs/adapter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@pollyjs/adapter/-/adapter-5.0.0.tgz", - "integrity": "sha512-GaJUp9hKKKbRbh1FDbjVfxNGIYs8/1QQI5SR+zjz+OjpjV2btRPFCq1cqO4ORrHA2pTIC8IvEL3lgPSG4v/cPg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@pollyjs/adapter/-/adapter-4.3.0.tgz", + "integrity": "sha512-8/kELw/esDY+Mi6xiYRSX3EHKoVmPeqjRYk7DHFbHhXoMs6ENrPS0ay8Ajl0KHppAnutTk4YLqaI/3VqFCR5iw==", "dev": true, "requires": { - "@pollyjs/utils": "^5.0.0" + "@pollyjs/utils": "^4.3.0" } }, "@pollyjs/adapter-node-http": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@pollyjs/adapter-node-http/-/adapter-node-http-5.0.0.tgz", - "integrity": "sha512-9WjV6UVHWpcDZc1Dt5Slxn191iN55RpCirEAdt+h5wvPgRJmsLuXMI1vT3r9va8Z+a3qCqM458waNfr5us2Wuw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@pollyjs/adapter-node-http/-/adapter-node-http-4.3.0.tgz", + "integrity": "sha512-13LC4/9a6rv8Av0qMM2k+MhVm9Txxai8aLZQabX0XTbnY9e+4yxeIdBC96HAgzmHqWlq30r8Tkgypu3agiKA9w==", "dev": true, "requires": { - "@pollyjs/adapter": "^5.0.0", - "@pollyjs/utils": "^5.0.0", + "@pollyjs/adapter": "^4.3.0", + "@pollyjs/utils": "^4.3.0", "lodash-es": "^4.17.11", "nock": "^12.0.3" } }, "@pollyjs/core": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@pollyjs/core/-/core-5.0.0.tgz", - "integrity": "sha512-f/v5z7aKSWdeBCZQFiQGL4aZmdWAuQWV+U/fMbAIEYjkX8Av0gWPhkFyeMfeFriYMg7Ts1XxPNI7LYroCnjn7w==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@pollyjs/core/-/core-4.3.0.tgz", + "integrity": "sha512-8Fs0Lg19P39oO/GvzWoC/fkckrCSx9rL5DPmU4Ez/Q+WXnDzmWtHI1x/jmD+jv5JLc2ig/ueWugIpE0f9eZbAQ==", "dev": true, "requires": { - "@pollyjs/utils": "^5.0.0", + "@pollyjs/utils": "^4.3.0", "@sindresorhus/fnv1a": "^1.2.0", "blueimp-md5": "^2.10.0", "fast-json-stable-stringify": "^2.0.0", @@ -2760,12 +2760,12 @@ } }, "@pollyjs/node-server": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@pollyjs/node-server/-/node-server-5.0.0.tgz", - "integrity": "sha512-DK1hqsZnIU7TXECNPI6E+ojaSpv62lt3+8eGn3qFbsa61ToTFQLFEMNfcTWA6ZXArrpdzYAdKVTt8BsdEujHkg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@pollyjs/node-server/-/node-server-4.3.0.tgz", + "integrity": "sha512-pGrX889RkJXleW7p1cMKJqEtuasABbosneU9VrHOUYOMlYnObIOTt5y/n3m0NMAh9SyyusgqC7yQ18/PnMCgfg==", "dev": true, "requires": { - "@pollyjs/utils": "^5.0.0", + "@pollyjs/utils": "^4.3.0", "body-parser": "^1.19.0", "cors": "^2.8.5", "express": "^4.17.1", @@ -2789,12 +2789,12 @@ } }, "@pollyjs/persister": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@pollyjs/persister/-/persister-5.0.0.tgz", - "integrity": "sha512-bTJes0c2/y4xrXi1vPypJhup/N/VPC1jY7maOuSv4pOFo6VAvoVJGT2sgMMnPU7qHSRn9Y6v+S7QqGtLOZCyAA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@pollyjs/persister/-/persister-4.3.0.tgz", + "integrity": "sha512-oKTl++rZdT/5tMoeHMsgUAHjnRf/4qLNv7kc7u8ddldjgZ4eDAIWlG6BwFkhDxjhV+ofVlnLxFnRjmK4qrrmqA==", "dev": true, "requires": { - "@pollyjs/utils": "^5.0.0", + "@pollyjs/utils": "^4.3.0", "bowser": "^2.4.0", "fast-json-stable-stringify": "^2.0.0", "lodash-es": "^4.17.11", @@ -2803,19 +2803,19 @@ } }, "@pollyjs/persister-fs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@pollyjs/persister-fs/-/persister-fs-5.0.0.tgz", - "integrity": "sha512-ju75JJ6sSJ/q68rB74wxIlvXCq/riuCqW3pS/Ucd4SFfks/HxXxM3yGTHn0Qq5SrmY9cYbQFfpOz0WCNDIqr+A==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@pollyjs/persister-fs/-/persister-fs-4.3.0.tgz", + "integrity": "sha512-2DUIyl/3mr5F1Orq6bnpW6Witz2I8ulMGsqCFrlh4LaA4DlSg2SwOv88bbjzMCfqGP1CN3U5Zu8o7tZlFxkd8w==", "dev": true, "requires": { - "@pollyjs/node-server": "^5.0.0", - "@pollyjs/persister": "^5.0.0" + "@pollyjs/node-server": "^4.3.0", + "@pollyjs/persister": "^4.3.0" } }, "@pollyjs/utils": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@pollyjs/utils/-/utils-5.0.0.tgz", - "integrity": "sha512-zXoR13NGR1fVoUAcKR9/8AYCXZsMkG5EdvTZbR1nk5hCYJN1AR73HAKXMPsk/2vkLnBr6LWoAr3f9LjwqJOehQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@pollyjs/utils/-/utils-4.3.0.tgz", + "integrity": "sha512-8BYWtP4nK6mAlDgZQ8j2+EZjQH2beoHaHnZDyG7GdspOLeILtXhHLeAxp371YSKN387t7OEBS0nIQSMppHuTQg==", "dev": true, "requires": { "qs": "^6.7.0", @@ -3855,6 +3855,29 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.6.tgz", "integrity": "sha512-FjsYUPzEJdGXjwKqSpE0/9QEh6kzhTAeObA54rn6j3rR4C/mzpI9L0KNfoeASSPMMdxIsoJuCLDWcM/rVjIsSA==" }, + "@types/node-fetch": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", + "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "@types/normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -4113,6 +4136,15 @@ "integrity": "sha512-wx2LQVvKlEkhXp/HoKIZ/aSL+TvfJdKco8i0xJS3aR877mg4qBHzNT6+B5a61vewZHo79EdZavskGnRXEC2H6A==", "dev": true }, + "@types/setup-polly-jest": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@types/setup-polly-jest/-/setup-polly-jest-0.5.0.tgz", + "integrity": "sha512-adGkOa52Z/zU6gZ6oU7iUyS6BOmueFUjPIXrW0INNZPZqB5p68Q6dukWVk2bqVXwDUy3Cd6B/aADSouClwwmew==", + "dev": true, + "requires": { + "@types/pollyjs__core": "*" + } + }, "@types/shallowequal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/shallowequal/-/shallowequal-1.1.1.tgz", @@ -15148,6 +15180,12 @@ "pretty-format": "^24.9.0" } }, + "jest-localstorage-mock": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/jest-localstorage-mock/-/jest-localstorage-mock-2.4.3.tgz", + "integrity": "sha512-UgifkHKoWVRUoSqO4Z4Z+Hl1NbiYBVDlmkmulFFeRRneGECWAlAdGWJdyz+2NisjOZnnQoxQl0s5dQ7ch62Jxw==", + "dev": true + }, "jest-matcher-utils": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", @@ -20510,9 +20548,9 @@ "dev": true }, "setup-polly-jest": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/setup-polly-jest/-/setup-polly-jest-0.9.1.tgz", - "integrity": "sha512-BbOxCvMnDHAPG9e3Qw3Ol4/ZWLwnDnhODqrfiIpAtF7Azu0O3PI4svZIfsh+/EV9xNj15LMkvchaWsDvF4A3hQ==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/setup-polly-jest/-/setup-polly-jest-0.8.0.tgz", + "integrity": "sha512-dY2vPM5c7mOoF7EbujEqapI6DgbG0x+yN/3cNW3coL7Kmypzu6Zwd978ssIzxAwjf9lXwhw8T9jMBjReDXf63A==", "dev": true }, "sha.js": { diff --git a/package.json b/package.json index a03c260ba..1d0ca3558 100644 --- a/package.json +++ b/package.json @@ -83,9 +83,9 @@ "@babel/preset-react": "^7.7.4", "@babel/preset-typescript": "^7.7.4", "@babel/runtime": "^7.7.6", - "@pollyjs/adapter-node-http": "^5.0.0", - "@pollyjs/core": "^5.0.0", - "@pollyjs/persister-fs": "^5.0.0", + "@pollyjs/adapter-node-http": "^4.3.0", + "@pollyjs/core": "^4.3.0", + "@pollyjs/persister-fs": "^4.3.0", "@storybook/addon-storyshots": "^5.2.8", "@storybook/react": "^5.1.9", "@testing-library/react-hooks": "^1.1.0", @@ -96,6 +96,7 @@ "@types/jest": "^24.0.24", "@types/lodash-es": "^4.17.3", "@types/moment-timezone": "^0.5.12", + "@types/node-fetch": "^2.5.7", "@types/pollyjs__adapter-node-http": "^2.0.0", "@types/pollyjs__persister-fs": "^2.0.0", "@types/react": "^16.9.16", @@ -108,6 +109,7 @@ "@types/react-sortable-tree": "^0.3.6", "@types/react-test-renderer": "^16.8.2", "@types/semver-compare": "^1.0.1", + "@types/setup-polly-jest": "^0.5.0", "@types/storybook__addon-storyshots": "^3.4.9", "@types/storybook__react": "^4.0.2", "@types/url-join": "^4.0.0", @@ -138,6 +140,7 @@ "husky": "^3.0.8", "jest": "^24.8.0", "jest-file": "^1.0.0", + "jest-localstorage-mock": "^2.4.3", "lint-staged": "^9.4.2", "mock-apollo-client": "^0.4.0", "node-fetch": "^2.6.0", @@ -148,7 +151,7 @@ "require-context.macro": "^1.1.1", "rimraf": "^3.0.0", "start-server-and-test": "^1.11.0", - "setup-polly-jest": "^0.9.0", + "setup-polly-jest": "^0.8.0", "testcafe": "^1.3.3", "ts-jest": "^24.2.0", "tsconfig-paths-webpack-plugin": "^3.2.0", @@ -160,6 +163,9 @@ "fsevents": "^1.2.9" }, "jest": { + "setupFiles": [ + "jest-localstorage-mock" + ], "transform": { "^.+\\.(jsx?|tsx?)$": "babel-jest", "^.+\\.(png|svg|jpe?g)$": "jest-file" diff --git a/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har b/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har new file mode 100644 index 000000000..af02d5788 --- /dev/null +++ b/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har @@ -0,0 +1,128 @@ +{ + "log": { + "_recordingName": "User/will be logged if has valid token", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "4.3.0" + }, + "entries": [ + { + "_id": "414f6b24b58b132c92e9a6ea67613a15", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 691, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-length", + "value": "691" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "localhost:8000" + } + ], + "headersSize": 254, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUyMzk4OTcsImV4cCI6MTU5NTI0MDE5NywidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.l-FnFDVmi5fASo7Uae2Emewu2pKyO2qLz7ZQl1fSzo4\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + }, + "queryString": [], + "url": "http://localhost:8000/graphql/" + }, + "response": { + "bodySize": 1619, + "content": { + "mimeType": "application/json", + "size": 1619, + "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1595239897, \"exp\": 1595240197, \"token\": \"qCRbkGN2zNOo\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyMQ==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}]" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Tue, 21 Jul 2020 10:59:19 GMT" + }, + { + "name": "server", + "value": "WSGIServer/0.2 CPython/3.8.1" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "POST, OPTIONS" + }, + { + "name": "access-control-allow-headers", + "value": "Origin, Content-Type, Accept, Authorization" + }, + { + "name": "content-length", + "value": "1619" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + } + ], + "headersSize": 316, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-07-21T10:59:19.225Z", + "time": 70, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 70 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/recordings/User_3768991250/will-not-be-logged-if-has-expired-token_544348836/recording.har b/recordings/User_3768991250/will-not-be-logged-if-has-expired-token_544348836/recording.har new file mode 100644 index 000000000..775ee5be6 --- /dev/null +++ b/recordings/User_3768991250/will-not-be-logged-if-has-expired-token_544348836/recording.har @@ -0,0 +1,128 @@ +{ + "log": { + "_recordingName": "User/will not be logged if has expired token", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "4.3.0" + }, + "entries": [ + { + "_id": "414f6b24b58b132c92e9a6ea67613a15", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 691, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-length", + "value": "691" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "localhost:8000" + } + ], + "headersSize": 254, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUyMzk4OTcsImV4cCI6MTU5NTI0MDE5NywidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.l-FnFDVmi5fASo7Uae2Emewu2pKyO2qLz7ZQl1fSzo4\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + }, + "queryString": [], + "url": "http://localhost:8000/graphql/" + }, + "response": { + "bodySize": 89, + "content": { + "mimeType": "application/json", + "size": 89, + "text": "[{\"data\": {\"tokenVerify\": {\"payload\": null, \"user\": null, \"__typename\": \"VerifyToken\"}}}]" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Tue, 21 Jul 2020 13:43:50 GMT" + }, + { + "name": "server", + "value": "WSGIServer/0.2 CPython/3.8.1" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "POST, OPTIONS" + }, + { + "name": "access-control-allow-headers", + "value": "Origin, Content-Type, Accept, Authorization" + }, + { + "name": "content-length", + "value": "89" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + } + ], + "headersSize": 314, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-07-21T13:43:50.249Z", + "time": 17, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 17 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har new file mode 100644 index 000000000..a4d7605fa --- /dev/null +++ b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har @@ -0,0 +1,128 @@ +{ + "log": { + "_recordingName": "User/will not be logged if has invalid token", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "4.3.0" + }, + "entries": [ + { + "_id": "f0343691dcc48a40921887f4f58c55b6", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 692, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-length", + "value": "692" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "localhost:8000" + } + ], + "headersSize": 254, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUyMzk4OTcsImV4cCI6MTU5NTI0MDE5NywidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.l-FnFDVmi5fASo7Uae2Emewu2pKyO2qLz7ZQl1fSzo41\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + }, + "queryString": [], + "url": "http://localhost:8000/graphql/" + }, + "response": { + "bodySize": 89, + "content": { + "mimeType": "application/json", + "size": 89, + "text": "[{\"data\": {\"tokenVerify\": {\"payload\": null, \"user\": null, \"__typename\": \"VerifyToken\"}}}]" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Tue, 21 Jul 2020 11:52:05 GMT" + }, + { + "name": "server", + "value": "WSGIServer/0.2 CPython/3.8.1" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "POST, OPTIONS" + }, + { + "name": "access-control-allow-headers", + "value": "Origin, Content-Type, Accept, Authorization" + }, + { + "name": "content-length", + "value": "89" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + } + ], + "headersSize": 314, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-07-21T11:52:05.050Z", + "time": 169, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 169 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har new file mode 100644 index 000000000..5e16f41bc --- /dev/null +++ b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har @@ -0,0 +1,128 @@ +{ + "log": { + "_recordingName": "User/will not be logged in if doesn't have valid credentials", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "4.3.0" + }, + "entries": [ + { + "_id": "8e6a0aa8632ed8e653d2c35119288634", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 1263, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-length", + "value": "1263" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "localhost:8000" + } + ], + "headersSize": 255, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUyNTc4MjQsImV4cCI6MTU5NTI1ODEyNCwidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.-zPNOLaP5zLOcMW1Xb6G5mf_95J24oBtb07wSRddVOw\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"},{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin1\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n token\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + }, + "queryString": [], + "url": "http://localhost:8000/graphql/" + }, + "response": { + "bodySize": 1814, + "content": { + "mimeType": "application/json", + "size": 1814, + "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1595257824, \"exp\": 1595258124, \"token\": \"qCRbkGN2zNOo\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyMQ==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}, {\"data\": {\"tokenCreate\": {\"token\": null, \"errors\": [{\"field\": \"email\", \"message\": \"Please, enter valid credentials\", \"__typename\": \"AccountError\"}], \"user\": null, \"__typename\": \"CreateToken\"}}}]" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Mon, 20 Jul 2020 15:12:28 GMT" + }, + { + "name": "server", + "value": "WSGIServer/0.2 CPython/3.8.1" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "POST, OPTIONS" + }, + { + "name": "access-control-allow-headers", + "value": "Origin, Content-Type, Accept, Authorization" + }, + { + "name": "content-length", + "value": "1814" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + } + ], + "headersSize": 316, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-07-20T15:12:28.390Z", + "time": 146, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 146 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/src/auth/AuthProvider.test.ts b/src/auth/AuthProvider.test.ts index e69de29bb..9b10fd6df 100644 --- a/src/auth/AuthProvider.test.ts +++ b/src/auth/AuthProvider.test.ts @@ -0,0 +1,76 @@ +import setupApi from "@test/api"; +import { act, renderHook } from "@testing-library/react-hooks"; +import ApolloClient from "apollo-client"; + +import { useAuthProvider } from "./AuthProvider"; +import { setAuthToken } from "./utils"; + +const apolloClient = setupApi(); + +function renderAuthProvider(apolloClient: ApolloClient) { + const intl = { + formatMessage: ({ defaultMessage }) => defaultMessage + }; + const notify = jest.fn(); + + const { result } = renderHook(() => + useAuthProvider(intl as any, notify, apolloClient) + ); + + return result; +} + +const credentials = { + email: "admin@example.com", + password: "admin", + token: + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUyMzk4OTcsImV4cCI6MTU5NTI0MDE5NywidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.l-FnFDVmi5fASo7Uae2Emewu2pKyO2qLz7ZQl1fSzo4" +}; + +beforeEach(() => { + localStorage.clear(); +}); + +describe("User", () => { + it("will be logged in if has valid credentials", async done => { + const hook = renderAuthProvider(apolloClient); + + await act(() => + hook.current.login(credentials.email, credentials.password) + ); + expect(hook.current.userContext.email).toBe(credentials.email); + + done(); + }); + + it("will not be logged in if doesn't have valid credentials", async done => { + const hook = renderAuthProvider(apolloClient); + + await act(() => + hook.current.login(credentials.email, credentials.password + "1") + ); + expect(hook.current.userContext).toBe(null); + + done(); + }); + + it("will be logged if has valid token", async done => { + setAuthToken(credentials.token, false); + const hook = renderAuthProvider(apolloClient); + + await act(() => hook.current.autologinPromise.current); + expect(hook.current.userContext.email).toBe(credentials.email); + + done(); + }); + + it("will not be logged if has invalid token", async done => { + setAuthToken(credentials.token + "1", false); + const hook = renderAuthProvider(apolloClient); + + await act(() => hook.current.autologinPromise.current); + expect(hook.current.userContext).toBe(undefined); + + done(); + }); +}); diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index 57e695345..a059e4aa6 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -9,7 +9,7 @@ import { saveCredentials } from "@saleor/utils/credentialsManagement"; import ApolloClient from "apollo-client"; -import React, { useContext, useEffect, useState } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react"; import { useApolloClient, useMutation } from "react-apollo"; import { IntlShape, useIntl } from "react-intl"; @@ -31,12 +31,13 @@ import { const persistToken = false; -function useAuthProvider( +export function useAuthProvider( intl: IntlShape, notify: IMessageContext, apolloClient: ApolloClient ) { const [userContext, setUserContext] = useState(undefined); + const autologinPromise = useRef>(); const logout = () => { setUserContext(undefined); @@ -111,21 +112,25 @@ function useAuthProvider( useEffect(() => { const token = getAuthToken(); if (!!token && !userContext) { - verifyToken(token); + autologinPromise.current = tokenVerify({ variables: { token } }); } else { - loginWithCredentialsManagementAPI(login); + autologinPromise.current = loginWithCredentialsManagementAPI(login); } }, []); const login = async (email: string, password: string) => { - tokenAuth({ variables: { email, password } }).then(result => { - if (result && !result.data.tokenCreate.errors.length) { - if (!!onLogin) { - onLogin(); - } - saveCredentials(result.data.tokenCreate.user, password); + const result = await tokenAuth({ variables: { email, password } }); + + if (result && !result.data.tokenCreate.errors.length) { + if (!!onLogin) { + onLogin(); } - }); + saveCredentials(result.data.tokenCreate.user, password); + + return result.data.tokenCreate.user; + } + + return null; }; const loginByToken = (token: string, user: User) => { @@ -133,8 +138,6 @@ function useAuthProvider( setAuthToken(token, persistToken); }; - const verifyToken = (token: string) => tokenVerify({ variables: { token } }); - const refreshToken = async () => { const token = getAuthToken(); @@ -144,6 +147,7 @@ function useAuthProvider( }; return { + autologinPromise, login, loginByToken, logout, diff --git a/src/utils/credentialsManagement.ts b/src/utils/credentialsManagement.ts index e87aa9679..7f7998caa 100644 --- a/src/utils/credentialsManagement.ts +++ b/src/utils/credentialsManagement.ts @@ -3,14 +3,16 @@ import { User } from "@saleor/fragments/types/User"; export const isSupported = navigator.credentials && navigator.credentials.preventSilentAccess; -export function login(loginFn: (id: string, password: string) => void) { +export function login(loginFn: (id: string, password: string) => T): T { if (isSupported) { navigator.credentials.get({ password: true }).then(credential => { if (credential instanceof PasswordCredential) { - loginFn(credential.id, credential.password); + return loginFn(credential.id, credential.password); } }); } + + return null; } export function saveCredentials(user: User, password: string) { diff --git a/testUtils/api.ts b/testUtils/api.ts index 6792ef5ea..583f2a6de 100644 --- a/testUtils/api.ts +++ b/testUtils/api.ts @@ -8,23 +8,18 @@ import fetch from "node-fetch"; import path from "path"; import { setupPolly } from "setup-polly-jest"; +Polly.register(NodeHttpAdapter); +Polly.register(FSPersister); + function setupApi() { - Polly.register(NodeHttpAdapter); - Polly.register(FSPersister); setupPolly({ - adapterOptions: { - fetch: { - context: global - } - }, adapters: ["node-http"], persister: "fs", persisterOptions: { fs: { recordingsDir: path.resolve(__dirname, "../recordings") } - }, - recordIfMissing: true + } }); const cache = new InMemoryCache(); const link = new BatchHttpLink({ From 79ed2a3c2712951e44815eb64124ef53292eb8fa Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Tue, 21 Jul 2020 15:56:12 +0200 Subject: [PATCH 11/23] Add snapshots --- .../recording.har | 143 ++++++++++++++++++ .../recording.har | 14 +- 2 files changed, 150 insertions(+), 7 deletions(-) create mode 100644 recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har diff --git a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har new file mode 100644 index 000000000..8f337cb51 --- /dev/null +++ b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har @@ -0,0 +1,143 @@ +{ + "log": { + "_recordingName": "User/will be logged in if has valid credentials", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "4.3.0" + }, + "entries": [ + { + "_id": "5a18fed13283ef12aa42d45d206a87bd", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 572, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-length", + "value": "572" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "localhost:8000" + } + ], + "headersSize": 254, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n token\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + }, + "queryString": [], + "url": "http://localhost:8000/graphql/" + }, + "response": { + "bodySize": 1749, + "content": { + "mimeType": "application/json", + "size": 1749, + "text": "[{\"data\": {\"tokenCreate\": {\"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUzMzk3NDIsImV4cCI6MTU5NTM0MDA0MiwidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.hZ2hTVOU8h7fMf--Qm891DpV1hssicEaQShyy4sPKXM\", \"errors\": [], \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" + }, + "cookies": [ + { + "expires": "2020-08-20T13:55:42.000Z", + "httpOnly": true, + "maxAge": 2592000, + "name": "refreshToken", + "path": "/", + "secure": true, + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUzMzk3NDIsImV4cCI6MTU5NzkzMTc0MiwidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6ImZiRzlQR3FtR2dLZnRlTEJZNHIycW9kV0J0djczeDF6SEtnM3JVRXQweXdZT081bVk0dGRFcE9DRVYxVGpUREoifQ.OmbZJ6T-gseVTakIUNv2IP0rZ_t-W6TmV3Z99jiPF64" + } + ], + "headers": [ + { + "name": "date", + "value": "Tue, 21 Jul 2020 13:55:42 GMT" + }, + { + "name": "server", + "value": "WSGIServer/0.2 CPython/3.8.1" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "POST, OPTIONS" + }, + { + "name": "access-control-allow-headers", + "value": "Origin, Content-Type, Accept, Authorization" + }, + { + "name": "content-length", + "value": "1749" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUzMzk3NDIsImV4cCI6MTU5NzkzMTc0MiwidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6ImZiRzlQR3FtR2dLZnRlTEJZNHIycW9kV0J0djczeDF6SEtnM3JVRXQweXdZT081bVk0dGRFcE9DRVYxVGpUREoifQ.OmbZJ6T-gseVTakIUNv2IP0rZ_t-W6TmV3Z99jiPF64; expires=Thu, 20 Aug 2020 13:55:42 GMT; HttpOnly; Max-Age=2592000; Path=/; Secure" + } + ], + "headersSize": 804, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-07-21T13:55:42.298Z", + "time": 198, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 198 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har index 5e16f41bc..d2c6bb9b2 100644 --- a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har @@ -8,7 +8,7 @@ }, "entries": [ { - "_id": "8e6a0aa8632ed8e653d2c35119288634", + "_id": "482243508a181587879cd204a88524d7", "_order": 0, "cache": {}, "request": { @@ -56,7 +56,7 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUyNTc4MjQsImV4cCI6MTU5NTI1ODEyNCwidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.-zPNOLaP5zLOcMW1Xb6G5mf_95J24oBtb07wSRddVOw\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"},{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin1\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n token\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUzMzk3NDIsImV4cCI6MTU5NTM0MDA0MiwidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.hZ2hTVOU8h7fMf--Qm891DpV1hssicEaQShyy4sPKXM\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"},{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin1\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n token\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" @@ -66,13 +66,13 @@ "content": { "mimeType": "application/json", "size": 1814, - "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1595257824, \"exp\": 1595258124, \"token\": \"qCRbkGN2zNOo\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyMQ==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}, {\"data\": {\"tokenCreate\": {\"token\": null, \"errors\": [{\"field\": \"email\", \"message\": \"Please, enter valid credentials\", \"__typename\": \"AccountError\"}], \"user\": null, \"__typename\": \"CreateToken\"}}}]" + "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1595339742, \"exp\": 1595340042, \"token\": \"qCRbkGN2zNOo\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyMQ==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}, {\"data\": {\"tokenCreate\": {\"token\": null, \"errors\": [{\"field\": \"email\", \"message\": \"Please, enter valid credentials\", \"__typename\": \"AccountError\"}], \"user\": null, \"__typename\": \"CreateToken\"}}}]" }, "cookies": [], "headers": [ { "name": "date", - "value": "Mon, 20 Jul 2020 15:12:28 GMT" + "value": "Tue, 21 Jul 2020 13:55:42 GMT" }, { "name": "server", @@ -109,8 +109,8 @@ "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-20T15:12:28.390Z", - "time": 146, + "startedDateTime": "2020-07-21T13:55:42.541Z", + "time": 185, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +118,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 146 + "wait": 185 } } ], From 7be748eed7dff0612e800585ba48973cf9c874d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20=C5=BBegle=C5=84?= Date: Wed, 22 Jul 2020 15:29:21 +0200 Subject: [PATCH 12/23] Apply suggestions from code review Co-authored-by: Krzysztof Wolski --- src/auth/AuthProvider.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/AuthProvider.test.ts b/src/auth/AuthProvider.test.ts index 9b10fd6df..3ec9c9570 100644 --- a/src/auth/AuthProvider.test.ts +++ b/src/auth/AuthProvider.test.ts @@ -47,7 +47,7 @@ describe("User", () => { const hook = renderAuthProvider(apolloClient); await act(() => - hook.current.login(credentials.email, credentials.password + "1") + hook.current.login(credentials.email, "NotAValidPassword123!") ); expect(hook.current.userContext).toBe(null); @@ -65,7 +65,7 @@ describe("User", () => { }); it("will not be logged if has invalid token", async done => { - setAuthToken(credentials.token + "1", false); + setAuthToken("NotAToken", false); const hook = renderAuthProvider(apolloClient); await act(() => hook.current.autologinPromise.current); From 987315edb2286e3378892900777d33b988613fd4 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Tue, 21 Jul 2020 18:49:20 +0200 Subject: [PATCH 13/23] Fix endless loading screen --- src/auth/index.tsx | 13 ++----------- src/index.tsx | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/auth/index.tsx b/src/auth/index.tsx index 447631f8d..9a5349b2c 100644 --- a/src/auth/index.tsx +++ b/src/auth/index.tsx @@ -3,7 +3,6 @@ import React from "react"; import { Route, Switch } from "react-router-dom"; import Layout from "./components/Layout"; -import LoginLoading from "./components/LoginLoading"; import { newPasswordPath, passwordResetPath, @@ -33,20 +32,12 @@ export const UserContext = React.createContext({ tokenVerifyLoading: false }); -interface AuthRouterProps { - hasToken: boolean; -} - -const AuthRouter: React.FC = ({ hasToken }) => ( +const AuthRouter: React.FC = () => ( - {!hasToken ? ( - - ) : ( - - )} + diff --git a/src/index.tsx b/src/index.tsx index 4bf643631..2118f7298 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -293,7 +293,7 @@ const Routes: React.FC = () => { ) : hasToken && tokenVerifyLoading ? ( ) : ( - + )} ); From 2823dee1614fc1263b34a90b7eb05853baa5040d Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Tue, 21 Jul 2020 18:51:36 +0200 Subject: [PATCH 14/23] Fix logging out if token has expired --- src/auth/errors.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/auth/errors.ts b/src/auth/errors.ts index c69adc137..4ced5f7a0 100644 --- a/src/auth/errors.ts +++ b/src/auth/errors.ts @@ -2,8 +2,9 @@ import { findValueInEnum } from "@saleor/misc"; import { GraphQLError } from "graphql"; export enum JWTError { - invalid = "JSONWebTokenError", - expired = "JSONWebTokenExpired" + invalid = "InvalidTokenError", + invalidSignature = "InvalidSignatureError", + expired = "ExpiredSignatureError" } export function isJwtError(error: GraphQLError): boolean { From 765953c9a87305b2a99d2cc1d7461a85e4be827f Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Tue, 21 Jul 2020 19:08:27 +0200 Subject: [PATCH 15/23] Sort keys --- src/orders/components/OrderHistory/OrderHistory.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/orders/components/OrderHistory/OrderHistory.tsx b/src/orders/components/OrderHistory/OrderHistory.tsx index 68f8fc588..dcdc59478 100644 --- a/src/orders/components/OrderHistory/OrderHistory.tsx +++ b/src/orders/components/OrderHistory/OrderHistory.tsx @@ -100,8 +100,8 @@ const getEventMessage = (event: OrderDetails_order_events, intl: IntlShape) => { description: "order history message" }, { - invoiceNumber: event.invoiceNumber, - generatedBy: event.user ? event.user.email : null + generatedBy: event.user ? event.user.email : null, + invoiceNumber: event.invoiceNumber } ); case OrderEventsEnum.INVOICE_UPDATED: From d23202bf004ab3dffb6e215ba7e83cc4ab20ea30 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Thu, 23 Jul 2020 15:37:39 +0200 Subject: [PATCH 16/23] Handle token refreshing --- .../recording.har | 34 ++++++------ .../recording.har | 28 +++++----- src/auth/AuthProvider.test.ts | 1 + src/auth/AuthProvider.tsx | 43 ++++++++++----- src/auth/errors.ts | 4 ++ src/auth/index.tsx | 4 +- src/auth/link.ts | 39 +++++++++++++ src/auth/mutations.ts | 5 +- src/auth/types/SetPassword.ts | 2 + src/auth/types/TokenAuth.ts | 3 +- src/auth/utils.ts | 45 +++++++++++---- src/auth/views/NewPassword.tsx | 6 +- src/hooks/makeQuery.ts | 55 +++++++++++-------- src/index.tsx | 38 ++----------- src/queries.tsx | 47 ++++++++++------ 15 files changed, 220 insertions(+), 134 deletions(-) create mode 100644 src/auth/link.ts diff --git a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har index 8f337cb51..f02c738f0 100644 --- a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har +++ b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "5a18fed13283ef12aa42d45d206a87bd", + "_id": "ec3f8ec4f0f88fc421dc0d46281d4515", "_order": 0, "cache": {}, "request": { - "bodySize": 572, + "bodySize": 587, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "572" + "value": "587" }, { "_fromType": "array", @@ -56,33 +56,33 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n token\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" }, "response": { - "bodySize": 1749, + "bodySize": 1830, "content": { "mimeType": "application/json", - "size": 1749, - "text": "[{\"data\": {\"tokenCreate\": {\"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUzMzk3NDIsImV4cCI6MTU5NTM0MDA0MiwidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.hZ2hTVOU8h7fMf--Qm891DpV1hssicEaQShyy4sPKXM\", \"errors\": [], \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" + "size": 1830, + "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"oQ3yHRrBRRtQNVuJgn4D6Txh3aPWC8fl91XMcA2bukbgkdUotAEAJbAcCvTsXn3Z\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTU0MDk1ODUsImV4cCI6MTU5NTQwOTU5MCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.jv57hd44KxRNMKO6IcP5bD7Upg2rnZ1fzXmsk0yAZDg\", \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" }, "cookies": [ { - "expires": "2020-08-20T13:55:42.000Z", + "expires": "2020-07-22T09:49:45.000Z", "httpOnly": true, - "maxAge": 2592000, + "maxAge": 1800, "name": "refreshToken", "path": "/", "secure": true, - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUzMzk3NDIsImV4cCI6MTU5NzkzMTc0MiwidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6ImZiRzlQR3FtR2dLZnRlTEJZNHIycW9kV0J0djczeDF6SEtnM3JVRXQweXdZT081bVk0dGRFcE9DRVYxVGpUREoifQ.OmbZJ6T-gseVTakIUNv2IP0rZ_t-W6TmV3Z99jiPF64" + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTU0MDk1ODUsImV4cCI6MTU5NTQxMTM4NSwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6Im9RM3lIUnJCUlJ0UU5WdUpnbjRENlR4aDNhUFdDOGZsOTFYTWNBMmJ1a2Jna2RVb3RBRUFKYkFjQ3ZUc1huM1oifQ.KeIbqoHp-XPFfny6tlq6EUA7BojWAnNcpePMtI2X4qo" } ], "headers": [ { "name": "date", - "value": "Tue, 21 Jul 2020 13:55:42 GMT" + "value": "Wed, 22 Jul 2020 09:19:45 GMT" }, { "name": "server", @@ -106,7 +106,7 @@ }, { "name": "content-length", - "value": "1749" + "value": "1830" }, { "name": "x-content-type-options", @@ -115,17 +115,17 @@ { "_fromType": "array", "name": "set-cookie", - "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUzMzk3NDIsImV4cCI6MTU5NzkzMTc0MiwidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6ImZiRzlQR3FtR2dLZnRlTEJZNHIycW9kV0J0djczeDF6SEtnM3JVRXQweXdZT081bVk0dGRFcE9DRVYxVGpUREoifQ.OmbZJ6T-gseVTakIUNv2IP0rZ_t-W6TmV3Z99jiPF64; expires=Thu, 20 Aug 2020 13:55:42 GMT; HttpOnly; Max-Age=2592000; Path=/; Secure" + "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTU0MDk1ODUsImV4cCI6MTU5NTQxMTM4NSwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6Im9RM3lIUnJCUlJ0UU5WdUpnbjRENlR4aDNhUFdDOGZsOTFYTWNBMmJ1a2Jna2RVb3RBRUFKYkFjQ3ZUc1huM1oifQ.KeIbqoHp-XPFfny6tlq6EUA7BojWAnNcpePMtI2X4qo; expires=Wed, 22 Jul 2020 09:49:45 GMT; HttpOnly; Max-Age=1800; Path=/; Secure" } ], - "headersSize": 804, + "headersSize": 801, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-21T13:55:42.298Z", - "time": 198, + "startedDateTime": "2020-07-22T09:19:45.127Z", + "time": 324, "timings": { "blocked": -1, "connect": -1, @@ -133,7 +133,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 198 + "wait": 324 } } ], diff --git a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har index d2c6bb9b2..4673d4895 100644 --- a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "482243508a181587879cd204a88524d7", + "_id": "29fb7ad4777c005f81fdfd957c1c81af", "_order": 0, "cache": {}, "request": { - "bodySize": 1263, + "bodySize": 588, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "1263" + "value": "588" }, { "_fromType": "array", @@ -50,29 +50,29 @@ "value": "localhost:8000" } ], - "headersSize": 255, + "headersSize": 254, "httpVersion": "HTTP/1.1", "method": "POST", "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUzMzk3NDIsImV4cCI6MTU5NTM0MDA0MiwidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.hZ2hTVOU8h7fMf--Qm891DpV1hssicEaQShyy4sPKXM\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"},{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin1\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n token\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin1\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" }, "response": { - "bodySize": 1814, + "bodySize": 214, "content": { "mimeType": "application/json", - "size": 1814, - "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1595339742, \"exp\": 1595340042, \"token\": \"qCRbkGN2zNOo\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyMQ==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}, {\"data\": {\"tokenCreate\": {\"token\": null, \"errors\": [{\"field\": \"email\", \"message\": \"Please, enter valid credentials\", \"__typename\": \"AccountError\"}], \"user\": null, \"__typename\": \"CreateToken\"}}}]" + "size": 214, + "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [{\"field\": \"email\", \"message\": \"Please, enter valid credentials\", \"__typename\": \"AccountError\"}], \"csrfToken\": null, \"token\": null, \"user\": null, \"__typename\": \"CreateToken\"}}}]" }, "cookies": [], "headers": [ { "name": "date", - "value": "Tue, 21 Jul 2020 13:55:42 GMT" + "value": "Wed, 22 Jul 2020 09:21:11 GMT" }, { "name": "server", @@ -96,21 +96,21 @@ }, { "name": "content-length", - "value": "1814" + "value": "214" }, { "name": "x-content-type-options", "value": "nosniff" } ], - "headersSize": 316, + "headersSize": 315, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-21T13:55:42.541Z", - "time": 185, + "startedDateTime": "2020-07-22T09:21:11.006Z", + "time": 363, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +118,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 185 + "wait": 363 } } ], diff --git a/src/auth/AuthProvider.test.ts b/src/auth/AuthProvider.test.ts index 3ec9c9570..8a55ae399 100644 --- a/src/auth/AuthProvider.test.ts +++ b/src/auth/AuthProvider.test.ts @@ -29,6 +29,7 @@ const credentials = { beforeEach(() => { localStorage.clear(); + sessionStorage.clear(); }); describe("User", () => { diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index a059e4aa6..ad83dcea8 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -24,9 +24,10 @@ import { TokenAuth, TokenAuthVariables } from "./types/TokenAuth"; import { VerifyToken, VerifyTokenVariables } from "./types/VerifyToken"; import { displayDemoMessage, - getAuthToken, - removeAuthToken, - setAuthToken + getTokens, + removeTokens, + setAuthToken, + setTokens } from "./utils"; const persistToken = false; @@ -38,13 +39,14 @@ export function useAuthProvider( ) { const [userContext, setUserContext] = useState(undefined); const autologinPromise = useRef>(); + const refreshPromise = useRef>(); const logout = () => { setUserContext(undefined); if (isCredentialsManagementAPISupported) { navigator.credentials.preventSilentAccess(); } - removeAuthToken(); + removeTokens(); }; const [tokenAuth, tokenAuthResult] = useMutation< @@ -63,7 +65,11 @@ export function useAuthProvider( // `null`, because the LoginView uses this `null` to display error. setUserContext(user); if (user) { - setAuthToken(result.tokenCreate.token, persistToken); + setTokens( + result.tokenCreate.token, + result.tokenCreate.csrfToken, + persistToken + ); } }, onError: logout @@ -110,7 +116,7 @@ export function useAuthProvider( }; useEffect(() => { - const token = getAuthToken(); + const token = getTokens().auth; if (!!token && !userContext) { autologinPromise.current = tokenVerify({ variables: { token } }); } else { @@ -133,17 +139,28 @@ export function useAuthProvider( return null; }; - const loginByToken = (token: string, user: User) => { + const loginByToken = (auth: string, refresh: string, user: User) => { setUserContext(user); - setAuthToken(token, persistToken); + setTokens(auth, refresh, persistToken); }; - const refreshToken = async () => { - const token = getAuthToken(); + const refreshToken = (): Promise => { + if (!!refreshPromise.current) { + return refreshPromise.current; + } - const refreshData = await tokenRefresh({ variables: { token } }); + return new Promise(resolve => { + const token = getTokens().refresh; - setAuthToken(refreshData.data.tokenRefresh.token, persistToken); + return tokenRefresh({ variables: { token } }).then(refreshData => { + if (!!refreshData.data.tokenRefresh?.token) { + setAuthToken(refreshData.data.tokenRefresh.token, persistToken); + return resolve(true); + } + + return resolve(false); + }); + }); }; return { @@ -199,7 +216,7 @@ export const useAuth = () => { const isAuthenticated = !!user.user; return { - hasToken: !!getAuthToken(), + hasToken: !!getTokens(), isAuthenticated, tokenAuthLoading: user.tokenAuthLoading, tokenVerifyLoading: user.tokenVerifyLoading, diff --git a/src/auth/errors.ts b/src/auth/errors.ts index 4ced5f7a0..c42a2dfa2 100644 --- a/src/auth/errors.ts +++ b/src/auth/errors.ts @@ -11,6 +11,10 @@ export function isJwtError(error: GraphQLError): boolean { return !!findValueInEnum(error.extensions.exception.code, JWTError); } +export function isJwtExpiredError(error: GraphQLError): boolean { + return error.extensions.exception.code === JWTError.expired; +} + export function isTokenExpired(error: GraphQLError): boolean { return error.extensions.exception.code === JWTError.expired; } diff --git a/src/auth/index.tsx b/src/auth/index.tsx index 9a5349b2c..4865edc02 100644 --- a/src/auth/index.tsx +++ b/src/auth/index.tsx @@ -15,10 +15,10 @@ import ResetPasswordSuccess from "./views/ResetPasswordSuccess"; interface UserContext { login: (username: string, password: string) => void; - loginByToken: (token: string, user: User) => void; + loginByToken: (auth: string, csrf: string, user: User) => void; logout: () => void; tokenAuthLoading: boolean; - tokenRefresh: () => Promise; + tokenRefresh: () => Promise; tokenVerifyLoading: boolean; user?: User; } diff --git a/src/auth/link.ts b/src/auth/link.ts new file mode 100644 index 000000000..66c19a069 --- /dev/null +++ b/src/auth/link.ts @@ -0,0 +1,39 @@ +import { setContext } from "apollo-link-context"; +import { ErrorResponse, onError } from "apollo-link-error"; + +import { getTokens, removeTokens } from "./"; +import { isJwtError, JWTError } from "./errors"; + +interface ResponseError extends ErrorResponse { + networkError?: Error & { + statusCode?: number; + bodyText?: string; + }; +} + +export const invalidTokenLink = onError((error: ResponseError) => { + if ( + (error.networkError && error.networkError.statusCode === 401) || + error.graphQLErrors?.some(isJwtError) + ) { + if (error.graphQLErrors[0].extensions.code !== JWTError.expired) { + removeTokens(); + } + } +}); + +export const tokenLink = setContext((_, context) => { + const authToken = getTokens().auth; + + return { + ...context, + headers: { + ...context.headers, + Authorization: authToken ? `JWT ${authToken}` : null + } + }; +}); + +const link = invalidTokenLink.concat(tokenLink); + +export default link; diff --git a/src/auth/mutations.ts b/src/auth/mutations.ts index 3ae53b360..8c21ce32d 100644 --- a/src/auth/mutations.ts +++ b/src/auth/mutations.ts @@ -13,11 +13,12 @@ export const tokenAuthMutation = gql` ${fragmentUser} mutation TokenAuth($email: String!, $password: String!) { tokenCreate(email: $email, password: $password) { - token errors: accountErrors { field message } + csrfToken + token user { ...User } @@ -68,6 +69,8 @@ export const setPassword = gql` errors: accountErrors { ...AccountErrorFragment } + csrfToken + refreshToken token user { ...User diff --git a/src/auth/types/SetPassword.ts b/src/auth/types/SetPassword.ts index 9a09baf70..0ede71f38 100644 --- a/src/auth/types/SetPassword.ts +++ b/src/auth/types/SetPassword.ts @@ -38,6 +38,8 @@ export interface SetPassword_setPassword_user { export interface SetPassword_setPassword { __typename: "SetPassword"; errors: SetPassword_setPassword_errors[]; + csrfToken: string | null; + refreshToken: string | null; token: string | null; user: SetPassword_setPassword_user | null; } diff --git a/src/auth/types/TokenAuth.ts b/src/auth/types/TokenAuth.ts index 7df055cfc..8da32e338 100644 --- a/src/auth/types/TokenAuth.ts +++ b/src/auth/types/TokenAuth.ts @@ -37,8 +37,9 @@ export interface TokenAuth_tokenCreate_user { export interface TokenAuth_tokenCreate { __typename: "CreateToken"; - token: string | null; errors: TokenAuth_tokenCreate_errors[]; + csrfToken: string | null; + token: string | null; user: TokenAuth_tokenCreate_user | null; } diff --git a/src/auth/utils.ts b/src/auth/utils.ts index 72ebf3a45..cce76bc10 100644 --- a/src/auth/utils.ts +++ b/src/auth/utils.ts @@ -2,20 +2,43 @@ import { UseNotifierResult } from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; import { IntlShape } from "react-intl"; -const TOKEN_STORAGE_KEY = "dashboardAuth"; +export enum TOKEN_STORAGE_KEY { + AUTH = "auth", + CSRF = "csrf" +} -export const getAuthToken = () => - localStorage.getItem(TOKEN_STORAGE_KEY) || - sessionStorage.getItem(TOKEN_STORAGE_KEY); +export const getTokens = () => ({ + auth: + localStorage.getItem(TOKEN_STORAGE_KEY.AUTH) || + sessionStorage.getItem(TOKEN_STORAGE_KEY.AUTH), + refresh: + localStorage.getItem(TOKEN_STORAGE_KEY.CSRF) || + sessionStorage.getItem(TOKEN_STORAGE_KEY.CSRF) +}); -export const setAuthToken = (token: string, persist: boolean) => - persist - ? localStorage.setItem(TOKEN_STORAGE_KEY, token) - : sessionStorage.setItem(TOKEN_STORAGE_KEY, token); +export const setTokens = (auth: string, csrf: string, persist: boolean) => { + if (persist) { + localStorage.setItem(TOKEN_STORAGE_KEY.AUTH, auth); + localStorage.setItem(TOKEN_STORAGE_KEY.CSRF, csrf); + } else { + sessionStorage.setItem(TOKEN_STORAGE_KEY.AUTH, auth); + sessionStorage.setItem(TOKEN_STORAGE_KEY.CSRF, csrf); + } +}; -export const removeAuthToken = () => { - localStorage.removeItem(TOKEN_STORAGE_KEY); - sessionStorage.removeItem(TOKEN_STORAGE_KEY); +export const setAuthToken = (auth: string, persist: boolean) => { + if (persist) { + localStorage.setItem(TOKEN_STORAGE_KEY.AUTH, auth); + } else { + sessionStorage.setItem(TOKEN_STORAGE_KEY.AUTH, auth); + } +}; + +export const removeTokens = () => { + localStorage.removeItem(TOKEN_STORAGE_KEY.AUTH); + // localStorage.removeItem(TOKEN_STORAGE_KEY.CSRF); + sessionStorage.removeItem(TOKEN_STORAGE_KEY.AUTH); + // sessionStorage.removeItem(TOKEN_STORAGE_KEY.CSRF); }; export const displayDemoMessage = ( diff --git a/src/auth/views/NewPassword.tsx b/src/auth/views/NewPassword.tsx index 5fe9267ef..95fa9a4df 100644 --- a/src/auth/views/NewPassword.tsx +++ b/src/auth/views/NewPassword.tsx @@ -19,7 +19,11 @@ const NewPassword: React.FC = ({ location }) => { const handleSetPassword = async (data: SetPassword) => { if (data.setPassword.errors.length === 0) { - loginByToken(data.setPassword.token, data.setPassword.user); + loginByToken( + data.setPassword.token, + data.setPassword.csrfToken, + data.setPassword.user + ); navigate("/", true); } }; diff --git a/src/hooks/makeQuery.ts b/src/hooks/makeQuery.ts index 743c5e7e8..d5117469a 100644 --- a/src/hooks/makeQuery.ts +++ b/src/hooks/makeQuery.ts @@ -1,7 +1,7 @@ -import { isJwtError } from "@saleor/auth/errors"; +import { isJwtError, isJwtExpiredError } from "@saleor/auth/errors"; import { commonMessages } from "@saleor/intl"; import { maybe, RequireAtLeastOne } from "@saleor/misc"; -import { ApolloQueryResult } from "apollo-client"; +import { ApolloError, ApolloQueryResult } from "apollo-client"; import { DocumentNode } from "graphql"; import { useEffect } from "react"; import { QueryResult, useQuery as useBaseQuery } from "react-apollo"; @@ -48,6 +48,37 @@ function makeQuery( }, errorPolicy: "all", fetchPolicy: "cache-and-network", + onError: async (error: ApolloError) => { + if (error.graphQLErrors.some(isJwtError)) { + if (error.graphQLErrors.every(isJwtExpiredError)) { + const success = await user.tokenRefresh(); + + if (!success) { + user.logout(); + notify({ + status: "error", + text: intl.formatMessage(commonMessages.sessionExpired) + }); + } + } else { + user.logout(); + notify({ + status: "error", + text: intl.formatMessage(commonMessages.somethingWentWrong) + }); + } + } else if ( + !error.graphQLErrors.every( + err => + maybe(() => err.extensions.exception.code) === "PermissionDenied" + ) + ) { + notify({ + status: "error", + text: intl.formatMessage(commonMessages.somethingWentWrong) + }); + } + }, skip, variables }); @@ -63,26 +94,6 @@ function makeQuery( } }, [queryData.loading]); - if (queryData.error) { - if (queryData.error.graphQLErrors.some(isJwtError)) { - user.logout(); - notify({ - status: "error", - text: intl.formatMessage(commonMessages.sessionExpired) - }); - } else if ( - !queryData.error.graphQLErrors.every( - err => - maybe(() => err.extensions.exception.code) === "PermissionDenied" - ) - ) { - notify({ - status: "error", - text: intl.formatMessage(commonMessages.somethingWentWrong) - }); - } - } - const loadMore = ( mergeFunc: (previousResults: TData, fetchMoreResult: TData) => TData, extraVariables: RequireAtLeastOne diff --git a/src/index.tsx b/src/index.tsx index 2118f7298..f684c326c 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,8 +4,6 @@ import { defaultDataIdFromObject, InMemoryCache } from "apollo-cache-inmemory"; import { ApolloClient } from "apollo-client"; import { ApolloLink } from "apollo-link"; import { BatchHttpLink } from "apollo-link-batch-http"; -import { setContext } from "apollo-link-context"; -import { ErrorResponse, onError } from "apollo-link-error"; import { createUploadLink } from "apollo-upload-client"; import React from "react"; import { ApolloProvider } from "react-apollo"; @@ -19,11 +17,11 @@ import AppsSection from "./apps"; import { appsSection } from "./apps/urls"; import AttributeSection from "./attributes"; import { attributeSection } from "./attributes/urls"; -import Auth, { getAuthToken, removeAuthToken } from "./auth"; +import Auth from "./auth"; import AuthProvider, { useAuth } from "./auth/AuthProvider"; import LoginLoading from "./auth/components/LoginLoading/LoginLoading"; import SectionRoute from "./auth/components/SectionRoute"; -import { isJwtError } from "./auth/errors"; +import authLink from "./auth/link"; import { hasPermission } from "./auth/misc"; import CategorySection from "./categories"; import CollectionSection from "./collections"; @@ -60,43 +58,15 @@ import { PermissionEnum } from "./types/globalTypes"; import WarehouseSection from "./warehouses"; import { warehouseSection } from "./warehouses/urls"; -interface ResponseError extends ErrorResponse { - networkError?: Error & { - statusCode?: number; - bodyText?: string; - }; -} - if (process.env.GTM_ID !== undefined) { TagManager.initialize({ gtmId: GTM_ID }); } -const invalidTokenLink = onError((error: ResponseError) => { - if ( - (error.networkError && error.networkError.statusCode === 401) || - error.graphQLErrors?.some(isJwtError) - ) { - removeAuthToken(); - } -}); - -const authLink = setContext((_, context) => { - const authToken = getAuthToken(); - - return { - ...context, - headers: { - ...context.headers, - Authorization: authToken ? `JWT ${authToken}` : null - } - }; -}); - // DON'T TOUCH THIS // These are separate clients and do not share configs between themselves // so we need to explicitly set them const linkOptions = { - credentials: "same-origin", + credentials: "include", uri: API_URI }; const uploadLink = createUploadLink(linkOptions); @@ -122,7 +92,7 @@ const apolloClient = new ApolloClient({ return defaultDataIdFromObject(obj); } }), - link: invalidTokenLink.concat(authLink.concat(link)) + link: authLink.concat(link) }); const App: React.FC = () => { diff --git a/src/queries.tsx b/src/queries.tsx index 55f878368..96c4f8b5d 100644 --- a/src/queries.tsx +++ b/src/queries.tsx @@ -1,10 +1,10 @@ -import { ApolloQueryResult } from "apollo-client"; +import { ApolloError, ApolloQueryResult } from "apollo-client"; import { DocumentNode } from "graphql"; import React from "react"; import { Query, QueryResult } from "react-apollo"; import { useIntl } from "react-intl"; -import { isJwtError } from "./auth/errors"; +import { isJwtError, isJwtExpiredError } from "./auth/errors"; import useAppState from "./hooks/useAppState"; import useNotifier from "./hooks/useNotifier"; import useUser from "./hooks/useUser"; @@ -79,29 +79,40 @@ export function TypedQuery( skip={skip} context={{ useBatching: true }} errorPolicy="all" - > - {(queryData: QueryResult) => { - if (queryData.error) { - if (queryData.error.graphQLErrors.some(isJwtError)) { + onError={async (error: ApolloError) => { + if (error.graphQLErrors.some(isJwtError)) { + if (error.graphQLErrors.every(isJwtExpiredError)) { + const success = await user.tokenRefresh(); + + if (!success) { + user.logout(); + notify({ + status: "error", + text: intl.formatMessage(commonMessages.sessionExpired) + }); + } + } else { user.logout(); - notify({ - status: "error", - text: intl.formatMessage(commonMessages.sessionExpired) - }); - } else if ( - !queryData.error.graphQLErrors.every( - err => - maybe(() => err.extensions.exception.code) === - "PermissionDenied" - ) - ) { notify({ status: "error", text: intl.formatMessage(commonMessages.somethingWentWrong) }); } + } else if ( + !error.graphQLErrors.every( + err => + maybe(() => err.extensions.exception.code) === + "PermissionDenied" + ) + ) { + notify({ + status: "error", + text: intl.formatMessage(commonMessages.somethingWentWrong) + }); } - + }} + > + {(queryData: QueryResult) => { const loadMore = ( mergeFunc: ( previousResults: TData, From 377147f5a1ba4dcff7e5268888cd27402dc37902 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Fri, 24 Jul 2020 11:17:25 +0200 Subject: [PATCH 17/23] Apply review changes --- .../recording.har | 24 ++++++---- .../recording.har | 24 ++++++---- src/auth/link.ts | 4 +- src/auth/utils.ts | 43 ++++++++++++++++- src/hooks/makeQuery.ts | 46 +++++------------- src/queries.tsx | 48 +++++-------------- 6 files changed, 94 insertions(+), 95 deletions(-) diff --git a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har index a4d7605fa..e287b6cf6 100644 --- a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "f0343691dcc48a40921887f4f58c55b6", + "_id": "4113b07f8435ac712a5761c5bc33aa90", "_order": 0, "cache": {}, "request": { - "bodySize": 692, + "bodySize": 428, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "692" + "value": "428" }, { "_fromType": "array", @@ -56,7 +56,7 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUyMzk4OTcsImV4cCI6MTU5NTI0MDE5NywidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.l-FnFDVmi5fASo7Uae2Emewu2pKyO2qLz7ZQl1fSzo41\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"NotAToken\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" @@ -72,7 +72,7 @@ "headers": [ { "name": "date", - "value": "Tue, 21 Jul 2020 11:52:05 GMT" + "value": "Fri, 24 Jul 2020 09:02:43 GMT" }, { "name": "server", @@ -84,7 +84,7 @@ }, { "name": "access-control-allow-origin", - "value": "*" + "value": "http://localhost:9000" }, { "name": "access-control-allow-methods", @@ -94,6 +94,10 @@ "name": "access-control-allow-headers", "value": "Origin, Content-Type, Accept, Authorization" }, + { + "name": "access-control-allow-credentials", + "value": "true" + }, { "name": "content-length", "value": "89" @@ -103,14 +107,14 @@ "value": "nosniff" } ], - "headersSize": 314, + "headersSize": 374, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-21T11:52:05.050Z", - "time": 169, + "startedDateTime": "2020-07-24T09:02:43.587Z", + "time": 13, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +122,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 169 + "wait": 13 } } ], diff --git a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har index 4673d4895..fccbdb2d0 100644 --- a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har @@ -8,11 +8,11 @@ }, "entries": [ { - "_id": "29fb7ad4777c005f81fdfd957c1c81af", + "_id": "faa83118f90012a7303c655a14d89c0d", "_order": 0, "cache": {}, "request": { - "bodySize": 588, + "bodySize": 603, "cookies": [], "headers": [ { @@ -28,7 +28,7 @@ { "_fromType": "array", "name": "content-length", - "value": "588" + "value": "603" }, { "_fromType": "array", @@ -56,7 +56,7 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"admin1\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"TokenAuth\",\"variables\":{\"email\":\"admin@example.com\",\"password\":\"NotAValidPassword123!\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation TokenAuth($email: String!, $password: String!) {\\n tokenCreate(email: $email, password: $password) {\\n errors: accountErrors {\\n field\\n message\\n __typename\\n }\\n csrfToken\\n token\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" @@ -72,7 +72,7 @@ "headers": [ { "name": "date", - "value": "Wed, 22 Jul 2020 09:21:11 GMT" + "value": "Fri, 24 Jul 2020 09:02:43 GMT" }, { "name": "server", @@ -84,7 +84,7 @@ }, { "name": "access-control-allow-origin", - "value": "*" + "value": "http://localhost:9000" }, { "name": "access-control-allow-methods", @@ -94,6 +94,10 @@ "name": "access-control-allow-headers", "value": "Origin, Content-Type, Accept, Authorization" }, + { + "name": "access-control-allow-credentials", + "value": "true" + }, { "name": "content-length", "value": "214" @@ -103,14 +107,14 @@ "value": "nosniff" } ], - "headersSize": 315, + "headersSize": 375, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-22T09:21:11.006Z", - "time": 363, + "startedDateTime": "2020-07-24T09:02:43.099Z", + "time": 426, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +122,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 363 + "wait": 426 } } ], diff --git a/src/auth/link.ts b/src/auth/link.ts index 66c19a069..306ef036f 100644 --- a/src/auth/link.ts +++ b/src/auth/link.ts @@ -11,7 +11,7 @@ interface ResponseError extends ErrorResponse { }; } -export const invalidTokenLink = onError((error: ResponseError) => { +export const invalidateTokenLink = onError((error: ResponseError) => { if ( (error.networkError && error.networkError.statusCode === 401) || error.graphQLErrors?.some(isJwtError) @@ -34,6 +34,6 @@ export const tokenLink = setContext((_, context) => { }; }); -const link = invalidTokenLink.concat(tokenLink); +const link = invalidateTokenLink.concat(tokenLink); export default link; diff --git a/src/auth/utils.ts b/src/auth/utils.ts index cce76bc10..9ce9a6b23 100644 --- a/src/auth/utils.ts +++ b/src/auth/utils.ts @@ -1,7 +1,11 @@ +import { IMessageContext } from "@saleor/components/messages"; import { UseNotifierResult } from "@saleor/hooks/useNotifier"; import { commonMessages } from "@saleor/intl"; +import { ApolloError } from "apollo-client"; import { IntlShape } from "react-intl"; +import { isJwtError, isJwtExpiredError } from "./errors"; + export enum TOKEN_STORAGE_KEY { AUTH = "auth", CSRF = "csrf" @@ -36,9 +40,7 @@ export const setAuthToken = (auth: string, persist: boolean) => { export const removeTokens = () => { localStorage.removeItem(TOKEN_STORAGE_KEY.AUTH); - // localStorage.removeItem(TOKEN_STORAGE_KEY.CSRF); sessionStorage.removeItem(TOKEN_STORAGE_KEY.AUTH); - // sessionStorage.removeItem(TOKEN_STORAGE_KEY.CSRF); }; export const displayDemoMessage = ( @@ -49,3 +51,40 @@ export const displayDemoMessage = ( text: intl.formatMessage(commonMessages.demo) }); }; + +export async function handleQueryAuthError( + error: ApolloError, + notify: IMessageContext, + tokenRefresh: () => Promise, + logout: () => void, + intl: IntlShape +) { + if (error.graphQLErrors.some(isJwtError)) { + if (error.graphQLErrors.every(isJwtExpiredError)) { + const success = await tokenRefresh(); + + if (!success) { + logout(); + notify({ + status: "error", + text: intl.formatMessage(commonMessages.sessionExpired) + }); + } + } else { + logout(); + notify({ + status: "error", + text: intl.formatMessage(commonMessages.somethingWentWrong) + }); + } + } else if ( + !error.graphQLErrors.every( + err => err.extensions?.exception?.code === "PermissionDenied" + ) + ) { + notify({ + status: "error", + text: intl.formatMessage(commonMessages.somethingWentWrong) + }); + } +} diff --git a/src/hooks/makeQuery.ts b/src/hooks/makeQuery.ts index d5117469a..e67bf93f7 100644 --- a/src/hooks/makeQuery.ts +++ b/src/hooks/makeQuery.ts @@ -1,7 +1,6 @@ -import { isJwtError, isJwtExpiredError } from "@saleor/auth/errors"; -import { commonMessages } from "@saleor/intl"; -import { maybe, RequireAtLeastOne } from "@saleor/misc"; -import { ApolloError, ApolloQueryResult } from "apollo-client"; +import { handleQueryAuthError } from "@saleor/auth"; +import { RequireAtLeastOne } from "@saleor/misc"; +import { ApolloQueryResult } from "apollo-client"; import { DocumentNode } from "graphql"; import { useEffect } from "react"; import { QueryResult, useQuery as useBaseQuery } from "react-apollo"; @@ -48,37 +47,14 @@ function makeQuery( }, errorPolicy: "all", fetchPolicy: "cache-and-network", - onError: async (error: ApolloError) => { - if (error.graphQLErrors.some(isJwtError)) { - if (error.graphQLErrors.every(isJwtExpiredError)) { - const success = await user.tokenRefresh(); - - if (!success) { - user.logout(); - notify({ - status: "error", - text: intl.formatMessage(commonMessages.sessionExpired) - }); - } - } else { - user.logout(); - notify({ - status: "error", - text: intl.formatMessage(commonMessages.somethingWentWrong) - }); - } - } else if ( - !error.graphQLErrors.every( - err => - maybe(() => err.extensions.exception.code) === "PermissionDenied" - ) - ) { - notify({ - status: "error", - text: intl.formatMessage(commonMessages.somethingWentWrong) - }); - } - }, + onError: error => + handleQueryAuthError( + error, + notify, + user.tokenRefresh, + user.logout, + intl + ), skip, variables }); diff --git a/src/queries.tsx b/src/queries.tsx index 96c4f8b5d..3f7e039cb 100644 --- a/src/queries.tsx +++ b/src/queries.tsx @@ -1,15 +1,14 @@ -import { ApolloError, ApolloQueryResult } from "apollo-client"; +import { ApolloQueryResult } from "apollo-client"; import { DocumentNode } from "graphql"; import React from "react"; import { Query, QueryResult } from "react-apollo"; import { useIntl } from "react-intl"; -import { isJwtError, isJwtExpiredError } from "./auth/errors"; +import { handleQueryAuthError } from "./auth"; import useAppState from "./hooks/useAppState"; import useNotifier from "./hooks/useNotifier"; import useUser from "./hooks/useUser"; -import { commonMessages } from "./intl"; -import { maybe, RequireAtLeastOne } from "./misc"; +import { RequireAtLeastOne } from "./misc"; export interface LoadMore { loadMore: ( @@ -79,38 +78,15 @@ export function TypedQuery( skip={skip} context={{ useBatching: true }} errorPolicy="all" - onError={async (error: ApolloError) => { - if (error.graphQLErrors.some(isJwtError)) { - if (error.graphQLErrors.every(isJwtExpiredError)) { - const success = await user.tokenRefresh(); - - if (!success) { - user.logout(); - notify({ - status: "error", - text: intl.formatMessage(commonMessages.sessionExpired) - }); - } - } else { - user.logout(); - notify({ - status: "error", - text: intl.formatMessage(commonMessages.somethingWentWrong) - }); - } - } else if ( - !error.graphQLErrors.every( - err => - maybe(() => err.extensions.exception.code) === - "PermissionDenied" - ) - ) { - notify({ - status: "error", - text: intl.formatMessage(commonMessages.somethingWentWrong) - }); - } - }} + onError={error => + handleQueryAuthError( + error, + notify, + user.tokenRefresh, + user.logout, + intl + ) + } > {(queryData: QueryResult) => { const loadMore = ( From dd6f261f782ef1c008f1deef622b8f9c0e14461b Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Mon, 27 Jul 2020 11:39:00 +0200 Subject: [PATCH 18/23] Remove duplicated functtion --- src/auth/errors.ts | 4 ---- src/auth/utils.ts | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/auth/errors.ts b/src/auth/errors.ts index c42a2dfa2..4ced5f7a0 100644 --- a/src/auth/errors.ts +++ b/src/auth/errors.ts @@ -11,10 +11,6 @@ export function isJwtError(error: GraphQLError): boolean { return !!findValueInEnum(error.extensions.exception.code, JWTError); } -export function isJwtExpiredError(error: GraphQLError): boolean { - return error.extensions.exception.code === JWTError.expired; -} - export function isTokenExpired(error: GraphQLError): boolean { return error.extensions.exception.code === JWTError.expired; } diff --git a/src/auth/utils.ts b/src/auth/utils.ts index 9ce9a6b23..4ea5c4afb 100644 --- a/src/auth/utils.ts +++ b/src/auth/utils.ts @@ -4,7 +4,7 @@ import { commonMessages } from "@saleor/intl"; import { ApolloError } from "apollo-client"; import { IntlShape } from "react-intl"; -import { isJwtError, isJwtExpiredError } from "./errors"; +import { isJwtError, isTokenExpired } from "./errors"; export enum TOKEN_STORAGE_KEY { AUTH = "auth", @@ -60,7 +60,7 @@ export async function handleQueryAuthError( intl: IntlShape ) { if (error.graphQLErrors.some(isJwtError)) { - if (error.graphQLErrors.every(isJwtExpiredError)) { + if (error.graphQLErrors.every(isTokenExpired)) { const success = await tokenRefresh(); if (!success) { From 9d4398a6c83ad5e4a0a27fce71e61e88cdb8e8cd Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Mon, 27 Jul 2020 13:25:29 +0200 Subject: [PATCH 19/23] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13a3dc8a3..28a010b95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable, unreleased changes to this project will be documented in this file. - Add order invoices management - #570 by @orzechdev - Add Cypress e2e runner - #584 by @krzysztofwolski - create Apps - #599 by @AlicjaSzu +- Refactor authorization - #624 by @dominik-zeglen ## 2.10.1 From 1e1de16c876e94db512fb97a2900716e18fa2d39 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Tue, 28 Jul 2020 12:33:55 +0200 Subject: [PATCH 20/23] Use env variable --- testUtils/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testUtils/api.ts b/testUtils/api.ts index 583f2a6de..58badce16 100644 --- a/testUtils/api.ts +++ b/testUtils/api.ts @@ -25,7 +25,7 @@ function setupApi() { const link = new BatchHttpLink({ // @ts-ignore fetch, - uri: "http://localhost:8000/graphql/" + uri: process.env.API_URI }); const apolloClient = new ApolloClient({ cache, From 6f29fa274e840712a416bd44f1c44461e09f1e38 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Tue, 28 Jul 2020 13:01:28 +0200 Subject: [PATCH 21/23] Throw error if API_URI is not set --- testUtils/api.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testUtils/api.ts b/testUtils/api.ts index 58badce16..9e5cf72ca 100644 --- a/testUtils/api.ts +++ b/testUtils/api.ts @@ -12,6 +12,10 @@ Polly.register(NodeHttpAdapter); Polly.register(FSPersister); function setupApi() { + if (!process.env.API_URI) { + throw new Error("Environment variable API_URI not set"); + } + setupPolly({ adapters: ["node-http"], persister: "fs", From 4ffbcd069dd7361ab4de449fe6d6891a8971a598 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Wed, 29 Jul 2020 15:10:39 +0200 Subject: [PATCH 22/23] Do not match requests by url and headers --- .../recording.har | 18 +++++++-------- .../recording.har | 22 +++++++++---------- .../recording.har | 16 +++++--------- .../recording.har | 16 +++++--------- src/auth/AuthProvider.test.ts | 6 ++--- testUtils/api.ts | 13 +++++++++++ 6 files changed, 47 insertions(+), 44 deletions(-) diff --git a/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har b/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har index af02d5788..8ad594963 100644 --- a/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har +++ b/recordings/User_3768991250/will-be-logged-if-has-valid-token_3465908808/recording.har @@ -8,7 +8,7 @@ }, "entries": [ { - "_id": "414f6b24b58b132c92e9a6ea67613a15", + "_id": "f515e15cbc83df73e5bd41437971c2e6", "_order": 0, "cache": {}, "request": { @@ -56,7 +56,7 @@ "postData": { "mimeType": "application/json", "params": [], - "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUyMzk4OTcsImV4cCI6MTU5NTI0MDE5NywidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.l-FnFDVmi5fASo7Uae2Emewu2pKyO2qLz7ZQl1fSzo4\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" + "text": "[{\"operationName\":\"VerifyToken\",\"variables\":{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5NjAyODUxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.eo8_Ew98HICB4cFQN2U7mCJ8ydGVOvQLGRT4CnkufMc\"},\"query\":\"fragment User on User {\\n id\\n email\\n firstName\\n lastName\\n userPermissions {\\n code\\n name\\n __typename\\n }\\n avatar {\\n url\\n __typename\\n }\\n __typename\\n}\\n\\nmutation VerifyToken($token: String!) {\\n tokenVerify(token: $token) {\\n payload\\n user {\\n ...User\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}]" }, "queryString": [], "url": "http://localhost:8000/graphql/" @@ -66,13 +66,13 @@ "content": { "mimeType": "application/json", "size": 1619, - "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1595239897, \"exp\": 1595240197, \"token\": \"qCRbkGN2zNOo\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyMQ==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}]" + "text": "[{\"data\": {\"tokenVerify\": {\"payload\": {\"iat\": 1596028218, \"exp\": 1596028518, \"token\": \"C3Sk2kLRVuPA\", \"email\": \"admin@example.com\", \"type\": \"access\", \"user_id\": \"VXNlcjoyMQ==\", \"is_staff\": true}, \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"VerifyToken\"}}}]" }, "cookies": [], "headers": [ { "name": "date", - "value": "Tue, 21 Jul 2020 10:59:19 GMT" + "value": "Wed, 29 Jul 2020 13:10:18 GMT" }, { "name": "server", @@ -84,7 +84,7 @@ }, { "name": "access-control-allow-origin", - "value": "*" + "value": "http://localhost:9000" }, { "name": "access-control-allow-methods", @@ -103,14 +103,14 @@ "value": "nosniff" } ], - "headersSize": 316, + "headersSize": 336, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-21T10:59:19.225Z", - "time": 70, + "startedDateTime": "2020-07-29T13:10:18.327Z", + "time": 23, "timings": { "blocked": -1, "connect": -1, @@ -118,7 +118,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 70 + "wait": 23 } } ], diff --git a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har index f02c738f0..be977fd9c 100644 --- a/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har +++ b/recordings/User_3768991250/will-be-logged-in-if-has-valid-credentials_3587751314/recording.har @@ -8,7 +8,7 @@ }, "entries": [ { - "_id": "ec3f8ec4f0f88fc421dc0d46281d4515", + "_id": "7c460842cac4a92c188d5451dfc533a2", "_order": 0, "cache": {}, "request": { @@ -66,23 +66,21 @@ "content": { "mimeType": "application/json", "size": 1830, - "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"oQ3yHRrBRRtQNVuJgn4D6Txh3aPWC8fl91XMcA2bukbgkdUotAEAJbAcCvTsXn3Z\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTU0MDk1ODUsImV4cCI6MTU5NTQwOTU5MCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.jv57hd44KxRNMKO6IcP5bD7Upg2rnZ1fzXmsk0yAZDg\", \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" + "text": "[{\"data\": {\"tokenCreate\": {\"errors\": [], \"csrfToken\": \"rLPNMGNYKXH8VY4UNEWl4nEOFMseocljioigPl36IM2CqbdmOTEpNwvdHBAJ1ZWQ\", \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5NjAyODUxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.eo8_Ew98HICB4cFQN2U7mCJ8ydGVOvQLGRT4CnkufMc\", \"user\": {\"id\": \"VXNlcjoyMQ==\", \"email\": \"admin@example.com\", \"firstName\": \"\", \"lastName\": \"\", \"userPermissions\": [{\"code\": \"MANAGE_APPS\", \"name\": \"Manage apps\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_CHECKOUTS\", \"name\": \"Manage checkouts\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_DISCOUNTS\", \"name\": \"Manage sales and vouchers.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_GIFT_CARD\", \"name\": \"Manage gift cards.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_MENUS\", \"name\": \"Manage navigation.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_ORDERS\", \"name\": \"Manage orders.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PAGES\", \"name\": \"Manage pages.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PLUGINS\", \"name\": \"Manage plugins\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_PRODUCTS\", \"name\": \"Manage products.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SETTINGS\", \"name\": \"Manage settings.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_SHIPPING\", \"name\": \"Manage shipping.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_STAFF\", \"name\": \"Manage staff.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_TRANSLATIONS\", \"name\": \"Manage translations.\", \"__typename\": \"UserPermission\"}, {\"code\": \"MANAGE_USERS\", \"name\": \"Manage customers.\", \"__typename\": \"UserPermission\"}], \"avatar\": null, \"__typename\": \"User\"}, \"__typename\": \"CreateToken\"}}}]" }, "cookies": [ { - "expires": "2020-07-22T09:49:45.000Z", "httpOnly": true, - "maxAge": 1800, "name": "refreshToken", "path": "/", "secure": true, - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTU0MDk1ODUsImV4cCI6MTU5NTQxMTM4NSwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6Im9RM3lIUnJCUlJ0UU5WdUpnbjRENlR4aDNhUFdDOGZsOTFYTWNBMmJ1a2Jna2RVb3RBRUFKYkFjQ3ZUc1huM1oifQ.KeIbqoHp-XPFfny6tlq6EUA7BojWAnNcpePMtI2X4qo" + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5ODYyMDIxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6InJMUE5NR05ZS1hIOFZZNFVORVdsNG5FT0ZNc2VvY2xqaW9pZ1BsMzZJTTJDcWJkbU9URXBOd3ZkSEJBSjFaV1EifQ.boD8G4pkSnZF-PLl5oOg85Uj-mqTiAzOkua9aAG3Bz4" } ], "headers": [ { "name": "date", - "value": "Wed, 22 Jul 2020 09:19:45 GMT" + "value": "Wed, 29 Jul 2020 13:10:18 GMT" }, { "name": "server", @@ -94,7 +92,7 @@ }, { "name": "access-control-allow-origin", - "value": "*" + "value": "http://localhost:9000" }, { "name": "access-control-allow-methods", @@ -115,17 +113,17 @@ { "_fromType": "array", "name": "set-cookie", - "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTU0MDk1ODUsImV4cCI6MTU5NTQxMTM4NSwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6Im9RM3lIUnJCUlJ0UU5WdUpnbjRENlR4aDNhUFdDOGZsOTFYTWNBMmJ1a2Jna2RVb3RBRUFKYkFjQ3ZUc1huM1oifQ.KeIbqoHp-XPFfny6tlq6EUA7BojWAnNcpePMtI2X4qo; expires=Wed, 22 Jul 2020 09:49:45 GMT; HttpOnly; Max-Age=1800; Path=/; Secure" + "value": "refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTYwMjgyMTgsImV4cCI6MTU5ODYyMDIxOCwidG9rZW4iOiJDM1NrMmtMUlZ1UEEiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6InJlZnJlc2giLCJ1c2VyX2lkIjoiVlhObGNqb3lNUT09IiwiaXNfc3RhZmYiOnRydWUsImNzcmZUb2tlbiI6InJMUE5NR05ZS1hIOFZZNFVORVdsNG5FT0ZNc2VvY2xqaW9pZ1BsMzZJTTJDcWJkbU9URXBOd3ZkSEJBSjFaV1EifQ.boD8G4pkSnZF-PLl5oOg85Uj-mqTiAzOkua9aAG3Bz4; HttpOnly; Path=/; Secure" } ], - "headersSize": 801, + "headersSize": 768, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-22T09:19:45.127Z", - "time": 324, + "startedDateTime": "2020-07-29T13:10:18.064Z", + "time": 118, "timings": { "blocked": -1, "connect": -1, @@ -133,7 +131,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 324 + "wait": 118 } } ], diff --git a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har index e287b6cf6..8e4ea3f81 100644 --- a/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-if-has-invalid-token_1301762210/recording.har @@ -8,7 +8,7 @@ }, "entries": [ { - "_id": "4113b07f8435ac712a5761c5bc33aa90", + "_id": "4836098613648775386c1e10728424dd", "_order": 0, "cache": {}, "request": { @@ -72,7 +72,7 @@ "headers": [ { "name": "date", - "value": "Fri, 24 Jul 2020 09:02:43 GMT" + "value": "Wed, 29 Jul 2020 13:10:18 GMT" }, { "name": "server", @@ -94,10 +94,6 @@ "name": "access-control-allow-headers", "value": "Origin, Content-Type, Accept, Authorization" }, - { - "name": "access-control-allow-credentials", - "value": "true" - }, { "name": "content-length", "value": "89" @@ -107,14 +103,14 @@ "value": "nosniff" } ], - "headersSize": 374, + "headersSize": 334, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-24T09:02:43.587Z", - "time": 13, + "startedDateTime": "2020-07-29T13:10:18.368Z", + "time": 6, "timings": { "blocked": -1, "connect": -1, @@ -122,7 +118,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 13 + "wait": 6 } } ], diff --git a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har index fccbdb2d0..547653fee 100644 --- a/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har +++ b/recordings/User_3768991250/will-not-be-logged-in-if-doesn-t-have-valid-credentials_3719199657/recording.har @@ -8,7 +8,7 @@ }, "entries": [ { - "_id": "faa83118f90012a7303c655a14d89c0d", + "_id": "86487093ff8b070d496fcdc566e01adf", "_order": 0, "cache": {}, "request": { @@ -72,7 +72,7 @@ "headers": [ { "name": "date", - "value": "Fri, 24 Jul 2020 09:02:43 GMT" + "value": "Wed, 29 Jul 2020 13:10:18 GMT" }, { "name": "server", @@ -94,10 +94,6 @@ "name": "access-control-allow-headers", "value": "Origin, Content-Type, Accept, Authorization" }, - { - "name": "access-control-allow-credentials", - "value": "true" - }, { "name": "content-length", "value": "214" @@ -107,14 +103,14 @@ "value": "nosniff" } ], - "headersSize": 375, + "headersSize": 335, "httpVersion": "HTTP/1.1", "redirectURL": "", "status": 200, "statusText": "OK" }, - "startedDateTime": "2020-07-24T09:02:43.099Z", - "time": 426, + "startedDateTime": "2020-07-29T13:10:18.208Z", + "time": 99, "timings": { "blocked": -1, "connect": -1, @@ -122,7 +118,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 426 + "wait": 99 } } ], diff --git a/src/auth/AuthProvider.test.ts b/src/auth/AuthProvider.test.ts index 8a55ae399..c9d82f557 100644 --- a/src/auth/AuthProvider.test.ts +++ b/src/auth/AuthProvider.test.ts @@ -3,7 +3,7 @@ import { act, renderHook } from "@testing-library/react-hooks"; import ApolloClient from "apollo-client"; import { useAuthProvider } from "./AuthProvider"; -import { setAuthToken } from "./utils"; +import { getTokens, setAuthToken } from "./utils"; const apolloClient = setupApi(); @@ -23,8 +23,7 @@ function renderAuthProvider(apolloClient: ApolloClient) { const credentials = { email: "admin@example.com", password: "admin", - token: - "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUyMzk4OTcsImV4cCI6MTU5NTI0MDE5NywidG9rZW4iOiJxQ1Jia0dOMnpOT28iLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwidHlwZSI6ImFjY2VzcyIsInVzZXJfaWQiOiJWWE5sY2pveU1RPT0iLCJpc19zdGFmZiI6dHJ1ZX0.l-FnFDVmi5fASo7Uae2Emewu2pKyO2qLz7ZQl1fSzo4" + token: null }; beforeEach(() => { @@ -40,6 +39,7 @@ describe("User", () => { hook.current.login(credentials.email, credentials.password) ); expect(hook.current.userContext.email).toBe(credentials.email); + credentials.token = getTokens().auth; done(); }); diff --git a/testUtils/api.ts b/testUtils/api.ts index 9e5cf72ca..07ef78be1 100644 --- a/testUtils/api.ts +++ b/testUtils/api.ts @@ -18,6 +18,19 @@ function setupApi() { setupPolly({ adapters: ["node-http"], + matchRequestsBy: { + headers: false, + url: { + hash: false, + hostname: false, + password: false, + pathname: false, + port: false, + protocol: false, + query: false, + username: false + } + }, persister: "fs", persisterOptions: { fs: { From 69336b8ccadf4a4eca789d94ff4453fbf8d70239 Mon Sep 17 00:00:00 2001 From: dominik-zeglen Date: Wed, 29 Jul 2020 15:34:39 +0200 Subject: [PATCH 23/23] Do not throw error if API_URI is not set --- testUtils/api.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/testUtils/api.ts b/testUtils/api.ts index 07ef78be1..aac1afeb3 100644 --- a/testUtils/api.ts +++ b/testUtils/api.ts @@ -12,10 +12,6 @@ Polly.register(NodeHttpAdapter); Polly.register(FSPersister); function setupApi() { - if (!process.env.API_URI) { - throw new Error("Environment variable API_URI not set"); - } - setupPolly({ adapters: ["node-http"], matchRequestsBy: { @@ -42,7 +38,7 @@ function setupApi() { const link = new BatchHttpLink({ // @ts-ignore fetch, - uri: process.env.API_URI + uri: process.env.API_URI || "http://localhost:8000/graphql/" }); const apolloClient = new ApolloClient({ cache,