diff --git a/app/actions/__tests__/invoices.spec.js b/app/actions/__tests__/invoices.spec.js index 8e59411e..ed361531 100644 --- a/app/actions/__tests__/invoices.spec.js +++ b/app/actions/__tests__/invoices.spec.js @@ -1,7 +1,7 @@ import * as ACTION_TYPES from '../../constants/actions.jsx'; import * as actions from '../invoices'; -it('getInvoices should create GET_INVOICES action', () => { +it('getInvoices should create INVOICE_GET_ALL action', () => { expect(actions.getInvoices()).toEqual({ type: ACTION_TYPES.INVOICE_GET_ALL, }); diff --git a/app/middlewares/SettingsMW.jsx b/app/middlewares/SettingsMW.jsx index caa9f427..041bb50f 100644 --- a/app/middlewares/SettingsMW.jsx +++ b/app/middlewares/SettingsMW.jsx @@ -32,10 +32,16 @@ const SettingsMW = ({ dispatch }) => next => action => { // Validation if (!validateTax(true, action.payload.invoice.tax)) break; if (!validateCurrency(true, action.payload.invoice.currency)) break; + // Change Preview Profile + const profile = appConfig.get('profile'); + const newProfile = action.payload.profile; + if (profile !== newProfile) { + ipc.send('change-preview-window-profile', newProfile); + } // Change UI language - const currentLang = appConfig.get('general.language'); + const { language } = appConfig.get('general'); const newLang = action.payload.general.language; - if (currentLang !== newLang) { + if (language !== newLang) { // Change the language i18n.changeLanguage(newLang); // Notify previewWindow to update diff --git a/app/middlewares/__tests__/SettingsMW.spec.js b/app/middlewares/__tests__/SettingsMW.spec.js index c27e7a0c..cd418e1c 100644 --- a/app/middlewares/__tests__/SettingsMW.spec.js +++ b/app/middlewares/__tests__/SettingsMW.spec.js @@ -1,7 +1,14 @@ +// Libs import * as Actions from '../../actions/settings'; -import SettingsMW from '../SettingsMW'; import * as ACTION_TYPES from '../../constants/actions.jsx'; +import SettingsMW from '../SettingsMW'; +import i18n from '../../../i18n/i18n'; +const sounds = require('../../../libs/sounds'); +const ipc = require('electron').ipcRenderer; +const openDialog = require('../../renderers/dialog'); +import { validateCurrency, validateTax } from '../../helpers/form.js'; +// Mocking jest.mock('../../../libs/sounds'); jest.mock('../../helpers/form'); @@ -33,33 +40,76 @@ describe('Settings Middleware', () => { ); }); - it('should handle SETTINGS_SAVE action', () => { - // TODO - // Validate data - const action = Actions.saveSettings({ - profile: 'someSettings', - invoice: { - currency: { - code: 'USD', - fraction: 2, - separator: 'commaDot', - placement: 'before' + describe('should handle SETTINGS_SAVE action', () => { + let action; + beforeEach(() => { + action = Actions.saveSettings({ + profile: 'New Profile', + general: { + language: 'fr', }, - tax: { - tin: '123-456-789', - amount: 10, - method: 'default' - } - }, - general: 'someSettings', + invoice: { + currency: { + code: 'USD', + fraction: 2, + separator: 'commaDot', + placement: 'before', + }, + tax: { + tin: '123-456-789', + amount: 10, + method: 'default', + }, + }, + }); }); - middleware(action); - // Send Notification - expect(dispatch.mock.calls.length).toBe(1); - expect(next.mock.calls.length).toBe(1); - expect(next).toHaveBeenCalledWith(action); + + it('should validate currency and tax data', () => { + middleware(action); + expect(validateCurrency).toHaveBeenCalled(); + expect(validateCurrency).toHaveBeenCalledWith(true, action.payload.invoice.currency); + expect(validateTax).toHaveBeenCalled(); + expect(validateTax).toHaveBeenCalledWith(true, action.payload.invoice.tax); + }); + // TODO - // check if a sound is played + it('should save data', () => {}); + + it('should reload sounds cache', () => { + middleware(action); + expect(sounds.preload).toHaveBeenCalled(); + }); + + it('should notify previewWindow of language & profile change', () => { + // Clear calls count + ipc.send.mockClear(); + middleware(action); + expect(ipc.send).toHaveBeenCalled(); + expect(ipc.send.mock.calls.length).toEqual(2); + // Notify about profile change + expect(ipc.send.mock.calls[0][0]).toBe('change-preview-window-profile'); + expect(ipc.send.mock.calls[0][1]).toBe('New Profile'); + // Notify about UI Lang change + expect(ipc.send.mock.calls[1][0]).toBe('change-preview-window-language'); + expect(ipc.send.mock.calls[1][1]).toBe('fr'); + }); + + it('should call next and dispatch notification', () => { + middleware(action); + // Call Next + expect(next.mock.calls.length).toBe(1); + expect(next).toHaveBeenCalledWith(action); + // Send Notification + expect(dispatch.mock.calls.length).toBe(1); + expect(dispatch).toHaveBeenCalledWith({ + type: ACTION_TYPES.UI_NOTIFICATION_NEW, + payload: { + type: 'success', + message: i18n.t('messages:settings:saved'), + }, + }); + }); + }); it('let other actions pass through', () => { diff --git a/main/preview-window.js b/main/preview-window.js index 4a09a8ac..47aedf11 100644 --- a/main/preview-window.js +++ b/main/preview-window.js @@ -24,6 +24,11 @@ ipcMain.on('change-preview-window-language', (event, newLang) => { previewWindow.webContents.send('change-preview-window-language', newLang); }); +// Change invoice profile +ipcMain.on('change-preview-window-profile', (event, newProfile) => { + previewWindow.webContents.send('change-preview-window-profile', newProfile); +}); + // Save configs to invoice ipcMain.on('save-configs-to-invoice', (event, invoiceID, configs) => { mainWindow.webContents.send('save-configs-to-invoice', invoiceID, configs); diff --git a/preview/Viewer.jsx b/preview/Viewer.jsx index ab3e1233..bbe89236 100644 --- a/preview/Viewer.jsx +++ b/preview/Viewer.jsx @@ -39,6 +39,10 @@ class Viewer extends Component { ipc.on('change-preview-window-language', (event, newLang) => { dispatch(ActionsCreator.changeUILanguage(newLang)); }); + ipc.on('change-preview-window-profile', (event, newProfile) => { + dispatch(ActionsCreator.updateProfile(newProfile)); + }); + ipc.on('pfd-exported', (event, options) => { const noti = Notify(options); // Handle click on notification diff --git a/preview/actions/__test__/index.spec.js b/preview/actions/__test__/index.spec.js new file mode 100644 index 00000000..7e190795 --- /dev/null +++ b/preview/actions/__test__/index.spec.js @@ -0,0 +1,42 @@ +import * as ACTION_TYPES from '../../constants/actions.jsx'; +import * as actions from '../index.jsx'; + +it('updateInvoice should create INVOICE_UPDATE action', () => { + const invoiceData = { test: 'test'} + expect(actions.updateInvoice(invoiceData)).toEqual({ + type: ACTION_TYPES.INVOICE_UPDATE, + payload: invoiceData, + }); +}); + +it('updateConfigs should create SETTINGS_UPDATE_CONFIGS action', () => { + const configs = { test: 'test'} + expect(actions.updateConfigs(configs)).toEqual({ + type: ACTION_TYPES.SETTINGS_UPDATE_CONFIGS, + payload: configs, + }); +}); + +it('updateProfile should create SETTINGS_UPDATE_PROFILE action', () => { + const profile = { test: 'test'} + expect(actions.updateProfile(profile)).toEqual({ + type: ACTION_TYPES.SETTINGS_UPDATE_PROFILE, + payload: profile, + }); +}); + +it('changeUILanguage should create UI_CHANGE_LANGUAGE action', () => { + const language = 'fr'; + expect(actions.changeUILanguage(language)).toEqual({ + type: ACTION_TYPES.UI_CHANGE_LANGUAGE, + payload: language, + }); +}); + +it('reloadConfigs should create SETTINGS_RELOAD_CONFIGS action', () => { + const newConfigs = { test: 'test'} + expect(actions.reloadConfigs(newConfigs)).toEqual({ + type: ACTION_TYPES.SETTINGS_RELOAD_CONFIGS, + payload: newConfigs, + }); +}); diff --git a/preview/actions/index.jsx b/preview/actions/index.jsx index dad10065..6c6cd95d 100644 --- a/preview/actions/index.jsx +++ b/preview/actions/index.jsx @@ -11,6 +11,11 @@ export const updateConfigs = createAction( configs => configs ); +export const updateProfile = createAction( + ACTION_TYPES.SETTINGS_UPDATE_PROFILE, + profile => profile +); + export const changeUILanguage = createAction( ACTION_TYPES.UI_CHANGE_LANGUAGE, language => language diff --git a/preview/constants/actions.jsx b/preview/constants/actions.jsx index d33fe6f6..eb7a18f9 100644 --- a/preview/constants/actions.jsx +++ b/preview/constants/actions.jsx @@ -7,3 +7,7 @@ export const UI_CHANGE_LANGUAGE = 'UI_CHANGE_LANGUAGE'; // Settings export const SETTINGS_RELOAD_CONFIGS = 'SETTINGS_RELOAD_CONFIGS'; export const SETTINGS_UPDATE_CONFIGS = 'SETTINGS_UPDATE_CONFIGS'; + +// Profile +export const SETTINGS_UPDATE_PROFILE = 'SETTINGS_UPDATE_PROFILE'; + diff --git a/preview/reducers/__test__/index.spec.js b/preview/reducers/__test__/index.spec.js new file mode 100644 index 00000000..7f00d0b5 --- /dev/null +++ b/preview/reducers/__test__/index.spec.js @@ -0,0 +1,120 @@ +import * as ACTION_TYPES from '../../constants/actions.jsx'; +import faker from 'faker'; +import uuidv4 from 'uuid/v4'; +import RootReducer, { + getConfigs, + getInvoice, + getProfile, + getUILang, + getInvoiceLang, +} from '../index.jsx'; + +describe('RootReducer should', () => { + it('handle INVOICE_UPDATE action', () => { + const currentState = { + invoice: {}, + configs: {}, + }; + const newState = RootReducer(currentState, { + type: ACTION_TYPES.INVOICE_UPDATE, + payload: { + invoice: 'invoice', + configs: 'configs', + } + }) + expect(newState.invoice).toEqual({ + invoice: 'invoice', + configs: 'configs', + }); + expect(newState.configs).toEqual('configs'); + }); + + it('handle UI_CHANGE_LANGUAGE action', () => { + const currentState = { + ui: { language: 'en' } + }; + const newState = RootReducer(currentState, { + type: ACTION_TYPES.UI_CHANGE_LANGUAGE, + payload: 'fr' + }) + expect(newState.ui.language).toEqual('fr'); + }); + + it('handle SETTINGS_UPDATE_PROFILE action', () => { + const currentState = { + profile: 'Profile' + }; + const newState = RootReducer(currentState, { + type: ACTION_TYPES.SETTINGS_UPDATE_PROFILE, + payload: 'New Profile' + }) + expect(newState.profile).toEqual('New Profile'); + }); + + it('handle SETTINGS_UPDATE_CONFIGS action', () => { + const currentState = { + configs: { template: 'minimal' } + }; + const newState = RootReducer(currentState, { + type: ACTION_TYPES.SETTINGS_UPDATE_CONFIGS, + payload: { + name: 'template', + value: 'business', + } + }) + expect(newState.configs.template).toEqual('business'); + }); + + it('handle SETTINGS_RELOAD_CONFIGS action', () => { + const currentState = { + profile: 'Profile', + configs: { + language: 'fr', + template: 'minimal', + dateFormat: 'DDMMYY', + } + }; + const newState = RootReducer(currentState, { + type: ACTION_TYPES.SETTINGS_RELOAD_CONFIGS, + payload: { + profile: 'New Profile', + general: { language: 'en' }, + invoice: { + dateFormat: 'MMMM/DDDD/YYYY', + template: 'business', + } + } + }) + expect(newState.profile).toEqual('New Profile'); + expect(newState.configs.language).toEqual('en'); + expect(newState.configs.template).toEqual('business'); + expect(newState.configs.dateFormat).toEqual('MMMM/DDDD/YYYY'); + }); +}); + +describe('Selectors should', () => { + let state; + beforeEach(() => { + state = { + ui: { language: 'en' }, + configs: { template: 'Business' }, + invoice: { language: 'fr' }, + profile: 'Profile', + } + }); + it('getConfigs should return invoice current configs', () => { + expect(getConfigs(state)).toEqual(state.configs); + }); + it('getInvoice should return invoice data', () => { + expect(getInvoice(state)).toEqual(state.invoice); + }); + it('getProfile should return profile data', () => { + expect(getProfile(state)).toEqual('Profile'); + }); + it('getUILang should return profile data', () => { + expect(getUILang(state)).toEqual('en'); + }); + it('getInvoiceLang should return language of the invoice', () => { + expect(getInvoiceLang(state)).toEqual('fr'); + }); +}); diff --git a/preview/reducers/index.jsx b/preview/reducers/index.jsx index a571b1c9..36fbbd6f 100644 --- a/preview/reducers/index.jsx +++ b/preview/reducers/index.jsx @@ -42,6 +42,10 @@ const RootReducer = handleActions( language: action.payload }) }), + [ACTION_TYPES.SETTINGS_UPDATE_PROFILE]: (state, action) => + Object.assign({}, state, { + profile: action.payload + }), [ACTION_TYPES.SETTINGS_UPDATE_CONFIGS]: (state, action) => Object.assign({}, state, { configs: Object.assign({}, state.configs, {