diff --git a/App.js b/App.js index 426ce63..fc85faa 100644 --- a/App.js +++ b/App.js @@ -12,9 +12,7 @@ if (!window.Intl) { }) .then(() => Promise.all([import('intl/locale-data/jsonp/en.js')])) .then(() => AppRegistry.registerComponent(appName, () => App)) - .catch(err => { - throw err; - }); + .catch(alert); } else { AppRegistry.registerComponent(appName, () => App); } diff --git a/app/components/atoms/If/index.js b/app/components/atoms/If/index.js new file mode 100644 index 0000000..551e325 --- /dev/null +++ b/app/components/atoms/If/index.js @@ -0,0 +1,26 @@ +/** + * + * If + * + */ +import PropTypes from 'prop-types'; + +const If = props => (props.condition ? props.children : props.otherwise); + +If.propTypes = { + condition: PropTypes.bool, + otherwise: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node + ]), + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node + ]) +}; + +If.defaultProps = { + otherwise: null +}; + +export default If; diff --git a/app/components/molecules/If/tests/__snapshots__/index.test.js.snap b/app/components/atoms/If/tests/__snapshots__/index.test.js.snap similarity index 100% rename from app/components/molecules/If/tests/__snapshots__/index.test.js.snap rename to app/components/atoms/If/tests/__snapshots__/index.test.js.snap diff --git a/app/components/molecules/If/tests/index.test.js b/app/components/atoms/If/tests/index.test.js similarity index 51% rename from app/components/molecules/If/tests/index.test.js rename to app/components/atoms/If/tests/index.test.js index 52e0090..387c410 100644 --- a/app/components/molecules/If/tests/index.test.js +++ b/app/components/atoms/If/tests/index.test.js @@ -9,7 +9,6 @@ import React from 'react'; import { Text } from 'react-native'; import { render } from '@testing-library/react-native'; - import If from '../index'; describe('', () => { @@ -17,8 +16,9 @@ describe('', () => { const baseElement = render(); expect(baseElement).toMatchSnapshot(); }); - it('Should render the correct prop component based on the condition', () => { - const conditionTrueText = 'Shoudld render when condition is true'; + + it('If should render the child component when props.condition = true', () => { + const conditionTrueText = 'Should render when condition is true'; const conditionFalseText = 'Should render condition is false'; const OtherwiseComponent = () => {conditionFalseText}; const TrueConditionComponent = () => {conditionTrueText}; @@ -26,18 +26,31 @@ describe('', () => { otherwise: , condition: true }; - const { getByText } = render( + const { queryByText, getByText } = render( ); expect(getByText(conditionTrueText)).toBeTruthy(); - props.condition = false; - const { getByText: textQueryOnReRender } = render( + expect(queryByText(conditionFalseText)).not.toBeTruthy(); + }); + + it('If should render the component passed on otherwise when props.condition = false', () => { + const conditionFalseText = 'Should render condition is false'; + const conditionTrueText = 'Should render when condition is true'; + const TrueConditionComponent = () => {conditionTrueText}; + const OtherwiseComponent = () => {conditionFalseText}; + const props = { + otherwise: , + condition: false + }; + + const { queryByText, getByText } = render( ); - expect(textQueryOnReRender(conditionFalseText)).toBeTruthy(); + expect(getByText(conditionFalseText)).toBeTruthy(); + expect(queryByText(conditionTrueText)).not.toBeTruthy(); }); }); diff --git a/app/components/molecules/CharacterWithQuote/index.js b/app/components/molecules/CharacterWithQuote/index.js index 3ccb5a7..c5c6ed6 100644 --- a/app/components/molecules/CharacterWithQuote/index.js +++ b/app/components/molecules/CharacterWithQuote/index.js @@ -1,6 +1,6 @@ import React from 'react'; import styled from 'styled-components/native'; -import { get } from 'lodash'; +import get from 'lodash/get'; import PropTypes from 'prop-types'; import { fonts } from '@themes'; import T from '@atoms/T'; @@ -23,7 +23,7 @@ function CharacterWithQuote({ user }) { diff --git a/app/components/molecules/If/index.js b/app/components/molecules/If/index.js deleted file mode 100644 index eb4c77b..0000000 --- a/app/components/molecules/If/index.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * - * If - * - */ -// eslint-disable-next-line -import React from 'react' -import Proptypes from 'prop-types'; -const If = props => (props.condition ? props.children : props.otherwise); -If.propsTypes = { - condition: Proptypes.bool, - otherwise: Proptypes.oneOfType([ - Proptypes.arrayOf(Proptypes.node), - Proptypes.node - ]), - children: Proptypes.oneOfType([ - Proptypes.arrayOf(Proptypes.node), - Proptypes.node - ]) -}; -If.defaultProps = { - otherwise: null -}; -export default If; diff --git a/app/components/organisms/SimpsonsLoveWednesday/index.js b/app/components/organisms/SimpsonsLoveWednesday/index.js index ffb6c5a..1affdb3 100644 --- a/app/components/organisms/SimpsonsLoveWednesday/index.js +++ b/app/components/organisms/SimpsonsLoveWednesday/index.js @@ -2,11 +2,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components/native'; import { fonts } from '@themes'; -import If from '@molecules/If'; +import If from '@app/components/atoms/If'; import CharacterWithQuote from '@molecules/CharacterWithQuote'; import LogoWithInstructions from '@molecules/LogoWithInstructions'; -const Error = styled.Text` +const Err = styled.Text` ${fonts.style.standard()}; text-align: center; margin-bottom: 5px; @@ -25,7 +25,7 @@ function SimpsonsLoveWednesday({ instructions, user, userErrorMessage }) { {userErrorMessage}} + otherwise={{userErrorMessage}} > diff --git a/app/components/organisms/SimpsonsLoveWednesday/tests/index.test.js b/app/components/organisms/SimpsonsLoveWednesday/tests/index.test.js index 9978c9c..0be176e 100644 --- a/app/components/organisms/SimpsonsLoveWednesday/tests/index.test.js +++ b/app/components/organisms/SimpsonsLoveWednesday/tests/index.test.js @@ -10,7 +10,6 @@ import React from 'react'; import { renderWithIntl } from '@utils/testUtils'; import { rerender } from '@testing-library/react-native'; import SimpsonsLoveWednesday from '../index'; - describe('', () => { it('Should render and match the snapshot', () => { const baseElement = renderWithIntl(); @@ -29,7 +28,18 @@ describe('', () => { }; const { getByText } = renderWithIntl(); expect(getByText(props.userErrorMessage)).toBeTruthy(); - props.userErrorMessage = null; + }); + it('Should render the component if userErrorMessage is empty', () => { + const props = { + userErrorMessage: null, + instructions: 'PRESS CMD + D for iOS', + user: { + character: 'Homer', + image: + 'https://www.onthisday.com/images/people/homer-simpson-medium.jpg', + quote: "D'Oh!" + } + }; const { getByText: textQueryOnReRender } = renderWithIntl( , rerender diff --git a/app/i18n.js b/app/i18n.js index 87c780f..6e375c1 100644 --- a/app/i18n.js +++ b/app/i18n.js @@ -7,12 +7,12 @@ * script `extract-intl`, and must use CommonJS module syntax * You CANNOT use import/export in this file. */ +import get from 'lodash/get'; const addLocaleData = require('react-intl').addLocaleData; //eslint-disable-line const enLocaleData = require('react-intl/locale-data/en'); const enTranslationMessages = require('./translations/en.json'); - addLocaleData(enLocaleData); export const DEFAULT_LOCALE = 'en'; @@ -28,11 +28,15 @@ export const formatTranslationMessages = (locale, messages) => { ? formatTranslationMessages(DEFAULT_LOCALE, enTranslationMessages) : {}; const flattenFormattedMessages = (formattedMessages, key) => { - const formattedMessage = - !messages[key] && locale !== DEFAULT_LOCALE - ? defaultFormattedMessages[key] - : messages[key]; - return Object.assign(formattedMessages, { [key]: formattedMessage }); + const formattedMessageOptions = { + true: get(defaultFormattedMessages, key), + false: get(messages, key) + }; + const formattedCondition = !get(messages, key) && locale !== DEFAULT_LOCALE; + return { + ...formattedMessages, + [key]: get(formattedMessageOptions, formattedCondition) + }; }; return Object.keys(messages).reduce(flattenFormattedMessages, {}); }; diff --git a/app/utils/apiUtils.js b/app/utils/apiUtils.js index 433a357..713aef2 100644 --- a/app/utils/apiUtils.js +++ b/app/utils/apiUtils.js @@ -1,6 +1,9 @@ import { create } from 'apisauce'; import mapKeysDeep from 'map-keys-deep'; -import { camelCase, snakeCase } from 'lodash'; +import camelCase from 'lodash/camelCase'; +import snakeCase from 'lodash/snakeCase'; +import set from 'lodash/set'; +import get from 'lodash/get'; import { Config } from '@app/config/index'; export const apiClients = { @@ -11,10 +14,10 @@ export const getApiClient = (type = 'configApi') => apiClients[type]; export const generateApiClient = (type = 'configApi') => { switch (type) { case 'configApi': - apiClients[type] = createApiClientWithTransForm(Config.API_URL); - return apiClients[type]; + set(apiClients, type, createApiClientWithTransForm(Config.API_URL)); + return get(apiClients, type); default: - apiClients.default = createApiClientWithTransForm(Config.API_URL); + set(apiClients, 'default', createApiClientWithTransForm(Config.API_URL)); return apiClients.default; } }; @@ -27,7 +30,11 @@ export const createApiClientWithTransForm = baseURL => { api.addResponseTransform(response => { const { ok, data } = response; if (ok && data) { - response.data = mapKeysDeep(data, keys => camelCase(keys)); + set( + response, + 'data', + mapKeysDeep(data, keys => camelCase(keys)) + ); } return response; }); @@ -35,7 +42,11 @@ export const createApiClientWithTransForm = baseURL => { api.addRequestTransform(request => { const { data } = request; if (data) { - request.data = mapKeysDeep(data, keys => snakeCase(keys)); + set( + request, + 'data', + mapKeysDeep(data, keys => snakeCase(keys)) + ); } return request; }); diff --git a/app/utils/createStore.js b/app/utils/createStore.js index ce230ca..fd5287a 100644 --- a/app/utils/createStore.js +++ b/app/utils/createStore.js @@ -26,14 +26,10 @@ const persistConfig = { }; export default (rootReducer, rootSaga) => { - const middleware = []; - const enhancers = []; - - // Connect the sagas to the redux store + // connect sagas to redux store const sagaMiddleware = createSagaMiddleware(); - middleware.push(sagaMiddleware); - - enhancers.push(applyMiddleware(...middleware)); + const middleware = [sagaMiddleware]; + const enhancers = [applyMiddleware(...middleware)]; // Redux persist const persistedReducer = persistReducer(persistConfig, rootReducer); diff --git a/app/utils/testUtils.js b/app/utils/testUtils.js index 86a7656..bad8004 100644 --- a/app/utils/testUtils.js +++ b/app/utils/testUtils.js @@ -3,6 +3,7 @@ import { IntlProvider } from 'react-intl'; import { render } from '@testing-library/react-native'; import { Provider } from 'react-redux'; import createStore from 'app/rootReducer'; +import get from 'lodash/get'; import { DEFAULT_LOCALE, translationMessages } from '@app/i18n'; import ConnectedLanguageProvider from '@atoms/LanguageProvider'; @@ -14,7 +15,7 @@ export const renderWithIntl = (children, renderFunction = render) => renderFunction( {children} diff --git a/babel.config.js b/babel.config.js index 88d868c..f4146e1 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,4 +1,4 @@ -module.exports = function(api = { cache: () => {} }) { +module.exports = (api = { cache: () => {} }) => { api.cache(true); return { presets: ['babel-preset-expo'], diff --git a/setupTests.js b/setupTests.js index 58d906e..34d761d 100644 --- a/setupTests.js +++ b/setupTests.js @@ -1,4 +1,4 @@ import mockAsyncStorage from '@react-native-async-storage/async-storage/jest/async-storage-mock'; - +import { LogBox } from 'react-native'; jest.mock('@react-native-async-storage/async-storage', () => mockAsyncStorage); -console.disableYellowBox = true; +LogBox.ignoreAllLogs();