diff --git a/__tests__/__main__/main-window.js b/__tests__/__main__/main-window.js index 4cb99e464..8ccb77b20 100644 --- a/__tests__/__main__/main-window.js +++ b/__tests__/__main__/main-window.js @@ -255,7 +255,6 @@ describe('main-window.js', () => test('Should minimize if minimize-to-tray is false', (done) => { - const userPreferencesSpy = jest.spyOn(userPreferences, 'getUserPreferences'); savePreferences({ ...defaultPreferences, ['minimize-to-tray']: false @@ -266,10 +265,11 @@ describe('main-window.js', () => * @type {BrowserWindow} */ const mainWindow = getMainWindow(); + const minimizeSpy = jest.spyOn(mainWindow, 'minimize'); mainWindow.on('ready-to-show', () => { mainWindow.emit('minimize', {}); - expect(userPreferencesSpy).toHaveBeenCalledTimes(1); + expect(minimizeSpy).toBeCalled(); done(); }); }); diff --git a/__tests__/__main__/notification.js b/__tests__/__main__/notification.js index 5290562fa..dfa7227b7 100644 --- a/__tests__/__main__/notification.js +++ b/__tests__/__main__/notification.js @@ -15,7 +15,7 @@ describe('Notifications', function() { describe('notify', () => { - beforeAll(() => + beforeEach(() => { // displays a notification in test fails if mocks are not restored jest.restoreAllMocks(); diff --git a/__tests__/__main__/time-balance.js b/__tests__/__main__/time-balance.js index 3fe8b3ff8..57d3a797f 100644 --- a/__tests__/__main__/time-balance.js +++ b/__tests__/__main__/time-balance.js @@ -4,7 +4,8 @@ const Store = require('electron-store'); import { computeAllTimeBalanceUntil, - getFirstInputInDb + getFirstInputInDb, + computeAllTimeBalanceUntilAsync } from '../../js/time-balance.js'; import { resetPreferences } from '../../js/user-preferences.js'; @@ -93,6 +94,26 @@ describe('Time Balance', () => await expect(computeAllTimeBalanceUntil(new Date(2020, 6, 7))).resolves.toBe('-24:00'); }); + test('computeAllTimeBalanceUntil: only regular days, timesAreProgressing false', async() => + { + const entryEx = { + '2020-6-1': {'values': ['08:00', '12:00', '17:00', '13:00']} // wed (8h total) + }; + flexibleStore.set(entryEx); + // time balance until thu (excluding thu) + await expect(computeAllTimeBalanceUntil(new Date(2020, 6, 2))).resolves.toBe('-08:00'); + // time balance until fri (excluding fri) + await expect(computeAllTimeBalanceUntil(new Date(2020, 6, 3))).resolves.toBe('-16:00'); + // time balance until sat (excluding sat) + await expect(computeAllTimeBalanceUntil(new Date(2020, 6, 4))).resolves.toBe('-24:00'); + // time balance until sun (excluding sun) + await expect(computeAllTimeBalanceUntil(new Date(2020, 6, 5))).resolves.toBe('-24:00'); + // time balance until mon (excluding mon) + await expect(computeAllTimeBalanceUntil(new Date(2020, 6, 6))).resolves.toBe('-24:00'); + // time balance until tue (excluding tue) + await expect(computeAllTimeBalanceUntil(new Date(2020, 6, 7))).resolves.toBe('-32:00'); + }); + test('computeAllTimeBalanceUntil: only regular days (6 entries)', async() => { const entryEx = { @@ -318,4 +339,19 @@ describe('Time Balance', () => waivedWorkdays.set(waivedEntries); await expect(computeAllTimeBalanceUntil(new Date(2020, 5, 1))).resolves.toBe('00:00'); }); + + test('computeAllTimeBalanceUntilAsync: target date in the past of entries', async() => + { + const entryEx = { + '2020-6-1': {'values': ['08:00', '12:00', '13:00', '17:00']}, // wed (8h total) + '2020-6-3': {'values': ['08:00', '12:00', '13:00', '17:00']} // fri (8h total) + }; + flexibleStore.set(entryEx); + const waivedEntries = { + '2020-07-02': { reason: 'Waiver', hours: '02:00' }, // tue + }; + waivedWorkdays.set(waivedEntries); + await expect(computeAllTimeBalanceUntilAsync(new Date(2020, 5, 1))).resolves.toBe('00:00'); + }); + }); diff --git a/__tests__/__main__/time-math.js b/__tests__/__main__/time-math.js index c3a717303..89ad8a3b7 100644 --- a/__tests__/__main__/time-math.js +++ b/__tests__/__main__/time-math.js @@ -175,15 +175,45 @@ describe('Time Math Functions', () => test('validateDate(date)', () => { - expect(validateDate('0001-00-00')).toBeFalsy(); - expect(validateDate('1-00-00')).toBeFalsy(); - expect(validateDate('1996-13-00')).toBeFalsy(); - expect(validateDate('1996-1-00')).toBeFalsy(); - expect(validateDate('1996-01-1')).toBeFalsy(); - expect(validateDate('1996-01-40')).toBeFalsy(); - expect(validateDate('1996-01-31')).toBeFalsy(); - expect(validateDate('I\'m a date!')).toBeFalsy(); - expect(validateDate('1996-01-29')).toBeTruthy(); - expect(validateDate('1996-01-30')).toBeFalsy(); + const tests = [ + {date: '0001-00-00',valid: false}, + {date: '1-00-00',valid: false}, + {date: '1996-13-00',valid: false}, + {date: '1996-1-00',valid: false}, + {date: '1996-01-1',valid: false}, + {date: '1996-01-40',valid: false}, + {date: '1996-01-31',valid: false}, + {date: 'I\'m a date!',valid: false}, + {date: '1996-01-29',valid: true}, + {date: '1996-01-30',valid: false}, + {date: '1996-00-01', valid: true}, + {date: '1996-01-01', valid: true}, + {date: '1996-02-01', valid: true}, + {date: '1996-03-01', valid: true}, + {date: '1996-04-01', valid: true}, + {date: '1996-05-01', valid: true}, + {date: '1996-06-01', valid: true}, + {date: '1996-07-01', valid: true}, + {date: '1996-08-01', valid: true}, + {date: '1996-09-01', valid: true}, + {date: '1996-10-01', valid: true}, + {date: '1996-11-01', valid: true}, + {date: '1996-00-40', valid: false}, + {date: '1996-01-40', valid: false}, + {date: '1996-02-40', valid: false}, + {date: '1996-03-40', valid: false}, + {date: '1996-04-40', valid: false}, + {date: '1996-05-40', valid: false}, + {date: '1996-06-40', valid: false}, + {date: '1996-07-40', valid: false}, + {date: '1996-08-40', valid: false}, + {date: '1996-09-40', valid: false}, + {date: '1996-10-40', valid: false}, + {date: '1996-11-40', valid: false}, + ]; + for (const test of tests) + { + expect(validateDate(test.date)).toBe(test.valid); + } }); }); diff --git a/__tests__/__main__/update-manager.js b/__tests__/__main__/update-manager.js new file mode 100644 index 000000000..42c94c652 --- /dev/null +++ b/__tests__/__main__/update-manager.js @@ -0,0 +1,85 @@ +const ElectronStore = require('electron-store'); +const { getDateStr } = require('../../js/date-aux'); +const {shouldCheckForUpdates, checkForUpdates} = require('../../js/update-manager'); + +jest.mock('electron', () => +{ + const original = jest.requireActual('electron'); + return { + __esModule: true, + ...original, + net: { + ...original.net, + request: jest.fn() + } + }; +}); + +jest.mock('is-online', () => () => jest.fn().mockResolvedValueOnce(false).mockResolvedValue(true)); + +const { net } = require('electron'); + +describe('js/update-manager.js', () => +{ + const mocks = {}; + describe('shouldCheckForUpdates', () => + { + test('Should return true when was never checked', () => + { + const store = new ElectronStore(); + store.set('update-remind-me-after', false); + expect(shouldCheckForUpdates()).toBe(true); + }); + + test('Should return true when was checked before today', () => + { + const now = new Date(); + now.setDate(now.getDate() - 1); + const store = new ElectronStore(); + store.set('update-remind-me-after', getDateStr(now)); + expect(shouldCheckForUpdates()).toBe(true); + }); + + test('Should return false when was checked today', () => + { + const now = new Date(); + const store = new ElectronStore(); + store.set('update-remind-me-after', getDateStr(now)); + expect(shouldCheckForUpdates()).toBe(false); + }); + }); + + describe('checkForUpdates', () => + { + test('should not execute when is offline', () => + { + mocks.net = jest.spyOn(net, 'request').mockImplementation(() => {}); + checkForUpdates(); + expect(mocks.net).toBeCalledTimes(0); + }); + + test('should not execute when is online', (done) => + { + mocks.net = jest.spyOn(net, 'request').mockImplementation(() => + { + return { + on: () => + { + expect(mocks.net).toBeCalledTimes(1); + done(); + } + }; + }); + checkForUpdates(); + }); + + }); + + afterEach(() => + { + for (const mock of Object.values(mocks)) + { + mock.mockClear(); + } + }); +}); \ No newline at end of file diff --git a/__tests__/__main__/windows.js b/__tests__/__main__/windows.js new file mode 100644 index 000000000..6683a795b --- /dev/null +++ b/__tests__/__main__/windows.js @@ -0,0 +1,94 @@ +const { BrowserWindow } = require('electron'); +const { getDateStr } = require('../../js/date-aux.js'); +const windows = require('../../js/windows.js'); +const {getWaiverWindow, tray, contextMenu, prefWindow, resetWindowsElements, openWaiverManagerWindow, getDialogCoordinates} = require('../../js/windows.js'); + +describe('windows.js', () => +{ + let showSpy; + let loadSpy; + beforeEach(() => + { + // Avoid window being shown + loadSpy = jest.spyOn(BrowserWindow.prototype, 'loadURL').mockImplementation(() => {}); + showSpy = jest.spyOn(BrowserWindow.prototype, 'show').mockImplementation(() => {}); + jest.spyOn(windows, 'getDialogCoordinates').mockImplementation(() => ({x:0, y:0})); + }); + + test('Elements should be null on starting', () => + { + expect(getWaiverWindow()).toBe(null); + expect(tray).toBe(null); + expect(contextMenu).toBe(null); + expect(prefWindow).toBe(null); + }); + + test('Should create waiver window', (done) => + { + const mainWindow = new BrowserWindow({ + show: false + }); + openWaiverManagerWindow(mainWindow); + expect(getWaiverWindow()).not.toBe(null); + expect(getWaiverWindow()).toBeInstanceOf(BrowserWindow); + expect(getWaiverWindow().getSize()).toEqual([600, 500]); + done(); + }); + + test('Should show waiver window it has been created', (done) => + { + const mainWindow = new BrowserWindow({ + show: false + }); + openWaiverManagerWindow(mainWindow); + openWaiverManagerWindow(mainWindow); + expect(getWaiverWindow()).not.toBe(null); + // It should only load once the URL because it already exists + expect(showSpy).toHaveBeenCalledTimes(2); + expect(loadSpy).toHaveBeenCalledTimes(1); + done(); + }); + + test('Should set global waiverDay when event is sent', (done) => + { + const mainWindow = new BrowserWindow({ + show: false + }); + openWaiverManagerWindow(mainWindow, true); + expect(getWaiverWindow()).not.toBe(null); + expect(global.waiverDay).toBe(getDateStr(new Date())); + done(); + }); + + test('Should reset waiverWindow on close', () => + { + const mainWindow = new BrowserWindow({ + show: false + }); + openWaiverManagerWindow(mainWindow, true); + getWaiverWindow().emit('close'); + expect(getWaiverWindow()).toBe(null); + }); + + test('Should get dialog coordinates', () => + { + const coordinates = getDialogCoordinates(500, 250, { + getBounds: () => ({ + x: 200, + y: 300, + width: 400, + height: 600 + }) + }); + expect(coordinates).toEqual({ + x: 150, + y: 475 + }); + }); + + afterEach(() => + { + jest.restoreAllMocks(); + resetWindowsElements(); + }); +}); \ No newline at end of file diff --git a/__tests__/__renderer__/workday-waiver.js b/__tests__/__renderer__/workday-waiver.js index 95ad65781..e319d70e4 100644 --- a/__tests__/__renderer__/workday-waiver.js +++ b/__tests__/__renderer__/workday-waiver.js @@ -419,6 +419,15 @@ describe('Test Workday Waiver Window', function() expect(mockCallback).toBeCalledTimes(holidaysLength); }); + test('Do not load holidays table on empty holidays', () => + { + loadHolidaysTable(); + const holidaysLength = 0; + const rowLength = $('#holiday-list-table tbody tr').length; + expect($('#holiday-list-table').css('display')).toBe('table'); + expect(holidaysLength).toBe(rowLength); + }); + test('Load holidays table', async() => { $('#year').append($('').val(year).html(year)); @@ -473,6 +482,50 @@ describe('Test Workday Waiver Window', function() expect(thirdCell).toBe('undefined'); expect(fourthCell).toEqual(fourthCellContent); }); + + test('Holiday added not working day, no conflicts', () => + { + const day = 'test day'; + const reason = 'test reason'; + const workingDay = 'No'; + const conflicts = undefined; + addHolidayToList(day, reason, workingDay); + const table = $('#holiday-list-table tbody'); + const rowsLength = table.find('tr').length; + expect(rowsLength).toBe(1); + const firstCell = table.find('td')[0].innerHTML; + const secondCell = table.find('td')[1].innerHTML; + const thirdCell = table.find('td')[2].innerHTML; + const fourthCell = table.find('td')[4].innerHTML; + const fourthCellContent = ``; + expect(firstCell).toBe(day); + expect(secondCell).toBe(reason); + expect(thirdCell).toBe(workingDay); + expect(fourthCell).toEqual(fourthCellContent); + }); + + test('Holiday added not working day, with conflicts', () => + { + const day = 'test day'; + const reason = 'test reason'; + const workingDay = 'No'; + const conflicts = 'this is a conflict'; + addHolidayToList(day, reason, workingDay, conflicts); + const table = $('#holiday-list-table tbody'); + const rowsLength = table.find('tr').length; + expect(rowsLength).toBe(1); + const firstCell = table.find('td')[0].innerHTML; + const secondCell = table.find('td')[1].innerHTML; + const thirdCell = table.find('td')[2].innerHTML; + const conflictsCell = table.find('td')[3].innerHTML; + const fourthCell = table.find('td')[4].innerHTML; + const fourthCellContent = ``; + expect(firstCell).toBe(day); + expect(secondCell).toBe(reason); + expect(thirdCell).toBe(workingDay); + expect(conflictsCell).toBe(conflicts); + expect(fourthCell).toEqual(fourthCellContent); + }); }); describe('Clearing the table', () => diff --git a/js/main-window.js b/js/main-window.js index 9f4b8826d..ff8513a42 100644 --- a/js/main-window.js +++ b/js/main-window.js @@ -92,7 +92,7 @@ function createWindow() // and load the index.html of the app. mainWindow.loadFile(path.join(__dirname, '../index.html')); - ipcMain.on('TOGGLE_TRAY_PUNCH_TIME', function(_event, arg) + ipcMain.on('TOGGLE_TRAY_PUNCH_TIME', (_event, arg) => { const contextMenuTemplate = getContextMenuTemplate(mainWindow); contextMenuTemplate[0].enabled = arg; @@ -140,6 +140,10 @@ function createWindow() event.preventDefault(); mainWindow.hide(); } + else + { + mainWindow.minimize(); + } }); // Emitted when the window is closed. diff --git a/js/menus.js b/js/menus.js index 120ffe727..9876b15d2 100644 --- a/js/menus.js +++ b/js/menus.js @@ -230,16 +230,13 @@ function getEditMenuTemplate(mainWindow) } else if (importResult['failed'] !== 0) { - if (importResult['failed'] !== 0) - { - const message = `${importResult['failed']}/${importResult['total']} ${getCurrentTranslation('$Menu.could-not-be-loaded')}`; - dialog.showMessageBoxSync({ - icon: appConfig.iconpath, - type: 'warning', - title: getCurrentTranslation('$Menu.failed-entries'), - message: message - }); - } + const message = `${importResult['failed']}/${importResult['total']} ${getCurrentTranslation('$Menu.could-not-be-loaded')}`; + dialog.showMessageBoxSync({ + icon: appConfig.iconpath, + type: 'warning', + title: getCurrentTranslation('$Menu.failed-entries'), + message: message + }); } else { diff --git a/js/time-math.js b/js/time-math.js index 7ad58252c..eba36cc39 100644 --- a/js/time-math.js +++ b/js/time-math.js @@ -137,7 +137,7 @@ function isValidDayOfMonth(dayOfMonth, month) */ function validateDate(date) { - const re = new RegExp('(1|2)[0-9]{3}-(0[1-9]{1}|1[0-1]{1})-(0[0-9]{1}|1[0-9]{1}|2[0-9]{1}|3[0-1]{1})$'); + const re = new RegExp('(1|2)[0-9]{3}-(0[0-9]{1}|1[0-1]{1})-(0[0-9]{1}|1[0-9]{1}|2[0-9]{1}|3[0-1]{1})$'); if (re.test(date)) { const [, month, day] = date.split('-').map(parseFloat); diff --git a/js/windows.js b/js/windows.js index 1aa77b05b..b2ad12192 100644 --- a/js/windows.js +++ b/js/windows.js @@ -8,9 +8,9 @@ import { getDateStr } from './date-aux.js'; // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. let waiverWindow = null; -const prefWindow = null; -const tray = null; -const contextMenu = null; +let prefWindow = null; +let tray = null; +let contextMenu = null; function openWaiverManagerWindow(mainWindow, event) { @@ -72,11 +72,25 @@ function getDialogCoordinates(dialogWidth, dialogHeight, mainWindow) }; } +function resetWindowsElements() +{ + waiverWindow = null; + prefWindow = null; + tray = null; + contextMenu = null; +} + +function getWaiverWindow() +{ + return waiverWindow; +} + module.exports = { - waiverWindow, prefWindow, tray, contextMenu, openWaiverManagerWindow, - getDialogCoordinates + getDialogCoordinates, + getWaiverWindow, + resetWindowsElements };