Skip to content

Commit

Permalink
Reflect changes on invoice immediately when profile's changed. (#245)
Browse files Browse the repository at this point in the history
* Notify previewWindow about profile chages
* Added profile changes handler in previewWindow
* Added tests to SettingsMW
* Added ActionCreator & Reducer tests to previewWindow
  • Loading branch information
hql287 committed Mar 4, 2018
1 parent d11accd commit 8358d86
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 28 deletions.
2 changes: 1 addition & 1 deletion app/actions/__tests__/invoices.spec.js
Original file line number Diff line number Diff line change
@@ -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,
});
Expand Down
10 changes: 8 additions & 2 deletions app/middlewares/SettingsMW.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
100 changes: 75 additions & 25 deletions app/middlewares/__tests__/SettingsMW.spec.js
Original file line number Diff line number Diff line change
@@ -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');

Expand Down Expand Up @@ -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', () => {
Expand Down
5 changes: 5 additions & 0 deletions main/preview-window.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions preview/Viewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions preview/actions/__test__/index.spec.js
Original file line number Diff line number Diff line change
@@ -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,
});
});
5 changes: 5 additions & 0 deletions preview/actions/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions preview/constants/actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

120 changes: 120 additions & 0 deletions preview/reducers/__test__/index.spec.js
Original file line number Diff line number Diff line change
@@ -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');
});
});
4 changes: 4 additions & 0 deletions preview/reducers/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down

0 comments on commit 8358d86

Please sign in to comment.