Skip to content

Commit

Permalink
Added notification threshold setting, badge count and missing test ca…
Browse files Browse the repository at this point in the history
…ses, ongoing refactoring
  • Loading branch information
ransome1 committed Oct 21, 2023
1 parent b30c8fd commit 8848b0b
Show file tree
Hide file tree
Showing 41 changed files with 389 additions and 151 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@
"webpack-merge": "^5.9.0"
},
"build": {
"appId": "com.todotxt.sleek",
"appId": "com.github.ransome1.sleek",
"buildVersion": "25",
"asar": true,
"asarUnpack": "**\\*.{node,dll}",
Expand Down
2 changes: 1 addition & 1 deletion release/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sleek",
"version": "2.0.0-dev18",
"version": "2.0.0-dev19",
"description": "todo.txt manager for Linux, Windows and MacOS, free and open-source (FOSS)",
"synopsis": "todo.txt manager for Linux, Windows and MacOS, free and open-source (FOSS)",
"keywords": [
Expand Down
16 changes: 8 additions & 8 deletions src/__tests__/__mock__/recurrence.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

2023-10-20 Line 1 rec:1d due:2023-10-21
2023-10-20 Line 1 rec:w due:2023-10-27
2023-10-20 Line 1 rec:2m due:2023-12-20
2023-10-20 Line 1 rec:+1d due:2023-10-22
2023-10-20 Line 1 rec:7w due:2023-12-08
2023-10-21 Line 1 rec:1d due:2023-10-22
2023-10-21 Line 1 rec:w due:2023-10-28
2023-10-21 Line 1 rec:2m due:2023-12-21
2023-10-21 Line 1 rec:+1d due:2023-10-23
2023-10-21 Line 1 rec:7w due:2023-12-09
2023-07-21 Line 1 due:2023-07-24 rec:+1b
2021-01-01 taxes are due in one year t:2022-03-30 due:2022-04-30 rec:+1y
2023-10-20 Water plants @home +quick due:2023-10-27 t:2023-10-17 rec:1w
2023-10-20 Line 1 rec:+1d t:2023-09-20 due:2023-10-21
(A) 2023-10-20 Line 1 rec:1d pri:A due:2023-10-21
2023-10-21 Water plants @home +quick due:2023-10-28 t:2023-10-18 rec:1w
2023-10-21 Line 1 rec:+1d t:2023-09-20 due:2023-10-22
(A) 2023-10-21 Line 1 rec:1d pri:A due:2023-10-22
12 changes: 9 additions & 3 deletions src/__tests__/__mock__/test.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
Line 1
Edited line
New line
2023-10-20 New line with creation date
New line with relative threshold date t:June 3rd, 2005
Multi line 1Multi line 2Multi line 3
Updated line
Append 1
Append 2
New line with relative threshold date t:June 3rd, 2005
Line4
Line5
Line6
Multi line 1Multi line 2Multi line 3
15 changes: 6 additions & 9 deletions src/__tests__/main/Archive.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import archiveTodos from '../../main/modules/File/Archive';

import fs from 'fs/promises';

jest.mock('electron', () => ({
app: {
setBadgeCount: jest.fn(),
},
}));

jest.mock('../../main/main', () => ({
mainWindow: {
webContents: {
Expand Down Expand Up @@ -44,12 +49,4 @@ describe('Archiving', () => {
await new Promise((resolve) => setTimeout(resolve, 1000));
expect(fileContent).toEqual(expectedContent);
});

// test('Should fail if no active file is available', async () => {
// await archiveTodos();
// const fileContent = await fs.readFile('./src/__tests__/__mock__/done.txt', 'utf8');
// const expectedContent = `x 2022-02-02 todo from done.txt 1\nx 2022-02-03 todo from done.txt 2\nx 2022-02-04 todo from done.txt 3\nx 2022-02-05 todo from done.txt 4\nx 2022-02-01 Finished todo 3\nx 2022-02-08 Finished todo 1\nx 2022-02-17 Finished todo 2`;
// await new Promise((resolve) => setTimeout(resolve, 1000));
// expect(fileContent).toEqual(expectedContent);
// });
});
6 changes: 6 additions & 0 deletions src/__tests__/main/CreateTodoObjects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import { configStorage } from '../../main/config';

const dateTodayString: string = dayjs(new Date()).format('YYYY-MM-DD');

jest.mock('electron', () => ({
app: {
setBadgeCount: jest.fn(),
},
}));

jest.mock('../../main/config', () => ({
configStorage: {
get: jest.fn()
Expand Down
80 changes: 80 additions & 0 deletions src/__tests__/main/HandleNotification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { handleNotification, sendNotification } from '../../main/modules/HandleNotification';
import { configStorage } from '../../main/config';
import { badge } from '../../main/modules/TodoObject/CreateTodoObjects';
import { Notification } from 'electron';
import dayjs from 'dayjs';

const dateToday = dayjs();
const dateTodayString = dateToday.format('YYYY-MM-DD');
const dateTomorrowString = dateToday.add(1, 'day').format('YYYY-MM-DD');
const dateInSevenDaysString = dateToday.add(7, 'day').format('YYYY-MM-DD');
const dateInTwentyDaysString = dateToday.add(20, 'day').format('YYYY-MM-DD');

jest.mock('../../main/modules/TodoObject/CreateTodoObjects', () => ({
badge: {
count: 0,
},
}));

jest.mock('electron', () => ({
Notification: jest.fn().mockImplementation(() => ({
show: jest.fn(),
})),
}));

jest.mock('../../main/config', () => ({
configStorage: {
get: jest.fn((key) => {
if (key === 'notificationsAllowed') {
return true;
} else if (key === 'notificationThreshold') {
return 10;
}
}),
set: jest.fn(),
},
}));

describe('Notifications', () => {
beforeEach(() => {
jest.clearAllMocks();
});

test('should push notification for a todo due today', () => {
handleNotification(1, dateTodayString, 'Sample todo due:today', badge);
expect(Notification).toHaveBeenCalledWith({ title: 'Due today', body: 'Sample todo due:today', silent: false });
expect(badge.count).toBe(1);
});

test('should push notification for a todo due tomorrow', () => {
handleNotification(2, dateTomorrowString, 'Sample todo due:tomorrow', badge);
expect(Notification).toHaveBeenCalledWith({ title: 'Due tomorrow', body: 'Sample todo due:tomorrow', silent: false });
expect(badge.count).toBe(2);
});

test('should push notification for a todo due in 7 days', () => {
handleNotification(3, dateInSevenDaysString, 'Sample todo due:in 7 days', badge);
expect(Notification).toHaveBeenCalledWith({ title: 'Due in 7 days', body: 'Sample todo due:in 7 days', silent: false });
expect(badge.count).toBe(3);
});

test('should NOT push notification for a todo due in 20 days', () => {
handleNotification(4, dateInTwentyDaysString, 'Sample todo due:in 20 days', badge);
expect(Notification).not.toHaveBeenCalled();
expect(badge.count).toBe(3);
});

test('should NOT handle notification when not allowed', () => {
const configStorageGetMock = jest.fn((key) => {
if (key === 'notificationsAllowed') {
return false;
}
});
configStorage.get = configStorageGetMock;
handleNotification(5, '2023-10-20', 'Sample todo', badge);
expect(configStorageGetMock).toHaveBeenCalledWith('notificationsAllowed');
expect(Notification).not.toHaveBeenCalled();
expect(badge.count).toBe(3);
});

});
109 changes: 92 additions & 17 deletions src/__tests__/main/Write.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fs from 'fs';
import { writeTodoObjectToFile } from '../../main/modules/File/Write';
import fs from 'fs/promises';
import { writeTodoObjectToFile, removeLineFromFile } from '../../main/modules/File/Write';
import { lines } from '../../main/modules/TodoObject/CreateTodoObjects';
import { configStorage } from '../../main/config';
import dayjs from 'dayjs';
Expand Down Expand Up @@ -34,19 +34,18 @@ jest.mock('../../main/modules/TodoObject/CreateTodoObjects', () => ({
}));

describe('Writing to file', () => {

beforeEach(async () => {
jest.clearAllMocks();
});

test('should fail if no string is provided', async () => {
await expect(writeTodoObjectToFile(-1, '', false)).rejects.toThrow(
"No string provided, won't write empty todo to file"
);
await expect(writeTodoObjectToFile(-1, '')).rejects.toThrow("No string provided, won't write empty todo to file");
});

test('should write a new line when id is not provided', async () => {
await writeTodoObjectToFile(-1, 'New line', false);
const fileContent = fs.readFileSync('./src/__tests__/__mock__/test.txt', 'utf8');
await writeTodoObjectToFile(-1, 'New line');
const fileContent = await fs.readFile('./src/__tests__/__mock__/test.txt', 'utf8');
expect(fileContent).toEqual('Line 1\nLine 2\nLine 3\nNew line');
});

Expand All @@ -58,15 +57,15 @@ describe('Writing to file', () => {
}
return originalGet.call(configStorage, key);
};
await writeTodoObjectToFile(-1, 'New line with creation date', false);
const fileContent = fs.readFileSync('./src/__tests__/__mock__/test.txt', 'utf8');
await writeTodoObjectToFile(-1, 'New line with creation date');
const fileContent = await fs.readFile('./src/__tests__/__mock__/test.txt', 'utf8');
expect(fileContent).toEqual(`Line 1\nLine 2\nLine 3\nNew line\n${date} New line with creation date`);
configStorage.get = originalGet;
});

test('should write a new line when id is not provided and convert a relative (speaking) date to an absolute date', async () => {
await writeTodoObjectToFile(-1, 'New line with relative threshold date t:June 3rd, 2005', false);
const fileContent = fs.readFileSync('./src/__tests__/__mock__/test.txt', 'utf8');
await writeTodoObjectToFile(-1, 'New line with relative threshold date t:June 3rd, 2005');
const fileContent = await fs.readFile('./src/__tests__/__mock__/test.txt', 'utf8');
expect(fileContent).toEqual(`Line 1\nLine 2\nLine 3\nNew line\n${date} New line with creation date\nNew line with relative threshold date t:2005-06-03`);
});

Expand All @@ -78,21 +77,97 @@ describe('Writing to file', () => {
}
return originalGet.call(configStorage, key);
};
await writeTodoObjectToFile(5, 'New line with relative threshold date t:June 3rd, 2005', false);
const fileContent = fs.readFileSync('./src/__tests__/__mock__/test.txt', 'utf8');
await writeTodoObjectToFile(5, 'New line with relative threshold date t:June 3rd, 2005');
const fileContent = await fs.readFile('./src/__tests__/__mock__/test.txt', 'utf8');
expect(fileContent).toEqual(`Line 1\nLine 2\nLine 3\nNew line\n${date} New line with creation date\nNew line with relative threshold date t:June 3rd, 2005`);
configStorage.get = originalGet;
});

test('should overwrite a line when id is provided', async () => {
await writeTodoObjectToFile(1, 'Edited line', false);
const fileContent = fs.readFileSync('./src/__tests__/__mock__/test.txt', 'utf8');
await writeTodoObjectToFile(1, 'Edited line');
const fileContent = await fs.readFile('./src/__tests__/__mock__/test.txt', 'utf8');
expect(fileContent).toEqual(`Line 1\nEdited line\nLine 3\nNew line\n${date} New line with creation date\nNew line with relative threshold date t:June 3rd, 2005`);
});

test('should delete a line when remove is true', async () => {
await writeTodoObjectToFile(2, '', true);
const fileContent = fs.readFileSync('./src/__tests__/__mock__/test.txt', 'utf8');
await removeLineFromFile(2);
const fileContent = await fs.readFile('./src/__tests__/__mock__/test.txt', 'utf8');
expect(fileContent).toEqual(`Line 1\nEdited line\nNew line\n${date} New line with creation date\nNew line with relative threshold date t:June 3rd, 2005`);
});

test('should append 3 new lines at the end of the file', async () => {
const originalGet = configStorage.get;
configStorage.get = (key: string) => {
if (key === 'multilineTextField') {
return true;
} else if (key === 'useMultilineForBulkTodoCreation') {
return true;
}
return originalGet.call(configStorage, key);
};

const content = 'Line4\nLine5\nLine6';

await writeTodoObjectToFile(-1, content);
const fileContent = await fs.readFile('./src/__tests__/__mock__/test.txt', 'utf8');
expect(fileContent).toEqual(`Line 1\nEdited line\nNew line\n${date} New line with creation date\nNew line with relative threshold date t:June 3rd, 2005\nLine4\nLine5\nLine6`);
configStorage.get = originalGet;
});

test('should update a specific line and append 2 lines to the updated line', async () => {
const originalGet = configStorage.get;
configStorage.get = (key: string) => {
if (key === 'multilineTextField') {
return true;
} else if (key === 'useMultilineForBulkTodoCreation') {
return true;
}
return originalGet.call(configStorage, key);
};

const content = 'Updated line\nAppend 1\nAppend 2';

await writeTodoObjectToFile(3, content);
const fileContent = await fs.readFile('./src/__tests__/__mock__/test.txt', 'utf8');
expect(fileContent).toEqual(`Line 1\nEdited line\nNew line\nUpdated line\nAppend 1\nAppend 2\nNew line with relative threshold date t:June 3rd, 2005\nLine4\nLine5\nLine6`);
configStorage.get = originalGet;
});

test('should append a multi line todo', async () => {
const originalGet = configStorage.get;
configStorage.get = (key: string) => {
if (key === 'multilineTextField') {
return true;
} else if (key === 'useMultilineForBulkTodoCreation') {
return false;
}
return originalGet.call(configStorage, key);
};

const content = 'Multi line 1\nMulti line 2\nMulti line 3';

await writeTodoObjectToFile(-1, content);
const fileContent = await fs.readFile('./src/__tests__/__mock__/test.txt', 'utf8');
expect(fileContent).toEqual(`Line 1\nEdited line\nNew line\nUpdated line\nAppend 1\nAppend 2\nNew line with relative threshold date t:June 3rd, 2005\nLine4\nLine5\nLine6\nMulti line 1Multi line 2Multi line 3`);
configStorage.get = originalGet;
});

test('should update line with a multi line todo', async () => {
const originalGet = configStorage.get;
configStorage.get = (key: string) => {
if (key === 'multilineTextField') {
return true;
} else if (key === 'useMultilineForBulkTodoCreation') {
return false;
}
return originalGet.call(configStorage, key);
};

const content = 'Multi line 1\nMulti line 2\nMulti line 3';

await writeTodoObjectToFile(2, content);
const fileContent = await fs.readFile('./src/__tests__/__mock__/test.txt', 'utf8');
expect(fileContent).toEqual(`Line 1\nEdited line\nMulti line 1Multi line 2Multi line 3\nUpdated line\nAppend 1\nAppend 2\nNew line with relative threshold date t:June 3rd, 2005\nLine4\nLine5\nLine6\nMulti line 1Multi line 2Multi line 3`);
configStorage.get = originalGet;
});
});
3 changes: 3 additions & 0 deletions src/locales/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"settings.light": "Světlé",
"settings.dark": "Tmavé",
"settings.language": "Jazyk",
"settings.notificationThreshold": "Hranice oznámení",
"settings.useMultilineForBulkTodoCreation": "Každý řádek v textovém poli vytvoří samostatnou položku úkolu",
"drawer.tabs.attributes": "Atributy",
"drawer.tabs.filters": "Filtry",
"drawer.tabs.sorting": "Řazení",
Expand All @@ -44,6 +46,7 @@
"splashscreen.noTodosVisible.text": "Žádné výsledky nejsou viditelné.",
"splashscreen.noTodosVisible.reset": "Obnovit filtry a hledání",
"search.visibleTodos": "Viditelné úkoly: ",
"search.addAsTodo": "Přidat jako úkol",
"todoDialog.footer.add": "Přidat",
"todoDialog.footer.update": "Aktualizovat",
"todoDialog.footer.cancel": "Zrušit",
Expand Down
5 changes: 4 additions & 1 deletion src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"settings.light": "Hell",
"settings.dark": "Dunkel",
"settings.language": "Sprache",
"settings.notificationThreshold": "Benachrichtigungsschwelle",
"settings.useMultilineForBulkTodoCreation": "Jede Zeile im Textfeld erstellt eine separate Todo",
"drawer.tabs.attributes": "Attribute",
"drawer.tabs.filters": "Filter",
"drawer.tabs.sorting": "Sortierung",
Expand All @@ -44,6 +46,7 @@
"splashscreen.noTodosVisible.text": "Keine sichtbaren Ergebnisse.",
"splashscreen.noTodosVisible.reset": "Filter und Suche zurücksetzen",
"search.visibleTodos": "Sichtbare Aufgaben: ",
"search.addAsTodo": "Als Aufgabe hinzufügen",
"todoDialog.footer.add": "Hinzufügen",
"todoDialog.footer.update": "Aktualisieren",
"todoDialog.footer.cancel": "Abbrechen",
Expand All @@ -64,4 +67,4 @@
"prompt.archive.headline": "Abgeschlossene Aufgaben archivieren?",
"prompt.archive.text": "Dadurch werden alle abgeschlossenen Aufgaben in Ihre angegebene 'erledigt'-Datei verschoben",
"prompt.archive.button": "Archivieren"
}
}
4 changes: 3 additions & 1 deletion src/locales/en-gb.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"settings.light": "Light",
"settings.dark": "Dark",
"settings.language": "Language",
"settings.notificationThresholdDueDates": "Notification threshold for due dates",
"settings.notificationThreshold": "Notification threshold",
"settings.useMultilineForBulkTodoCreation": "Each row in the text field creates a separate to-do item",
"drawer.tabs.attributes": "Attributes",
"drawer.tabs.filters": "Filters",
"drawer.tabs.sorting": "Sorting",
Expand All @@ -45,6 +46,7 @@
"splashscreen.noTodosVisible.text": "No results visible.",
"splashscreen.noTodosVisible.reset": "Reset filters and search",
"search.visibleTodos": "Visible todos: ",
"search.addAsTodo": "Add as todo",
"todoDialog.footer.add": "Add",
"todoDialog.footer.update": "Update",
"todoDialog.footer.cancel": "Cancel",
Expand Down
Loading

0 comments on commit 8848b0b

Please sign in to comment.