Repository: obsidian-tasks-group/obsidian-tasks
Pull request #1676 (2023-02-20 21:55:46)

test: Enable CustomMatchersForFilters without imports
<!--- Provide a general summary of your changes in the Title above -->

# Description

This adds `setupFilesAfterEnv` to `jest.config.js`, in order to enable the automatic use of all `CustomMatchersForFilters.ts`.

It removes the need to add calling code like this to lots of test source files:

```ts
import { toBeValid, toHaveExplanation } from '../../CustomMatchers/CustomMatchersForFilters';

expect.extend({
    toBeValid,
    toHaveExplanation,
});
```

## Motivation and Context

I've been wanting to do this for ages, to avoid clutter and complexity in test files.

## How has this been tested?

By running existing tests.

## Screenshots (if appropriate)

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->


Internal changes:

- [x] **Tests** (prefix: `test` - additions and improvements to unit tests and the smoke tests)

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [ ] My change requires a change to the documentation.
- [ ] I have [updated the documentation](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CONTRIBUTING.md#updating-documentation) accordingly.
- [x] My change has adequate [Unit Test coverage](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CONTRIBUTING.md#maintaining-the-tests).

## Terms

<!--
By submitting this pull request, you must agree to follow our
[contributing guide](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CONTRIBUTING.md) and
[Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md).
Put an x in the boxes to confirm you agree.
-->

- [x] My contribution follow this project's [contributing guide](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CONTRIBUTING.md)
- [x] I agree to follow this project's [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md)



Patch:
diff --git a/jest.config.js b/jest.config.js
index ebdcbe24..46cc00e3 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -5,4 +5,8 @@ module.exports = {
         '^.+\\.ts$': 'ts-jest',
     },
     moduleFileExtensions: ['js', 'ts'],
+
+    // A list of paths to modules that run some code to configure or
+    // set up the testing framework before each test.
+    setupFilesAfterEnv: ['<rootDir>/tests/CustomMatchers/jest.custom_matchers.setup.ts'],
 };


Test patch:
diff --git a/tests/CustomMatchers/CustomMatchersForFilters.ts b/tests/CustomMatchers/CustomMatchersForFilters.ts
index 09a7968b..c4c6c849 100644
--- a/tests/CustomMatchers/CustomMatchersForFilters.ts
+++ b/tests/CustomMatchers/CustomMatchersForFilters.ts
@@ -32,11 +32,8 @@ import { Status } from '../../src/Status';
  @example
 
  // Setup:
- import { toMatchTaskFromLine } from '<relative-path>/CustomMatchersForFilters';
 
- expect.extend({
-    toMatchTaskFromLine,
-});
+ Imports are done automatically in tests/CustomMatchers/jest.custom_matchers.setup.ts
 
  // Inside it() and describe() blocks:
  it('works negating regexes', () => {
@@ -56,6 +53,13 @@ import { Status } from '../../src/Status';
 
  */
 
+/* MAINTENANCE NOTE:
+        Please add any newly added matchers to this file:
+            tests/CustomMatchers/jest.custom_matchers.setup.ts
+        so that tests can find them automatically, without needing
+        to write imports, and complex 'expect.extend(...)' lines.
+        Thank you.
+ */
 declare global {
     namespace jest {
         interface Matchers<R> {
diff --git a/tests/CustomMatchers/jest.custom_matchers.setup.ts b/tests/CustomMatchers/jest.custom_matchers.setup.ts
new file mode 100644
index 00000000..d6b0fd92
--- /dev/null
+++ b/tests/CustomMatchers/jest.custom_matchers.setup.ts
@@ -0,0 +1,22 @@
+// ---------------------------------------------------------------------
+// CustomMatchersForFilters
+// ---------------------------------------------------------------------
+import {
+    toBeValid,
+    toHaveExplanation,
+    toMatchTask,
+    toMatchTaskFromLine,
+    toMatchTaskWithHeading,
+    toMatchTaskWithPath,
+    toMatchTaskWithStatus,
+} from './CustomMatchersForFilters';
+
+expect.extend({
+    toBeValid,
+    toHaveExplanation,
+    toMatchTask,
+    toMatchTaskFromLine,
+    toMatchTaskWithHeading,
+    toMatchTaskWithPath,
+    toMatchTaskWithStatus,
+});
diff --git a/tests/Query/Filter/BooleanField.test.ts b/tests/Query/Filter/BooleanField.test.ts
index 72ec3f09..30ffee55 100644
--- a/tests/Query/Filter/BooleanField.test.ts
+++ b/tests/Query/Filter/BooleanField.test.ts
@@ -7,14 +7,8 @@ import type { FilterOrErrorMessage } from '../../../src/Query/Filter/Filter';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { testFilter } from '../../TestingTools/FilterTestHelpers';
 
-import { toHaveExplanation } from '../../CustomMatchers/CustomMatchersForFilters';
-
 window.moment = moment;
 
-expect.extend({
-    toHaveExplanation,
-});
-
 function testWithDescription(filter: FilterOrErrorMessage, description: string, expected: boolean) {
     const builder = new TaskBuilder();
     testFilter(filter, builder.description(description), expected);
diff --git a/tests/Query/Filter/DescriptionField.test.ts b/tests/Query/Filter/DescriptionField.test.ts
index 4d0d7533..ce5b2578 100644
--- a/tests/Query/Filter/DescriptionField.test.ts
+++ b/tests/Query/Filter/DescriptionField.test.ts
@@ -8,7 +8,6 @@ import { testTaskFilter } from '../../TestingTools/FilterTestHelpers';
 import { fromLine } from '../../TestHelpers';
 import type { FilterOrErrorMessage } from '../../../src/Query/Filter/Filter';
 import { BooleanField } from '../../../src/Query/Filter/BooleanField';
-import { toMatchTaskFromLine } from '../../CustomMatchers/CustomMatchersForFilters';
 import { Sort } from '../../../src/Query/Sort';
 import {
     expectTaskComparesAfter,
@@ -26,10 +25,6 @@ function testDescriptionFilter(filter: FilterOrErrorMessage, line: string, expec
     testTaskFilter(filter, task, expected);
 }
 
-expect.extend({
-    toMatchTaskFromLine,
-});
-
 describe('description should strip signifiers, some duplicate spaces and trailing spaces', () => {
     const field = new DescriptionField();
     it('without global filter - all tags included', () => {
diff --git a/tests/Query/Filter/DoneDateField.test.ts b/tests/Query/Filter/DoneDateField.test.ts
index 122833c5..b8cf9c72 100644
--- a/tests/Query/Filter/DoneDateField.test.ts
+++ b/tests/Query/Filter/DoneDateField.test.ts
@@ -6,15 +6,10 @@ import { DoneDateField } from '../../../src/Query/Filter/DoneDateField';
 import type { FilterOrErrorMessage } from '../../../src/Query/Filter/Filter';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { testFilter } from '../../TestingTools/FilterTestHelpers';
-import { toHaveExplanation } from '../../CustomMatchers/CustomMatchersForFilters';
 import { expectTaskComparesAfter, expectTaskComparesBefore } from '../../CustomMatchers/CustomMatchersForSorting';
 
 window.moment = moment;
 
-expect.extend({
-    toHaveExplanation,
-});
-
 function testTaskFilterForTaskWithDoneDate(filter: FilterOrErrorMessage, doneDate: string | null, expected: boolean) {
     const builder = new TaskBuilder();
     testFilter(filter, builder.doneDate(doneDate), expected);
diff --git a/tests/Query/Filter/DueDateField.test.ts b/tests/Query/Filter/DueDateField.test.ts
index 3104a08b..0e608b38 100644
--- a/tests/Query/Filter/DueDateField.test.ts
+++ b/tests/Query/Filter/DueDateField.test.ts
@@ -6,7 +6,6 @@ import { DueDateField } from '../../../src/Query/Filter/DueDateField';
 import type { FilterOrErrorMessage } from '../../../src/Query/Filter/Filter';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { testFilter } from '../../TestingTools/FilterTestHelpers';
-import { toHaveExplanation } from '../../CustomMatchers/CustomMatchersForFilters';
 import {
     expectTaskComparesAfter,
     expectTaskComparesBefore,
@@ -17,10 +16,6 @@ import { MarkdownTable } from '../../TestingTools/VerifyMarkdownTable';
 
 window.moment = moment;
 
-expect.extend({
-    toHaveExplanation,
-});
-
 function testTaskFilterForTaskWithDueDate(filter: FilterOrErrorMessage, dueDate: string | null, expected: boolean) {
     const builder = new TaskBuilder();
     testFilter(filter, builder.dueDate(dueDate), expected);
diff --git a/tests/Query/Filter/FilenameField.test.ts b/tests/Query/Filter/FilenameField.test.ts
index f077fe78..3558b403 100644
--- a/tests/Query/Filter/FilenameField.test.ts
+++ b/tests/Query/Filter/FilenameField.test.ts
@@ -1,16 +1,7 @@
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { FilenameField } from '../../../src/Query/Filter/FilenameField';
-import { toBeValid, toMatchTaskWithPath } from '../../CustomMatchers/CustomMatchersForFilters';
 import * as CustomMatchersForSorting from '../../CustomMatchers/CustomMatchersForSorting';
 
-expect.extend({
-    toMatchTaskWithPath,
-});
-
-expect.extend({
-    toBeValid,
-});
-
 describe('filename', () => {
     it('should provide access to the file name with extension', () => {
         const pathField = new FilenameField();
diff --git a/tests/Query/Filter/Filter.test.ts b/tests/Query/Filter/Filter.test.ts
index 1b5492e2..f0eb09d0 100644
--- a/tests/Query/Filter/Filter.test.ts
+++ b/tests/Query/Filter/Filter.test.ts
@@ -1,13 +1,8 @@
 import { Filter, FilterOrErrorMessage } from '../../../src/Query/Filter/Filter';
 import type { FilterFunction } from '../../../src/Query/Filter/Filter';
 import type { Task } from '../../../src/Task';
-import { toMatchTaskFromLine } from '../../CustomMatchers/CustomMatchersForFilters';
 import { Explanation } from '../../../src/Query/Explain/Explanation';
 
-expect.extend({
-    toMatchTaskFromLine,
-});
-
 describe('Filter', () => {
     it('should create a Filter object', () => {
         const filterFunction: FilterFunction = (task: Task) => {
diff --git a/tests/Query/Filter/HappensDateField.test.ts b/tests/Query/Filter/HappensDateField.test.ts
index 67073577..1022e5ab 100644
--- a/tests/Query/Filter/HappensDateField.test.ts
+++ b/tests/Query/Filter/HappensDateField.test.ts
@@ -5,15 +5,10 @@ import moment from 'moment';
 import { HappensDateField } from '../../../src/Query/Filter/HappensDateField';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { testFilter } from '../../TestingTools/FilterTestHelpers';
-import { toHaveExplanation } from '../../CustomMatchers/CustomMatchersForFilters';
 import * as CustomMatchersForSorting from '../../CustomMatchers/CustomMatchersForSorting';
 
 window.moment = moment;
 
-expect.extend({
-    toHaveExplanation,
-});
-
 describe('happens date', () => {
     it('by happens date presence', () => {
         // Arrange
diff --git a/tests/Query/Filter/HeadingField.test.ts b/tests/Query/Filter/HeadingField.test.ts
index 7866f204..67bcf496 100644
--- a/tests/Query/Filter/HeadingField.test.ts
+++ b/tests/Query/Filter/HeadingField.test.ts
@@ -2,7 +2,6 @@ import { HeadingField } from '../../../src/Query/Filter/HeadingField';
 import type { FilterOrErrorMessage } from '../../../src/Query/Filter/Filter';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { testFilter } from '../../TestingTools/FilterTestHelpers';
-import { toBeValid, toMatchTaskWithHeading } from '../../CustomMatchers/CustomMatchersForFilters';
 import * as CustomMatchersForSorting from '../../CustomMatchers/CustomMatchersForSorting';
 
 function testTaskFilterForHeading(filter: FilterOrErrorMessage, precedingHeader: string | null, expected: boolean) {
@@ -10,14 +9,6 @@ function testTaskFilterForHeading(filter: FilterOrErrorMessage, precedingHeader:
     testFilter(filter, builder.precedingHeader(precedingHeader), expected);
 }
 
-expect.extend({
-    toBeValid,
-});
-
-expect.extend({
-    toMatchTaskWithHeading,
-});
-
 describe('heading', () => {
     it('by heading (includes)', () => {
         // Arrange
diff --git a/tests/Query/Filter/PathField.test.ts b/tests/Query/Filter/PathField.test.ts
index c3fa9f12..fad2415d 100644
--- a/tests/Query/Filter/PathField.test.ts
+++ b/tests/Query/Filter/PathField.test.ts
@@ -2,7 +2,6 @@ import { PathField } from '../../../src/Query/Filter/PathField';
 import type { FilterOrErrorMessage } from '../../../src/Query/Filter/Filter';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { testFilter } from '../../TestingTools/FilterTestHelpers';
-import { toBeValid, toMatchTaskWithPath } from '../../CustomMatchers/CustomMatchersForFilters';
 import {
     expectTaskComparesAfter,
     expectTaskComparesBefore,
@@ -15,14 +14,6 @@ function testTaskFilterForTaskWithPath(filter: FilterOrErrorMessage, path: strin
     testFilter(filter, builder.path(path), expected);
 }
 
-expect.extend({
-    toMatchTaskWithPath,
-});
-
-expect.extend({
-    toBeValid,
-});
-
 describe('path', () => {
     it('by path (includes)', () => {
         // Arrange
diff --git a/tests/Query/Filter/PriorityField.test.ts b/tests/Query/Filter/PriorityField.test.ts
index 74a6372a..93113a93 100644
--- a/tests/Query/Filter/PriorityField.test.ts
+++ b/tests/Query/Filter/PriorityField.test.ts
@@ -3,19 +3,12 @@ import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { testFilter } from '../../TestingTools/FilterTestHelpers';
 import { PriorityField } from '../../../src/Query/Filter/PriorityField';
 
-import { toBeValid, toHaveExplanation } from '../../CustomMatchers/CustomMatchersForFilters';
-
 import {
     expectTaskComparesAfter,
     expectTaskComparesBefore,
     expectTaskComparesEqual,
 } from '../../CustomMatchers/CustomMatchersForSorting';
 
-expect.extend({
-    toBeValid,
-    toHaveExplanation,
-});
-
 function testTaskFilterForTaskWithPriority(filter: string, priority: Priority, expected: boolean) {
     const builder = new TaskBuilder();
     const filterOrError = new PriorityField().createFilterOrErrorMessage(filter);
diff --git a/tests/Query/Filter/RecurrenceField.test.ts b/tests/Query/Filter/RecurrenceField.test.ts
index 228a32e4..5f9cdf5d 100644
--- a/tests/Query/Filter/RecurrenceField.test.ts
+++ b/tests/Query/Filter/RecurrenceField.test.ts
@@ -5,16 +5,10 @@ import moment from 'moment';
 
 import { RecurrenceField } from '../../../src/Query/Filter/RecurrenceField';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
-import { toBeValid, toMatchTask } from '../../CustomMatchers/CustomMatchersForFilters';
 import { RecurrenceBuilder } from '../../TestingTools/RecurrenceBuilder';
 
 window.moment = moment;
 
-expect.extend({
-    toBeValid,
-    toMatchTask,
-});
-
 describe('recurrence', () => {
     // Note: We don't need to check all behaviours that are implemented in the base class.
     // These are minimal tests to confirm that the filters are correctly wired up,
diff --git a/tests/Query/Filter/ScheduledDateField.test.ts b/tests/Query/Filter/ScheduledDateField.test.ts
index 21cffa2d..9750f047 100644
--- a/tests/Query/Filter/ScheduledDateField.test.ts
+++ b/tests/Query/Filter/ScheduledDateField.test.ts
@@ -2,17 +2,12 @@
  * @jest-environment jsdom
  */
 import moment from 'moment';
-import { toHaveExplanation } from '../../CustomMatchers/CustomMatchersForFilters';
 import { ScheduledDateField } from '../../../src/Query/Filter/ScheduledDateField';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { expectTaskComparesAfter, expectTaskComparesBefore } from '../../CustomMatchers/CustomMatchersForSorting';
 
 window.moment = moment;
 
-expect.extend({
-    toHaveExplanation,
-});
-
 describe('explain scheduled date queries', () => {
     it('should explain explicit date', () => {
         const filterOrMessage = new ScheduledDateField().createFilterOrErrorMessage('scheduled before 2023-01-02');
diff --git a/tests/Query/Filter/StartDateField.test.ts b/tests/Query/Filter/StartDateField.test.ts
index 17811df7..aabab759 100644
--- a/tests/Query/Filter/StartDateField.test.ts
+++ b/tests/Query/Filter/StartDateField.test.ts
@@ -2,17 +2,12 @@
  * @jest-environment jsdom
  */
 import moment from 'moment';
-import { toHaveExplanation } from '../../CustomMatchers/CustomMatchersForFilters';
 import { StartDateField } from '../../../src/Query/Filter/StartDateField';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { expectTaskComparesAfter, expectTaskComparesBefore } from '../../CustomMatchers/CustomMatchersForSorting';
 
 window.moment = moment;
 
-expect.extend({
-    toHaveExplanation,
-});
-
 describe('explain start date queries', () => {
     it('should explain explicit date', () => {
         const filterOrMessage = new StartDateField().createFilterOrErrorMessage('starts before 2023-01-02');
diff --git a/tests/Query/Filter/StatusField.test.ts b/tests/Query/Filter/StatusField.test.ts
index 7562c84d..3319cf1b 100644
--- a/tests/Query/Filter/StatusField.test.ts
+++ b/tests/Query/Filter/StatusField.test.ts
@@ -1,6 +1,5 @@
 import { StatusField } from '../../../src/Query/Filter/StatusField';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
-import { toMatchTaskFromLine, toMatchTaskWithStatus } from '../../CustomMatchers/CustomMatchersForFilters';
 import { Status } from '../../../src/Status';
 import * as TestHelpers from '../../TestHelpers';
 import {
@@ -10,11 +9,6 @@ import {
 } from '../../CustomMatchers/CustomMatchersForSorting';
 import { StatusConfiguration, StatusType } from '../../../src/StatusConfiguration';
 
-expect.extend({
-    toMatchTaskFromLine,
-    toMatchTaskWithStatus,
-});
-
 describe('status', () => {
     it('done', () => {
         // Arrange
diff --git a/tests/Query/Filter/StatusNameField.test.ts b/tests/Query/Filter/StatusNameField.test.ts
index 5f9f575e..0a69a064 100644
--- a/tests/Query/Filter/StatusNameField.test.ts
+++ b/tests/Query/Filter/StatusNameField.test.ts
@@ -1,17 +1,11 @@
 import { StatusNameField } from '../../../src/Query/Filter/StatusNameField';
 import * as TestHelpers from '../../TestHelpers';
-import { toBeValid, toMatchTaskFromLine } from '../../CustomMatchers/CustomMatchersForFilters';
 import {
     expectTaskComparesAfter,
     expectTaskComparesBefore,
     expectTaskComparesEqual,
 } from '../../CustomMatchers/CustomMatchersForSorting';
 
-expect.extend({
-    toBeValid,
-    toMatchTaskFromLine,
-});
-
 // Abbreviated names so that the markdown text is aligned
 const todoTask = TestHelpers.fromLine({ line: '- [ ] Xxx' });
 const inprTask = TestHelpers.fromLine({ line: '- [/] Xxx' });
diff --git a/tests/Query/Filter/StatusTypeField.test.ts b/tests/Query/Filter/StatusTypeField.test.ts
index 763b883f..60060400 100644
--- a/tests/Query/Filter/StatusTypeField.test.ts
+++ b/tests/Query/Filter/StatusTypeField.test.ts
@@ -1,6 +1,5 @@
 import { StatusTypeField } from '../../../src/Query/Filter/StatusTypeField';
 import * as TestHelpers from '../../TestHelpers';
-import { toBeValid, toMatchTask, toMatchTaskFromLine } from '../../CustomMatchers/CustomMatchersForFilters';
 import {
     expectTaskComparesAfter,
     expectTaskComparesBefore,
@@ -11,12 +10,6 @@ import { StatusType } from '../../../src/StatusConfiguration';
 import { Status } from '../../../src/Status';
 import * as FilterParser from '../../../src/Query/FilterParser';
 
-expect.extend({
-    toBeValid,
-    toMatchTask,
-    toMatchTaskFromLine,
-});
-
 // Abbreviated names so that the markdown text is aligned
 const todoTask = TestHelpers.fromLine({ line: '- [ ] Todo' });
 const inprTask = TestHelpers.fromLine({ line: '- [/] In progress' });
diff --git a/tests/Query/Filter/TagsField.test.ts b/tests/Query/Filter/TagsField.test.ts
index c4f15f2c..1682f60d 100644
--- a/tests/Query/Filter/TagsField.test.ts
+++ b/tests/Query/Filter/TagsField.test.ts
@@ -1,7 +1,6 @@
 import { resetSettings, updateSettings } from '../../../src/Config/Settings';
 import type { FilteringCase } from '../../TestingTools/FilterTestHelpers';
 import { shouldSupportFiltering } from '../../TestingTools/FilterTestHelpers';
-import { toMatchTaskFromLine } from '../../CustomMatchers/CustomMatchersForFilters';
 import { TagsField } from '../../../src/Query/Filter/TagsField';
 import { DescriptionField } from '../../../src/Query/Filter/DescriptionField';
 import { fromLine } from '../../TestHelpers';
@@ -9,10 +8,6 @@ import { Sort } from '../../../src/Query/Sort';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { expectTaskComparesAfter, expectTaskComparesBefore } from '../../CustomMatchers/CustomMatchersForSorting';
 
-expect.extend({
-    toMatchTaskFromLine,
-});
-
 describe('tag/tags', () => {
     it('should honour any # in query', () => {
         const filter = new TagsField().createFilterOrErrorMessage('tags include #home');
diff --git a/tests/Query/Filter/UrgencyField.test.ts b/tests/Query/Filter/UrgencyField.test.ts
index c97d5cb5..434e495d 100644
--- a/tests/Query/Filter/UrgencyField.test.ts
+++ b/tests/Query/Filter/UrgencyField.test.ts
@@ -6,8 +6,6 @@ import { Priority } from '../../../src/Task';
 import { TaskBuilder } from '../../TestingTools/TaskBuilder';
 import { UrgencyField } from '../../../src/Query/Filter/UrgencyField';
 
-import { toBeValid, toHaveExplanation } from '../../CustomMatchers/CustomMatchersForFilters';
-
 import {
     expectTaskComparesAfter,
     expectTaskComparesBefore,
@@ -16,11 +14,6 @@ import {
 
 window.moment = moment;
 
-expect.extend({
-    toBeValid,
-    toHaveExplanation,
-});
-
 describe('urgency', () => {
     it('should not yet be implemented', () => {
         // Arrange