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
};