UNPKG

react-gapi

Version:
379 lines (375 loc) 14.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function createGapiMock(setWindowProp = 'gapi') { let nextInteraction; const _user = { isSignedIn: false, scopes: [], props: { id: undefined, name: '', givenName: '', familyName: '', imageUrl: '', email: '' }, resolveNextInteraction: ()=>{ if (typeof nextInteraction === 'function') { nextInteraction(); nextInteraction = undefined; } else if (!nextInteraction) { nextInteraction = Promise.resolve(); } } }; const user = { isSignedIn: (scopes = [], userProps = {})=>{ _user.isSignedIn = true, _user.scopes = scopes; _user.props = { ..._user.props, ...userProps, id: (userProps.id ?? _user.props.id) ?? mockId() }; }, isNotSignedIn: (scopes = [], userProps)=>{ _user.isSignedIn = false, _user.scopes = scopes; _user.props = { ..._user.props, ...userProps }; }, nextInteraction: ()=>{ if (!nextInteraction) { return new Promise((res)=>{ nextInteraction = res; }); } else if (nextInteraction instanceof Promise) { const p = nextInteraction; nextInteraction = undefined; return p; } throw 'user.nextInteraction() was called multiple times before an interaction happened.\n' + 'This was probably not intended.'; } }; const _discoveryDocs = {}; const registerDiscoveryDocs = (o)=>Object.keys(o).forEach((k)=>_discoveryDocs[k] = o[k] ) ; const _modules = { auth2: createAuthModuleMock, client: createClientModuleMock }; const registerModuleMocks = (o)=>Object.keys(o).forEach((k)=>_modules[k] = o[k] ) ; const gapi = { load: (modules, then)=>{ Promise.all(modules.split(':').map((k)=>new Promise((res)=>{ res(); if (!gapi[k]) { gapi[k] = _modules[k] ? _modules[k]({ gapi, user, _user, _discoveryDocs }) : (name)=>({ init: ()=>notImplemented(`Mock for gapi module "${name}"`) }) ; } res(); }) )).then(typeof then === 'function' ? then : then.callback); } }; if (setWindowProp) { window[setWindowProp] = gapi; } return { gapi, user, registerModuleMocks, registerDiscoveryDocs }; } function mockId() { return Math.random().toString(10).substr(2, 10); } function notImplemented(descr) { return `${descr} is not implemented yet.`; } function createAuthModuleMock({ user , _user }) { let initConfig; let authInstance; const auth2 = { init: (config)=>{ const { client_id , fetch_basic_profile =true , scope ='' , } = config; if (!client_id) { throw { message: `Missing required parameter 'client_id'` }; } else if (!fetch_basic_profile && !scope) { throw { message: `Missing required parameter 'scope'` }; } if (!initConfig) { initConfig = { client_id, fetch_basic_profile, scope }; } else if (client_id !== initConfig.client_id || fetch_basic_profile !== initConfig.fetch_basic_profile || scope !== initConfig.scope) { throw { message: 'gapi.auth2 has been initialized with different options. Consider calling gapi.auth2.getAuthInstance() instead of gapi.auth2.init().' }; } if (!authInstance) { const effectiveScope = fetch_basic_profile ? scope + ' openid email profile' : scope; authInstance = createAuthInstanceMock(fetch_basic_profile, effectiveScope); } return authInstance; }, getAuthInstance: ()=>authInstance , authorize: ()=>{ throw notImplemented('auth2.authorize'); }, enableDebugLogs: ()=>{ throw notImplemented('auth2.enableDebugLogs'); } }; function createAuthInstanceMock(fetch_basic_profile, initScope) { let currentUser; const isSignedInListeners = []; const currentUserListeners = []; const auth = { then: (onInit, onFailure)=>{ Promise.resolve().then(()=>onInit(auth) , (r)=>onFailure(r) ); }, signIn: ({ scope ='' , prompt =undefined } = {})=>{ const scopeArray = (initScope ?? '' + ' ' + scope).split(' ').filter((k)=>Boolean(k) ); return requestScopes(scopeArray, prompt); }, signOut: ()=>new Promise((res)=>{ const isSignedInChanged = _user.isSignedIn; _user.isSignedIn = false; currentUser = createCurrentUserMock(); res(currentUser); notifyListeners(isSignedInChanged); }) , disconnect: ()=>new Promise((res)=>{ _user.scopes = []; auth.signOut().then(()=>res() ); }) , currentUser: { // currentUser.get() can return undefined on newly initialized GoogleAuth get: ()=>currentUser , listen: (c)=>{ currentUserListeners.push(c); } }, isSignedIn: { get: ()=>_user.isSignedIn , listen: (c)=>{ isSignedInListeners.push(c); } }, grantOfflineAccess: ()=>{ throw notImplemented('GoogleAuth.grantOfflineAccess'); }, attachClickHandler: ()=>{ throw notImplemented('GoogleAuth.attachClickHandler'); } }; if (_user.isSignedIn) { currentUser = createCurrentUserMock(); } return auth; function notifyListeners(isSignedInChanged) { if (isSignedInChanged) { isSignedInListeners.forEach((c)=>c(_user.isSignedIn) ); } currentUserListeners.forEach((c)=>currentUser && c(currentUser) ); } function requestScopes(scopes, prompt) { if (_user.promise) { throw 'The behavior when a previous Promise is still pending is unknown - call user.grantsScopes() or user.closesPopup()'; } return _user.promise = new Promise((res, rej)=>{ function clearInteraction() { delete user.grantsScopes; delete user.closesPopup; Promise.resolve().then(()=>{ delete _user.promise; }); } user.grantsScopes = (grantedScopes = true, userProps = {})=>{ const isSignedInChanged = !_user.isSignedIn; _user.isSignedIn = true; if (grantedScopes === true) { grantedScopes = scopes; } // openid is always added grantedScopes.push('openid'); grantedScopes.forEach((k)=>{ if (!_user.scopes.includes(k)) { _user.scopes.push(k); } // the module adds these scopes automatically if (k === 'profile' && !_user.scopes.includes('https://www.googleapis.com/auth/userinfo.profile')) { _user.scopes.push('https://www.googleapis.com/auth/userinfo.profile'); } else if (k === 'email' && !_user.scopes.includes('https://www.googleapis.com/auth/userinfo.email')) { _user.scopes.push('https://www.googleapis.com/auth/userinfo.email'); } }); _user.props = { ..._user.props, ...userProps, id: (userProps?.id ?? _user.props.id) ?? mockId() }; currentUser = createCurrentUserMock(); clearInteraction(); res(currentUser); notifyListeners(isSignedInChanged); }; user.deniesAccess = ()=>{ clearInteraction(); rej({ error: 'access_denied' }); }; user.closesPopup = ()=>{ clearInteraction(); rej({ error: 'popup_closed_by_user' }); }; if (scopes.every((k)=>_user.scopes.includes(k) )) { // if a user granted scopes before, the popup closes automatically if (_user.isSignedIn && prompt && ![ 'consent', 'select_account' ].includes(prompt)) { user.grantsScopes(); } } else if (prompt === 'none') { rej({ error_subtype: 'access_denied', error: 'immediate_failed' }); } _user.resolveNextInteraction(); }); } function createCurrentUserMock() { const basicProfile = createBasicProfileMock(); return { getId: ()=>_user.props.id ?? '' , isSignedIn: ()=>_user.isSignedIn , getHostedDomain: ()=>{ throw notImplemented('GoogleUser.getHostedDomain'); }, getGrantedScopes: ()=>_user.isSignedIn ? _user.scopes.concat(fetch_basic_profile ? [ 'email', 'openid', 'profile' ] : []).join(' ') : '' , getBasicProfile: ()=>basicProfile , getAuthResponse: ()=>{ throw notImplemented('GoogleUser.getHostedDomain'); }, reloadAuthResponse: ()=>{ throw notImplemented('GoogleUser.getHostedDomain'); }, hasGrantedScopes: (scope)=>scope.split(' ').filter((k)=>Boolean(k) && !_user.scopes.includes(k) ).length > 0 , grant: (options = {})=>authInstance && authInstance.signIn(options) , grantOfflineAccess: ()=>{ throw notImplemented('GoogleUser.getHostedDomain'); }, disconnect: ()=>authInstance && authInstance.disconnect() }; function createBasicProfileMock() { return initConfig?.fetch_basic_profile ? { getId: ()=>_user.props.id ?? '' , getName: ()=>_user.props.name ?? '' , getGivenName: ()=>_user.props.givenName ?? '' , getFamilyName: ()=>_user.props.familyName ?? '' , getImageUrl: ()=>_user.props.imageUrl ?? '' , getEmail: ()=>_user.props.email ?? '' } : { getId: ()=>_user.props.id ?? '' , getName: ()=>'' , getGivenName: ()=>'' , getFamilyName: ()=>'' , getImageUrl: ()=>'' , getEmail: ()=>'' }; } } } return auth2; } function createClientModuleMock({ gapi , _discoveryDocs }) { const client = { init: ({ clientId =undefined , scope =undefined , discoveryDocs =[] })=>new Promise((res1, rej1)=>{ const p = []; if (scope) { if (gapi.auth2) { p.push(gapi.auth2.init({ client_id: clientId, scope })); } else { throw 'gapi.auth2 not loaded'; } } p.push(...discoveryDocs.map((k)=>new Promise((res, rej)=>{ try { if (typeof _discoveryDocs[k] === 'function') { _discoveryDocs[k](gapi); } else { throw `Tried to load discoveryDocs ${k} - use registerDiscoveryDocs to mock discoveryDocs`; } res(); } catch (e) { rej(e); } }) )); Promise.all(p).then(()=>res1() , ()=>rej1() ); }) }; return client; } exports.createGapiMock = createGapiMock;