From 3a79a84b5fd8f2a26debce77ac411700e4fee30c Mon Sep 17 00:00:00 2001 From: iturgeon Date: Mon, 24 Jan 2022 23:10:08 -0500 Subject: [PATCH 1/3] Add support for CDN asset hosting Supports setting an env var for CDN_ASSET_HOST. Doing so will prepend the asset urls that are built in asset_resolver with a cdn. For this to work, you need to set CDN_ASSET_HOST=https://cdn.com Then upload everything from packages/app/obojobo-express/server/public to the cdn after running yarn build. --- README.md | 30 +++++++++++++++++++ .../__tests__/asset_resolver.test.js | 13 ++++++++ .../obojobo-express/server/asset_resolver.js | 13 ++++++-- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c56a4fe6f6..0522de051a 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,36 @@ Once logged in, visit [https://127.0.0.1:8080/editor](https://127.0.0.1:8080/edi `yarn test` or `yarn test:ci` +### Deployment and Setup + +For deployment, there are several places you can set configuration options. Obojobo uses a hybrid of json files and environment variables. We are moving more toward using more environment variables. However, json config files can be found in several obojobo packages (they are usually in server/config/*.json files). Below are a set of environment variables that we recommend using + +- `CDN_ASSET_HOST`: If you want to host compiled browser assets (js/css/etc) on a CDN. Ex value: `https://site.com` +- `NODE_ENV`: determines which environment from the config files will be used. Use production unless you have a use for multiple environments. +- `DB_USER`: db user +- `DB_PASS`: db pass +- `DB_HOST`: db host +- `DB_NAME`: db name +- `DB_PORT`: db port +- `DB_SSL_JSON`: enable or disable using ssl to connect to the database +- `DB_TIMEOUT`: ex: 29000. Used to set the pg-promise settings for query_timeout and statement_timeout. +- `OBO_LTI_KEYS_JSON`: ex: '{"key1":"secret1","key2":"secret2}'. Use json string to define valid lti key/secret pairs. +- `OBO_LTI_USERNAME_PARAM`: What lti launch param do you want to use as the username in obojobo. ex: `user_id` or `lis_person_sourcedid` +- `OBO_COOKIE_SECRET`: Randomized secret key that is used to encrypt cookies. See the docs for express-session. +- `OBO_COOKIE_SECURE`: Set to true when using https. See docs for express-session. Used to set secure, sameSite and httpOnly options. +- `DEBUG`: Logging Verbosity. Use obojobo_server:* for more output. ex: `obojobo_server:error,obojobo_server:warn` +- `YARN_PRODUCTION`: Set to true. Makes sure yarn install includes dev dependencies. +- `OBO_DEMO_PURGE_MODE`: Automatically purge old data for demo purposes or to limit database growth. Additional addon setup required. ex: disabled (default), DANGER-delete-HISTORY-data, DANGER-delete-ALL-data +- `OBO_DEMO_PURGE_DAYS_AGO`: If purge mode is enabled, purge data older than this many days. ex: 7 +- `OBO_EDITLOCK_TIME_UNTIL_RELEASE_MINUTES`: Editor lock: period of inactivity to release the lock. ex: 45 +- `OBO_EDITLOCK_TIME_UNTIL_WARN_MINUTES`: Editor lock: period of inactivity to warn the user about lock release. ex: 40 +- `OBO_EDITLOCK_DB_LOCK_DURATION`: Editor lock: period of time a lock lasts unless it is renewed. ex: 5 +- `OBO_LTI_GUID`: A GUID unique to this install of Obojobo. Google LTI launch param tool_consumer_instance_guid for more details. ex: `edu.your-school.obojobo` +- `MATERIA_OAUTH_KEY`: LTI Oauth key used just for the optional Materia integration. +- `MATERIA_OAUTH_SECRET`: LTI Oauth secret used the optional Materia integration. +- `MATERIA_HOST`: URL for the optional Materia integration. ex: https://your.materia.com +- `OBO_OPTIONAL_NODES`: Comma separated list of optional nodes that are already installed to enable. ex: `*` or `obojobo-chunks-materia` + ### Special Thanks Support for this work was provided by the National Science Foundation Scholarships in Science, Technology, Engineering, and Mathematics (S-STEM) program under [Award No.1643835](https://www.nsf.gov/awardsearch/showAward?AWD_ID=1643835). Any opinions, findings, conclusions and recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation. diff --git a/packages/app/obojobo-express/__tests__/asset_resolver.test.js b/packages/app/obojobo-express/__tests__/asset_resolver.test.js index d6549bb5ae..289286c3b3 100644 --- a/packages/app/obojobo-express/__tests__/asset_resolver.test.js +++ b/packages/app/obojobo-express/__tests__/asset_resolver.test.js @@ -14,10 +14,12 @@ const { assetForEnv } = require('../server/asset_resolver') describe('Asset Resolver', () => { const originalNODE_ENV = process.env.NODE_ENV const originalIS_WEBPACK = process.env.IS_WEBPACK + const originalCDN_ASSET_HOST = process.env.CDN_ASSET_HOST afterAll(() => { process.env.NODE_ENV = originalNODE_ENV process.env.IS_WEBPACK = originalIS_WEBPACK + process.env.CDN_ASSET_HOST = originalCDN_ASSET_HOST }) beforeEach(() => { @@ -25,6 +27,7 @@ describe('Asset Resolver', () => { delete process.env.NODE_ENV delete process.env.ASSET_ENV delete process.env.IS_WEBPACK + delete process.env.CDN_ASSET_HOST }) test('assetForEnv builds pattern for dev server', () => { @@ -90,4 +93,14 @@ describe('Asset Resolver', () => { expect(webpackAssetPath('subdir/test.json')).toBe('/static-from-manifest/subdir/test.json') expect(webpackAssetPath('asset-that-doesnt-exist.js')).toBe(undefined) //eslint-disable-line no-undefined }) + + test('CDN_ASSET_HOST is prepended to manifest assets when present', () => { + process.env.IS_WEBPACK = 'false' + process.env.CDN_ASSET_HOST = 'https://test-cdn-value.com' + const { webpackAssetPath } = require('../server/asset_resolver') + + expect(webpackAssetPath('test.js')).toBe( + 'https://test-cdn-value.com/static-from-manifest/test.js' + ) + }) }) diff --git a/packages/app/obojobo-express/server/asset_resolver.js b/packages/app/obojobo-express/server/asset_resolver.js index a66bc04d32..be29fc15f5 100644 --- a/packages/app/obojobo-express/server/asset_resolver.js +++ b/packages/app/obojobo-express/server/asset_resolver.js @@ -43,12 +43,19 @@ const getEnv = forceEnvTo => { return (env || NODE_ENV).toLowerCase() } +const resolveFromManifest = assetName => { + // eslint-disable-next-line no-undefined + if (!manifest[assetName]) return undefined + return `${process.env.CDN_ASSET_HOST || ''}${manifest[assetName]}` +} + +// use the original name in the static path +const resolveFromWebpackDevServer = assetName => `/static/${assetName}` + const IS_WEBPACK = process.env.IS_WEBPACK === 'true' // NOTE: manifest created via `yarn build` const manifest = IS_WEBPACK ? {} : require('./public/compiled/manifest.json') -const webpackAssetPath = IS_WEBPACK - ? assetName => `/static/${assetName}` // use the original name in the static path - : assetName => manifest[assetName] // return path from the manifest +const webpackAssetPath = IS_WEBPACK ? resolveFromWebpackDevServer : resolveFromManifest module.exports = { assetForEnv, From 94f0787be8ab16db6e824bb4ec1472290595f04c Mon Sep 17 00:00:00 2001 From: iturgeon Date: Sat, 29 Jan 2022 18:15:06 -0500 Subject: [PATCH 2/3] allows module images to be loaded from a CDN Now, setting CDN_ASSET_HOST will now prepend module image urls to the configured CDN. This required updates to some of the repository changes to pass a server variable to the universal react pages so they can render correctly on SSR and CSR environments. --- .../app/obojobo-repository/server/events.js | 15 ++++++++++--- .../server/routes/dashboard.js | 5 ++++- .../server/routes/library.js | 21 ++++++++++++++++--- .../obojobo-repository/server/routes/stats.js | 5 ++++- .../shared/components/module-image.jsx | 3 ++- .../components/pages/page-module-client.jsx | 7 ++++--- .../components/pages/page-module-hoc.js | 7 +++++++ .../components/pages/page-module-server.jsx | 8 +++++-- .../pages/page-module-server.test.js | 3 +++ .../obojobo-repository/shared/react-utils.js | 19 ++++++++++++++++- .../shared/reducers/about-module-reducer.js | 5 +++++ .../shared/util/client-globals.js | 2 ++ 12 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 packages/app/obojobo-repository/shared/components/pages/page-module-hoc.js create mode 100644 packages/app/obojobo-repository/shared/reducers/about-module-reducer.js create mode 100644 packages/app/obojobo-repository/shared/util/client-globals.js diff --git a/packages/app/obojobo-repository/server/events.js b/packages/app/obojobo-repository/server/events.js index 9eb7691c76..b9c11b8add 100644 --- a/packages/app/obojobo-repository/server/events.js +++ b/packages/app/obojobo-repository/server/events.js @@ -28,7 +28,10 @@ oboEvents.on('HTTP_NOT_AUTHORIZED', ({ req, res, next }) => { title: 'Not Authorized', children: 'You do not have the permissions required to view this page.', currentUser: req.currentUser, - appCSSUrl + appCSSUrl, + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-error.jsx', props) }) @@ -42,7 +45,10 @@ oboEvents.on('HTTP_NOT_FOUND', ({ req, res, next }) => { title: 'Not Found', children: "The page you requested doesn't exist.", currentUser: req.currentUser, - appCSSUrl + appCSSUrl, + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-error.jsx', props) }) @@ -56,7 +62,10 @@ oboEvents.on('HTTP_UNEXPECTED', ({ req, res, next }) => { title: 'Unexpected Server Error', children: 'There was an internal server error.', currentUser: req.currentUser, - appCSSUrl + appCSSUrl, + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-error.jsx', props) }) diff --git a/packages/app/obojobo-repository/server/routes/dashboard.js b/packages/app/obojobo-repository/server/routes/dashboard.js index 2f5e759744..3cfac192a7 100644 --- a/packages/app/obojobo-repository/server/routes/dashboard.js +++ b/packages/app/obojobo-repository/server/routes/dashboard.js @@ -30,7 +30,10 @@ router currentUser: req.currentUser, // must use webpackAssetPath for all webpack assets to work in dev and production! appCSSUrl: webpackAssetPath('dashboard.css'), - appJsUrl: webpackAssetPath('dashboard.js') + appJsUrl: webpackAssetPath('dashboard.js'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-dashboard-server.jsx', props) }) diff --git a/packages/app/obojobo-repository/server/routes/library.js b/packages/app/obojobo-repository/server/routes/library.js index 375aef64b8..08d1b748d6 100644 --- a/packages/app/obojobo-repository/server/routes/library.js +++ b/packages/app/obojobo-repository/server/routes/library.js @@ -11,6 +11,7 @@ const { requireDraftId, getCurrentUser } = require('obojobo-express/server/express_validators') +const { populateClientGlobals } = require('../../shared/react-utils') const publicLibCollectionId = require('../../shared/publicLibCollectionId') @@ -21,7 +22,10 @@ router const props = { currentUser: req.currentUser, // must use webpackAssetPath for all webpack assets to work in dev and production! - appCSSUrl: webpackAssetPath('homepage.css') + appCSSUrl: webpackAssetPath('homepage.css'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-homepage.jsx', props) }) @@ -102,7 +106,10 @@ router const props = { currentUser: req.currentUser, // must use webpackAssetPath for all webpack assets to work in dev and production! - appCSSUrl: webpackAssetPath('repository.css') + appCSSUrl: webpackAssetPath('repository.css'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-login.jsx', props) }) @@ -122,8 +129,12 @@ router pageCount: 1, currentUser: req.currentUser, // must use webpackAssetPath for all webpack assets to work in dev and production! - appCSSUrl: webpackAssetPath('repository.css') + appCSSUrl: webpackAssetPath('repository.css'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } + populateClientGlobals(props) res.render('pages/page-library.jsx', props) }) .catch(res.unexpected) @@ -159,8 +170,12 @@ router // must use webpackAssetPath for all webpack assets to work in dev and production! appCSSUrl: webpackAssetPath('repository.css'), appJsUrl: webpackAssetPath('page-module.js'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + }, canCopy } + res.render('pages/page-module-server.jsx', props) } catch (e) { res.unexpected(e) diff --git a/packages/app/obojobo-repository/server/routes/stats.js b/packages/app/obojobo-repository/server/routes/stats.js index 766d8370ad..738e8594d5 100644 --- a/packages/app/obojobo-repository/server/routes/stats.js +++ b/packages/app/obojobo-repository/server/routes/stats.js @@ -18,7 +18,10 @@ router currentUser: req.currentUser, // must use webpackAssetPath for all webpack assets to work in dev and production! appCSSUrl: webpackAssetPath('stats.css'), - appJsUrl: webpackAssetPath('stats.js') + appJsUrl: webpackAssetPath('stats.js'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-stats-server.jsx', props) }) diff --git a/packages/app/obojobo-repository/shared/components/module-image.jsx b/packages/app/obojobo-repository/shared/components/module-image.jsx index f58cc9061a..7ac2d10bd2 100644 --- a/packages/app/obojobo-repository/shared/components/module-image.jsx +++ b/packages/app/obojobo-repository/shared/components/module-image.jsx @@ -1,10 +1,11 @@ require('./module-image.scss') const React = require('react') +const clientGlobals = require('../util/client-globals') const ModuleImage = props => (
- +
) diff --git a/packages/app/obojobo-repository/shared/components/pages/page-module-client.jsx b/packages/app/obojobo-repository/shared/components/pages/page-module-client.jsx index 2a30f50fae..cc58f895af 100644 --- a/packages/app/obojobo-repository/shared/components/pages/page-module-client.jsx +++ b/packages/app/obojobo-repository/shared/components/pages/page-module-client.jsx @@ -1,4 +1,5 @@ -import { hydrateElWithoutStore } from '../../react-utils' -import PageModule from './page-module.jsx' +import { hydrateEl } from '../../react-utils' +import PageModule from './page-module-hoc' +import AboutModuleReducer from '../../reducers/about-module-reducer' -hydrateElWithoutStore(PageModule, '#react-hydrate-root') +hydrateEl(PageModule, AboutModuleReducer, '#react-hydrate-root') diff --git a/packages/app/obojobo-repository/shared/components/pages/page-module-hoc.js b/packages/app/obojobo-repository/shared/components/pages/page-module-hoc.js new file mode 100644 index 0000000000..19fcb10d10 --- /dev/null +++ b/packages/app/obojobo-repository/shared/components/pages/page-module-hoc.js @@ -0,0 +1,7 @@ +const PageModule = require('./page-module') +const connect = require('react-redux').connect +const mapStoreStateToProps = state => state +module.exports = connect( + mapStoreStateToProps, + {} +)(PageModule) diff --git a/packages/app/obojobo-repository/shared/components/pages/page-module-server.jsx b/packages/app/obojobo-repository/shared/components/pages/page-module-server.jsx index 570eb07d7f..1b083872d9 100644 --- a/packages/app/obojobo-repository/shared/components/pages/page-module-server.jsx +++ b/packages/app/obojobo-repository/shared/components/pages/page-module-server.jsx @@ -1,6 +1,8 @@ const React = require('react') const DefaultLayout = require('../layouts/default') -const { convertPropsToString } = require('../../react-utils') +const { propsToStore, createCommonReactApp, convertPropsToString } = require('../../react-utils') +const PageModule = require('./page-module-hoc') +const AboutModuleReducer = require('../../reducers/about-module-reducer') const PageModuleServer = props => { return ( @@ -10,7 +12,9 @@ const PageModuleServer = props => { appScriptUrl={props.appJsUrl} appCSSUrl={props.appCSSUrl} > - + + {createCommonReactApp(PageModule, propsToStore(AboutModuleReducer, props))} + ) } diff --git a/packages/app/obojobo-repository/shared/components/pages/page-module-server.test.js b/packages/app/obojobo-repository/shared/components/pages/page-module-server.test.js index 36303a4781..48087e21e5 100644 --- a/packages/app/obojobo-repository/shared/components/pages/page-module-server.test.js +++ b/packages/app/obojobo-repository/shared/components/pages/page-module-server.test.js @@ -12,6 +12,9 @@ describe('Server-side Module Page', () => { const mockProps = { appJsUrl: '/path/to/js', appCSSUrl: '/path/to/css', + globals: { + staticAssetUrl: 'https://cdn.sample.com' + }, module: { title: 'mockModuleTitle' } diff --git a/packages/app/obojobo-repository/shared/react-utils.js b/packages/app/obojobo-repository/shared/react-utils.js index feb3ea57b2..58a1490984 100644 --- a/packages/app/obojobo-repository/shared/react-utils.js +++ b/packages/app/obojobo-repository/shared/react-utils.js @@ -3,6 +3,20 @@ const { Provider } = require('react-redux') const { createStore, applyMiddleware, compose } = require('redux') const { middleware } = require('redux-pack') +// if initial state contains a globals object, we need to copy +// those globals into clientGlobals so that they can be loaded +// as global constants anywhere in the application. +function populateClientGlobals(initialState){ + if(initialState.globals){ + const clientGlobals = require('../shared/util/client-globals') + for (const property in initialState.globals) { + clientGlobals[property] = initialState.globals[property] + } + // freeze to prevent changes + Object.freeze(clientGlobals) + } +} + // used in the browser to hydrate a SSR page w/o Redux function hydrateElWithoutStore(Component, domSelector) { const domEl = document.querySelector(domSelector) @@ -32,7 +46,9 @@ function propsToStore(reducer, initialState, optionalMiddleware) { return createStore(reducer, initialState, preppedMiddleware) } +// this function is called on both the server and the client function createCommonReactApp(Component, store) { + populateClientGlobals(store.getState()) const app = React.createElement(Component) const provider = React.createElement(Provider, { store }, app) return provider @@ -53,5 +69,6 @@ module.exports = { hydrateElWithoutStore, propsToStore, createCommonReactApp, - convertPropsToString + convertPropsToString, + populateClientGlobals } diff --git a/packages/app/obojobo-repository/shared/reducers/about-module-reducer.js b/packages/app/obojobo-repository/shared/reducers/about-module-reducer.js new file mode 100644 index 0000000000..6f9cb0258a --- /dev/null +++ b/packages/app/obojobo-repository/shared/reducers/about-module-reducer.js @@ -0,0 +1,5 @@ +function AboutModuleReducer(state, action) { + return state +} + +module.exports = AboutModuleReducer diff --git a/packages/app/obojobo-repository/shared/util/client-globals.js b/packages/app/obojobo-repository/shared/util/client-globals.js new file mode 100644 index 0000000000..97a58c9d07 --- /dev/null +++ b/packages/app/obojobo-repository/shared/util/client-globals.js @@ -0,0 +1,2 @@ +const clientGlobals = {} +module.exports = clientGlobals From 0fefa407e9aa01aab8f1b56cedfc75de76254dc2 Mon Sep 17 00:00:00 2001 From: iturgeon Date: Mon, 31 Jan 2022 00:36:29 -0500 Subject: [PATCH 3/3] update tests to work with extended cdn changes --- .../server/routes/library.test.js | 4 +--- .../shared/components/module-image.jsx | 6 +++++- .../components/pages/page-module-client.test.js | 13 +++++++++---- .../shared/components/pages/page-module-hoc.js | 1 + .../obojobo-repository/shared/react-utils.js | 4 ++-- .../shared/react-utils.test.js | 17 ++++++++++++++++- .../shared/reducers/about-module-reducer.js | 4 +++- 7 files changed, 37 insertions(+), 12 deletions(-) diff --git a/packages/app/obojobo-repository/server/routes/library.test.js b/packages/app/obojobo-repository/server/routes/library.test.js index 757fd6afe9..049a3542bf 100644 --- a/packages/app/obojobo-repository/server/routes/library.test.js +++ b/packages/app/obojobo-repository/server/routes/library.test.js @@ -2,6 +2,7 @@ jest.mock('../models/collection') jest.mock('../models/draft_summary') jest.mock('obojobo-express/server/models/user') jest.mock('../models/draft_permissions') +jest.mock('../../shared/react-utils') jest.mock('trianglify') jest.unmock('fs') // need fs working for view rendering jest.unmock('express') // we'll use supertest + express for this @@ -17,14 +18,11 @@ jest.mock( jest.setTimeout(10000) // extend test timeout? const publicLibCollectionId = require('../../shared/publicLibCollectionId') - let trianglify - let Collection let DraftSummary let UserModel let DraftPermissions - // override requireCurrentUser for tests to provide our own user let mockCurrentUser diff --git a/packages/app/obojobo-repository/shared/components/module-image.jsx b/packages/app/obojobo-repository/shared/components/module-image.jsx index 7ac2d10bd2..ee5f6f4f1c 100644 --- a/packages/app/obojobo-repository/shared/components/module-image.jsx +++ b/packages/app/obojobo-repository/shared/components/module-image.jsx @@ -5,7 +5,11 @@ const clientGlobals = require('../util/client-globals') const ModuleImage = props => (
- +
) diff --git a/packages/app/obojobo-repository/shared/components/pages/page-module-client.test.js b/packages/app/obojobo-repository/shared/components/pages/page-module-client.test.js index 335de5e27e..4823052adb 100644 --- a/packages/app/obojobo-repository/shared/components/pages/page-module-client.test.js +++ b/packages/app/obojobo-repository/shared/components/pages/page-module-client.test.js @@ -1,15 +1,20 @@ jest.mock('../../react-utils') -jest.mock('./page-module') -jest.mock('../../reducers/dashboard-reducer') +jest.mock('./page-module-hoc') +jest.mock('../../reducers/about-module-reducer') const ReactUtils = require('../../react-utils') -const PageModule = require('./page-module') +const PageModule = require('./page-module-hoc') +const AboutModuleReducer = require('../../reducers/about-module-reducer') describe('Client-side Module Page', () => { test('passes the right arguments to ReactUtils.hydrateElWithoutStore', () => { // just need to require this, it will run itself require('./page-module-client') - expect(ReactUtils.hydrateElWithoutStore).toHaveBeenCalledWith(PageModule, '#react-hydrate-root') + expect(ReactUtils.hydrateEl).toHaveBeenCalledWith( + PageModule, + AboutModuleReducer, + '#react-hydrate-root' + ) }) }) diff --git a/packages/app/obojobo-repository/shared/components/pages/page-module-hoc.js b/packages/app/obojobo-repository/shared/components/pages/page-module-hoc.js index 19fcb10d10..c89c9cbe88 100644 --- a/packages/app/obojobo-repository/shared/components/pages/page-module-hoc.js +++ b/packages/app/obojobo-repository/shared/components/pages/page-module-hoc.js @@ -1,5 +1,6 @@ const PageModule = require('./page-module') const connect = require('react-redux').connect +/* istanbul ignore next */ const mapStoreStateToProps = state => state module.exports = connect( mapStoreStateToProps, diff --git a/packages/app/obojobo-repository/shared/react-utils.js b/packages/app/obojobo-repository/shared/react-utils.js index 58a1490984..4129fe2969 100644 --- a/packages/app/obojobo-repository/shared/react-utils.js +++ b/packages/app/obojobo-repository/shared/react-utils.js @@ -6,8 +6,8 @@ const { middleware } = require('redux-pack') // if initial state contains a globals object, we need to copy // those globals into clientGlobals so that they can be loaded // as global constants anywhere in the application. -function populateClientGlobals(initialState){ - if(initialState.globals){ +function populateClientGlobals(initialState) { + if (initialState && initialState.globals) { const clientGlobals = require('../shared/util/client-globals') for (const property in initialState.globals) { clientGlobals[property] = initialState.globals[property] diff --git a/packages/app/obojobo-repository/shared/react-utils.test.js b/packages/app/obojobo-repository/shared/react-utils.test.js index ec414e5401..d772350092 100644 --- a/packages/app/obojobo-repository/shared/react-utils.test.js +++ b/packages/app/obojobo-repository/shared/react-utils.test.js @@ -5,6 +5,7 @@ jest.mock('react-redux', () => ({ Provider: jest.fn() })) jest.mock('redux-pack') +jest.mock('../shared/util/client-globals') let React let Redux // { createStore, applyMiddleware } @@ -32,7 +33,7 @@ describe('react utils', () => { mockReducerFunction: jest.fn() } const mockMiddleware = { middlewareKey: 'middlewareProp' } - const mockStore = { storeKey: 'storeProp' } + const mockStore = { storeKey: 'storeProp', getState: jest.fn().mockReturnValue({}) } beforeAll(() => { global.ReactDOM = { @@ -161,4 +162,18 @@ describe('react utils', () => { desiredKeyTwo: 'desiredPropTwo' }) }) + + test('populateClientGlobals ignores missing globals', () => { + const clientGlobals = require('../shared/util/client-globals') + expect(clientGlobals).not.toHaveProperty('key') + reactUtils.populateClientGlobals({ nonGlobals: { key: 'value' } }) + expect(clientGlobals).not.toHaveProperty('key') + }) + + test('populateClientGlobals registers globals', () => { + const clientGlobals = require('../shared/util/client-globals') + expect(clientGlobals).not.toHaveProperty('key') + reactUtils.populateClientGlobals({ globals: { key: 'value' } }) + expect(clientGlobals).toHaveProperty('key', 'value') + }) }) diff --git a/packages/app/obojobo-repository/shared/reducers/about-module-reducer.js b/packages/app/obojobo-repository/shared/reducers/about-module-reducer.js index 6f9cb0258a..3de52cfb54 100644 --- a/packages/app/obojobo-repository/shared/reducers/about-module-reducer.js +++ b/packages/app/obojobo-repository/shared/reducers/about-module-reducer.js @@ -1,4 +1,6 @@ -function AboutModuleReducer(state, action) { +/* istanbul ignore next */ +function AboutModuleReducer(state) { + /* istanbul ignore next */ return state }