From 633213545900f323ec18b982345128ed28ddd1f8 Mon Sep 17 00:00:00 2001 From: Clare Macrae Date: Mon, 20 Mar 2023 17:10:44 +0000 Subject: [PATCH] fix: Prevent exception if toggling in unsaved file in Reading mode (#1772) * fix: Prevent exception if toggling in unsaved file in Reading mode See the comment https://github.com/obsidian-tasks-group/obsidian-tasks/issues/1680#issuecomment-1457826313 in #1680. If lines had been deleted from a file, and it was not yet saved when user then toggled a line near the end of the file in Reading mode, there could be an access of a non-existent line number. The resultant output was: plugin:obsidian-tasks-plugin:17605 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'includes') at eval (plugin:obsidian-tasks-plugin:17605:14) at Generator.next () at fulfilled (plugin:obsidian-tasks-plugin:173:24) * comment: Fix typo --- src/File.ts | 15 ++- ...f undefined correct behaviour.approved.txt | 1 + ...d current incorrect behaviour.approved.txt | 1 + tests/File.test.ts | 30 +++++- ...680_task_line_number_past_end_of_file.json | 91 +++++++++++++++++++ 5 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined correct behaviour.approved.txt create mode 100644 tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined current incorrect behaviour.approved.txt create mode 100644 tests/__test_data__/MockDataForTogglingTasks/1680_task_line_number_past_end_of_file.json diff --git a/src/File.ts b/src/File.ts index 18a3b0a3a5..2e45074979 100644 --- a/src/File.ts +++ b/src/File.ts @@ -192,11 +192,20 @@ export function findLineNumberOfTaskToToggle( listItemsCache: ListItemCache[] | MockListItemCache[], errorLoggingFunction: ErrorLoggingFunction, ) { + const fileLinesCount = fileLines.length; const { globalFilter } = getSettings(); let taskLineNumber: number | undefined; let sectionIndex = 0; for (const listItemCache of listItemsCache) { - if (listItemCache.position.start.line < originalTask.taskLocation.sectionStart) { + const listItemLineNumber = listItemCache.position.start.line; + if (listItemLineNumber >= fileLinesCount) { + // One or more lines has been deleted since the cache was populated, + // so there is at least one list item in the cache that is beyond + // the end of the actual file on disk. + return undefined; + } + + if (listItemLineNumber < originalTask.taskLocation.sectionStart) { continue; } @@ -204,11 +213,11 @@ export function findLineNumberOfTaskToToggle( continue; } - const line = fileLines[listItemCache.position.start.line]; + const line = fileLines[listItemLineNumber]; if (line.includes(globalFilter)) { if (sectionIndex === originalTask.taskLocation.sectionIndex) { if (line === originalTask.originalMarkdown) { - taskLineNumber = listItemCache.position.start.line; + taskLineNumber = listItemLineNumber; } else { errorLoggingFunction( `Tasks: Unable to find task in file ${originalTask.taskLocation.path}. diff --git a/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined correct behaviour.approved.txt b/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined correct behaviour.approved.txt new file mode 100644 index 0000000000..35f6fc0805 --- /dev/null +++ b/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined correct behaviour.approved.txt @@ -0,0 +1 @@ +Could not find line for task diff --git a/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined current incorrect behaviour.approved.txt b/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined current incorrect behaviour.approved.txt new file mode 100644 index 0000000000..35f6fc0805 --- /dev/null +++ b/tests/File.test.replaceTaskWithTasks_issue 1680 - Cannot read properties of undefined current incorrect behaviour.approved.txt @@ -0,0 +1 @@ +Could not find line for task diff --git a/tests/File.test.ts b/tests/File.test.ts index e7ebdd30e8..25f2ddea6e 100644 --- a/tests/File.test.ts +++ b/tests/File.test.ts @@ -55,7 +55,15 @@ function testFindLineNumberOfTaskToToggle( ); // Assert - verify(errorString ? errorString : 'Success. Line found OK. No error reported.'); + let descriptionOfOutcome = ''; + if (errorString !== undefined) { + descriptionOfOutcome = errorString; + } else if (result === undefined) { + descriptionOfOutcome = 'Could not find line for task'; + } else { + descriptionOfOutcome = 'Success. Line found OK. No error reported.'; + } + verify(descriptionOfOutcome); if (expectedLineNumber !== undefined) { expect(result).not.toBeUndefined(); @@ -97,5 +105,25 @@ describe('replaceTaskWithTasks', () => { }); }); + // -------------------------------------------------------------------------------- + // Issue 1680 + describe('issue 1680 - Cannot read properties of undefined', () => { + const jsonFileName = '1680_task_line_number_past_end_of_file.json'; + const taskLineToToggle = '- [ ] #task Section 2/Task 2'; + + it.failing('correct behaviour', () => { + // An incorrect line is currently found, so this test fails, due to bug 1680 + const expectedLineNumber = 9; + testFindLineNumberOfTaskToToggle(jsonFileName, taskLineToToggle, expectedLineNumber); + }); + + it('current incorrect behaviour', () => { + // An incorrect line is currently found, due to bug 688. + // It is recognised as an incorrect line, and so line number is returned as undefined. + const expectedLineNumber = undefined; + testFindLineNumberOfTaskToToggle(jsonFileName, taskLineToToggle, expectedLineNumber); + }); + }); + // -------------------------------------------------------------------------------- }); diff --git a/tests/__test_data__/MockDataForTogglingTasks/1680_task_line_number_past_end_of_file.json b/tests/__test_data__/MockDataForTogglingTasks/1680_task_line_number_past_end_of_file.json new file mode 100644 index 0000000000..08e0920817 --- /dev/null +++ b/tests/__test_data__/MockDataForTogglingTasks/1680_task_line_number_past_end_of_file.json @@ -0,0 +1,91 @@ +{ + "taskData": { + "originalMarkdown": "- [ ] #task Section 2/Task 2", + "taskLocation": { + "path": "Manual Testing/Task Toggling Scenarios/1680 - Reading Mode line numbers not updated on editing.md", + "lineNumber": 14, + "sectionStart": 13, + "sectionIndex": 1, + "precedingHeader": null + } + }, + "fileData": { + "fileLines": [ + "## Test Data", + "", + "### Tasks Section 1", + "- [ ] #task Section 1/Task 1", + "- [ ] #task Section 1/Task 2", + "", + "### Tasks Section 2", + "", + "- [ ] #task Section 2/Task 1", + "- [ ] #task Section 2/Task 2", + "" + ] + }, + "cacheData": { + "listItemsCache": [ + { + "position": { + "start": { + "line": 8, + "col": 0, + "offset": 39 + }, + "end": { + "line": 8, + "col": 28, + "offset": 67 + } + }, + "task": " " + }, + { + "position": { + "start": { + "line": 9, + "col": 0, + "offset": 68 + }, + "end": { + "line": 9, + "col": 28, + "offset": 96 + } + }, + "task": " " + }, + { + "position": { + "start": { + "line": 13, + "col": 0, + "offset": 119 + }, + "end": { + "line": 13, + "col": 28, + "offset": 147 + } + }, + "task": " " + }, + { + "position": { + "start": { + "line": 14, + "col": 0, + "offset": 148 + }, + "end": { + "line": 14, + "col": 28, + "offset": 176 + } + }, + "task": " " + } + ] + } +} \ No newline at end of file