Repository: obsidian-tasks-group/obsidian-tasks
Pull request #1553 (2023-01-19 09:40:26)

Feat group by status type
<!--- Provide a general summary of your changes in the Title above -->

# Description

Add and document `group by status.type`

In order to impose a useful sort order, the types are prefixed with a number, so the groups will appear in this order, and with these group names:
        - `1 IN_PROGRESS`
        - `2 TODO`
        - `3 DONE`
        - `4 CANCELLED`
        - `5 NON_TASK`

## Motivation and Context

I found I really needed it.

## How has this been tested?

- new tests
- manual testing

## Screenshots (if appropriate)

![image](https://user-images.githubusercontent.com/4840096/213407489-b4a43c45-fd78-4f26-bb68-893744b5d2bb.png)


## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

Changes visible to users:

- [x] **New feature** (prefix: `feat` - non-breaking change which adds functionality)
- [x] **Documentation** (prefix: `docs` - improvements to any documentation content)

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`.
- [x] My change requires a change to the documentation.
- [x] 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/docs/getting-started/statuses.md b/docs/getting-started/statuses.md
index 3a863938..b1c2d4b0 100644
--- a/docs/getting-started/statuses.md
+++ b/docs/getting-started/statuses.md
@@ -60,12 +60,13 @@ The tasks shown are purely examples for context. The `~` column is just an arbit
 | Matches `status.type includes CANCELLED` | no | no | no | YES | no |
 | Matches `status.type includes NON_TASK` | no | no | no | no | YES |
 | Name for `group by status` | Todo | Done | Done | Done | Done |
+| Name for `group by status.type` | 2 TODO | 1 IN_PROGRESS | 3 DONE | 4 CANCELLED | 5 NON_TASK |
 | Name for `group by status.name` | Todo | In Progress | Done | Cancelled | My custom status |
 
 <!-- placeholder to force blank line after table --> <!-- endInclude -->
 
 {: .warning }
-The `group by status` results of the above table are subject to change.
+The `group by` results of the above table are subject to change.
 
 ## Standard Markdown task statuses
 
@@ -91,7 +92,7 @@ This table shows the statuses provided by default:
 
 <!-- placeholder to force blank line before table --> <!-- include: DocsSamplesForStatuses.test.DefaultStatuses_core-statuses.approved.md -->
 
-| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type | Needs Custom Styling |
+| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type<br>`status.type includes...`<br>`sort by status.type`<br>`group by status.type` | Needs Custom Styling |
 | ----- | ----- | ----- | ----- | ----- |
 | `space` | Todo | `x` | `TODO` | No |
 | `/` | In Progress | `x` | `IN_PROGRESS` | Yes |
@@ -136,7 +137,7 @@ Remember to set up your chosen CSS Snippet or Theme before setting up the custom
 
 <!-- placeholder to force blank line before table --> <!-- include: DocsSamplesForStatuses.test.DefaultStatuses_minimal-supported-statuses.approved.md -->
 
-| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type | Needs Custom Styling |
+| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type<br>`status.type includes...`<br>`sort by status.type`<br>`group by status.type` | Needs Custom Styling |
 | ----- | ----- | ----- | ----- | ----- |
 | `>` | Forwarded | `x` | `TODO` | Yes |
 | `<` | Schedule | `x` | `TODO` | Yes |
@@ -164,7 +165,7 @@ Remember to set up your chosen CSS Snippet or Theme before setting up the custom
 
 <!-- placeholder to force blank line before table --> <!-- include: DocsSamplesForStatuses.test.DefaultStatuses_its-theme-supported-statuses.approved.md -->
 
-| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type | Needs Custom Styling |
+| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type<br>`status.type includes...`<br>`sort by status.type`<br>`group by status.type` | Needs Custom Styling |
 | ----- | ----- | ----- | ----- | ----- |
 | `>` | Forward | `x` | `TODO` | Yes |
 | `D` | Deferred/Scheduled | `x` | `TODO` | Yes |
@@ -205,11 +206,14 @@ We are tracking this in [issue #1486](https://github.com/obsidian-tasks-group/ob
 
 - `done` - matches tasks status types `TODO` and `CANCELLED`
 - `not done` - matches tasks with status types `TODO` and `IN_PROGRESS`
-- `status.name` text search
-- `sort by status.name`
-- `group by status.name`
-- `status.type` text search
-- `sort by status.type`
+- **Status Name**
+  - `status.name` text search
+  - `sort by status.name`
+  - `group by status.name`
+- **Status Type**
+  - `status.type` text search
+  - `sort by status.type`
+  - `group by status.type`
 
 For details, see [Filters for Task Statuses]({{ site.baseurl }}{% link queries/filters.md %}#filters-for-task-statuses)
 
diff --git a/docs/how-to/set-up-custom-statuses.md b/docs/how-to/set-up-custom-statuses.md
index daf3d540..70abc0dc 100644
--- a/docs/how-to/set-up-custom-statuses.md
+++ b/docs/how-to/set-up-custom-statuses.md
@@ -73,7 +73,7 @@ Suppose that you wanted to create a set of 3 statuses that cycle between each ot
 
 <!-- placeholder to force blank line before table --> <!-- include: DocsSamplesForStatuses.test.DefaultStatuses_important-cycle.approved.md -->
 
-| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type | Needs Custom Styling |
+| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type<br>`status.type includes...`<br>`sort by status.type`<br>`group by status.type` | Needs Custom Styling |
 | ----- | ----- | ----- | ----- | ----- |
 | `!` | Important | `D` | `TODO` | Yes |
 | `D` | Doing - Important | `X` | `TODO` | Yes |
diff --git a/docs/queries/grouping.md b/docs/queries/grouping.md
index 888a19b4..b4c1beda 100644
--- a/docs/queries/grouping.md
+++ b/docs/queries/grouping.md
@@ -69,11 +69,21 @@ You can group by the following properties.
 1. `status` (Done or Todo, which is capitalized for visibility in the headings)
     - Note that the Done group is displayed before the Todo group,
       which differs from the Sorting ordering of this property.
-1. `group by status.name`
+1. `status.name`
     - This groups by the names you give to your custom statuses, in alphabetical order.
+1. `status.type`
+    - This groups by the types you have given to your custom statuses.
+    - In order to impose a useful sort order, the types are prefixed with a number, so the groups will appear in this order, and with these group names:
+        - `1 IN_PROGRESS`
+        - `2 TODO`
+        - `3 DONE`
+        - `4 CANCELLED`
+        - `5 NON_TASK`
 
-{: .released }  
-`group by status.name` was introduced in Tasks X.Y.Z.
+{: .released }
+`group by status.name` and `group by status.type` were introduced in Tasks X.Y.Z.
+
+For more information, including adding your own customised statuses, see [Statuses]({{ site.baseurl }}{% link getting-started/statuses.md %}).
 
 ### Other task properties
 
diff --git a/docs/quick-reference/index.md b/docs/quick-reference/index.md
index 02e0ab5d..e5d3dd16 100644
--- a/docs/quick-reference/index.md
+++ b/docs/quick-reference/index.md
@@ -20,7 +20,7 @@ This table summarizes the filters and other options available inside a `tasks` b
 | `done`<br>`not done`                                                                                                                                                                                | `sort by status`                            | `group by status`      |                        |
 | `done (before, after, on) <date>`<br>`has done date`<br>`no  done date`<br>`done date is invalid`                                                                                                   | `sort by done`                              | `group by done`        | `hide done date`       |
 | `status.name (includes, does not include) <string>`<br>`status.name (regex matches, regex does not match) /regex/i`                                                                                 | `sort by status.name`                       | `group by status.name` |                        |
-| `status.type (includes, does not include) <string>`<br>`status.type (regex matches, regex does not match) /regex/i`                                                                                 | `sort by status.type`                       |                        |                        |
+| `status.type (includes, does not include) <string>`<br>`status.type (regex matches, regex does not match) /regex/i`                                                                                 | `sort by status.type`                       | `group by status.type` |                        |
 | `starts (before, after, on) <date>`<br>`has start date`<br>`no  start date`<br>`start date is invalid`                                                                                              | `sort by start`                             | `group by start`       | `hide start date`      |
 | `scheduled (before, after, on) <date>`<br>`has scheduled date`<br>`no  scheduled date`<br>`scheduled date is invalid`                                                                               | `sort by scheduled`                         | `group by scheduled`   | `hide scheduled date`  |
 | `due (before, after, on) <date>`<br>`has due date`<br>`no  due date`<br>`due date is invalid`                                                                                                       | `sort by due`                               | `group by due`         | `hide due date`        |
diff --git a/src/Query/Filter/StatusNameField.ts b/src/Query/Filter/StatusNameField.ts
index de0fbb6f..3a295886 100644
--- a/src/Query/Filter/StatusNameField.ts
+++ b/src/Query/Filter/StatusNameField.ts
@@ -1,5 +1,4 @@
 import type { Task } from '../../Task';
-import type { GrouperFunction } from '../Grouper';
 import { TextField } from './TextField';
 
 /**
@@ -25,10 +24,4 @@ export class StatusNameField extends TextField {
     public supportsGrouping(): boolean {
         return true;
     }
-
-    public grouper(): GrouperFunction {
-        return (task: Task) => {
-            return [this.value(task)];
-        };
-    }
 }
diff --git a/src/Query/Filter/StatusTypeField.ts b/src/Query/Filter/StatusTypeField.ts
index a52d4f71..6f423488 100644
--- a/src/Query/Filter/StatusTypeField.ts
+++ b/src/Query/Filter/StatusTypeField.ts
@@ -1,4 +1,6 @@
 import type { Task } from '../../Task';
+import type { GrouperFunction } from '../Grouper';
+import { StatusType } from '../../StatusConfiguration';
 import { TextField } from './TextField';
 
 /**
@@ -20,4 +22,36 @@ export class StatusTypeField extends TextField {
     supportsSorting(): boolean {
         return true;
     }
+
+    public supportsGrouping(): boolean {
+        return true;
+    }
+
+    public grouper(): GrouperFunction {
+        return (task: Task) => {
+            let prefix: string;
+            // Add a numeric prefix to sort in to a meaningful order for users
+            switch (task.status.type) {
+                case StatusType.IN_PROGRESS:
+                    prefix = '1';
+                    break;
+                case StatusType.TODO:
+                    prefix = '2';
+                    break;
+                case StatusType.DONE:
+                    prefix = '3';
+                    break;
+                case StatusType.CANCELLED:
+                    prefix = '4';
+                    break;
+                case StatusType.NON_TASK:
+                    prefix = '5';
+                    break;
+                case StatusType.EMPTY:
+                    prefix = '6';
+                    break;
+            }
+            return [prefix + ' ' + task.status.type];
+        };
+    }
 }
diff --git a/src/Query/Filter/TextField.ts b/src/Query/Filter/TextField.ts
index 92cce099..2be2e3c1 100644
--- a/src/Query/Filter/TextField.ts
+++ b/src/Query/Filter/TextField.ts
@@ -4,6 +4,7 @@ import { RegexMatcher } from '../Matchers/RegexMatcher';
 import type { IStringMatcher } from '../Matchers/IStringMatcher';
 import { Explanation } from '../Explain/Explanation';
 import type { Comparator } from '../Sorter';
+import type { GrouperFunction } from '../Grouper';
 import { Field } from './Field';
 import type { FilterFunction } from './Filter';
 import { Filter, FilterOrErrorMessage } from './Filter';
@@ -99,4 +100,17 @@ export abstract class TextField extends Field {
             return this.value(a).localeCompare(this.value(b), undefined, { numeric: true });
         };
     }
+
+    /**
+     * A default implementation of grouping, for text fields where simple grouping by field value is the
+     * desired behaviour.
+     *
+     * Each class that wants to use this will need to override supportsGrouping() to return true,
+     * to turn on grouping.
+     */
+    public grouper(): GrouperFunction {
+        return (task: Task) => {
+            return [this.value(task)];
+        };
+    }
 }
diff --git a/src/Query/Group.ts b/src/Query/Group.ts
index 2fbbb5ef..9b249342 100644
--- a/src/Query/Group.ts
+++ b/src/Query/Group.ts
@@ -36,7 +36,7 @@ export class Group {
         return grouping.grouper(task);
     }
 
-    public static grouperForProperty(property: GroupingProperty) {
+    public static grouperForProperty(property: GroupingProperty): GrouperFunction {
         return Group.groupers[property];
     }
 


Test patch:
diff --git a/tests/DocsSamplesForStatuses.test.DefaultStatuses_core-statuses.approved.md b/tests/DocsSamplesForStatuses.test.DefaultStatuses_core-statuses.approved.md
index 8e4b632b..a7fb618d 100644
--- a/tests/DocsSamplesForStatuses.test.DefaultStatuses_core-statuses.approved.md
+++ b/tests/DocsSamplesForStatuses.test.DefaultStatuses_core-statuses.approved.md
@@ -1,6 +1,6 @@
 <!-- placeholder to force blank line before table -->
 
-| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type | Needs Custom Styling |
+| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type<br>`status.type includes...`<br>`sort by status.type`<br>`group by status.type` | Needs Custom Styling |
 | ----- | ----- | ----- | ----- | ----- |
 | `space` | Todo | `x` | `TODO` | No |
 | `/` | In Progress | `x` | `IN_PROGRESS` | Yes |
diff --git a/tests/DocsSamplesForStatuses.test.DefaultStatuses_important-cycle.approved.md b/tests/DocsSamplesForStatuses.test.DefaultStatuses_important-cycle.approved.md
index f0ae2a48..73be963a 100644
--- a/tests/DocsSamplesForStatuses.test.DefaultStatuses_important-cycle.approved.md
+++ b/tests/DocsSamplesForStatuses.test.DefaultStatuses_important-cycle.approved.md
@@ -1,6 +1,6 @@
 <!-- placeholder to force blank line before table -->
 
-| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type | Needs Custom Styling |
+| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type<br>`status.type includes...`<br>`sort by status.type`<br>`group by status.type` | Needs Custom Styling |
 | ----- | ----- | ----- | ----- | ----- |
 | `!` | Important | `D` | `TODO` | Yes |
 | `D` | Doing - Important | `X` | `TODO` | Yes |
diff --git a/tests/DocsSamplesForStatuses.test.DefaultStatuses_its-theme-supported-statuses.approved.md b/tests/DocsSamplesForStatuses.test.DefaultStatuses_its-theme-supported-statuses.approved.md
index 453bc16d..428b0088 100644
--- a/tests/DocsSamplesForStatuses.test.DefaultStatuses_its-theme-supported-statuses.approved.md
+++ b/tests/DocsSamplesForStatuses.test.DefaultStatuses_its-theme-supported-statuses.approved.md
@@ -1,6 +1,6 @@
 <!-- placeholder to force blank line before table -->
 
-| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type | Needs Custom Styling |
+| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type<br>`status.type includes...`<br>`sort by status.type`<br>`group by status.type` | Needs Custom Styling |
 | ----- | ----- | ----- | ----- | ----- |
 | `>` | Forward | `x` | `TODO` | Yes |
 | `D` | Deferred/Scheduled | `x` | `TODO` | Yes |
diff --git a/tests/DocsSamplesForStatuses.test.DefaultStatuses_minimal-supported-statuses.approved.md b/tests/DocsSamplesForStatuses.test.DefaultStatuses_minimal-supported-statuses.approved.md
index b968c032..3cd4596b 100644
--- a/tests/DocsSamplesForStatuses.test.DefaultStatuses_minimal-supported-statuses.approved.md
+++ b/tests/DocsSamplesForStatuses.test.DefaultStatuses_minimal-supported-statuses.approved.md
@@ -1,6 +1,6 @@
 <!-- placeholder to force blank line before table -->
 
-| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type | Needs Custom Styling |
+| Status Character | Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name` | Next Status Character | Status Type<br>`status.type includes...`<br>`sort by status.type`<br>`group by status.type` | Needs Custom Styling |
 | ----- | ----- | ----- | ----- | ----- |
 | `>` | Forwarded | `x` | `TODO` | Yes |
 | `<` | Schedule | `x` | `TODO` | Yes |
diff --git a/tests/DocsSamplesForStatuses.test.Status_Transitions status-types.approved.md b/tests/DocsSamplesForStatuses.test.Status_Transitions status-types.approved.md
index e4705300..de7f45ed 100644
--- a/tests/DocsSamplesForStatuses.test.Status_Transitions status-types.approved.md
+++ b/tests/DocsSamplesForStatuses.test.Status_Transitions status-types.approved.md
@@ -15,6 +15,7 @@
 | Matches `status.type includes CANCELLED` | no | no | no | YES | no |
 | Matches `status.type includes NON_TASK` | no | no | no | no | YES |
 | Name for `group by status` | Todo | Done | Done | Done | Done |
+| Name for `group by status.type` | 2 TODO | 1 IN_PROGRESS | 3 DONE | 4 CANCELLED | 5 NON_TASK |
 | Name for `group by status.name` | Todo | In Progress | Done | Cancelled | My custom status |
 
 
diff --git a/tests/DocsSamplesForStatuses.test.ts b/tests/DocsSamplesForStatuses.test.ts
index 4a7709a3..a0b34ac8 100644
--- a/tests/DocsSamplesForStatuses.test.ts
+++ b/tests/DocsSamplesForStatuses.test.ts
@@ -10,6 +10,7 @@ import type { FilterOrErrorMessage } from '../src/Query/Filter/Filter';
 import * as FilterParser from '../src/Query/FilterParser';
 import { Group } from '../src/Query/Group';
 import { StatusNameField } from '../src/Query/Filter/StatusNameField';
+import { StatusTypeField } from '../src/Query/Filter/StatusTypeField';
 import { TaskBuilder } from './TestingTools/TaskBuilder';
 
 class MarkdownTable {
@@ -65,7 +66,7 @@ function verifyStatusesAsMarkdownTable(statuses: Status[]) {
         'Status Character',
         'Status Name<br>`status.name includes...`<br>`sort by status.name`<br>`group by status.name`',
         'Next Status Character',
-        'Status Type',
+        'Status Type<br>`status.type includes...`<br>`sort by status.type`<br>`group by status.type`',
         'Needs Custom Styling',
     ]);
 
@@ -155,25 +156,19 @@ function verifyTransitionsAsMarkdownTable(statuses: Status[]) {
     filterAllStatuses(FilterParser.parseFilter('status.type includes CANCELLED')!);
     filterAllStatuses(FilterParser.parseFilter('status.type includes NON_TASK')!);
 
-    {
-        const cells: string[] = ['Name for `group by status`'];
+    function showGroupNamesForAllTasks(groupName: string, grouperFunction: (task: Task) => string[]) {
+        const cells: string[] = ['Name for `group by ' + groupName + '`'];
         tasks.forEach((task) => {
-            const groupNamesForTask = Group.getGroupNamesForTask(Group.fromGroupingProperty('status'), task);
+            const groupNamesForTask = grouperFunction(task);
             const names = groupNamesForTask.join(',');
             cells.push(names);
         });
         table.addRow(cells);
     }
 
-    {
-        const cells: string[] = ['Name for `group by status.name`'];
-        tasks.forEach((task) => {
-            const groupNamesForTask = new StatusNameField().createGrouper().grouper(task);
-            const names = groupNamesForTask.join(',');
-            cells.push(names);
-        });
-        table.addRow(cells);
-    }
+    showGroupNamesForAllTasks('status', Group.grouperForProperty('status'));
+    showGroupNamesForAllTasks('status.type', new StatusTypeField().createGrouper().grouper);
+    showGroupNamesForAllTasks('status.name', new StatusNameField().createGrouper().grouper);
 
     table.verify();
 }
diff --git a/tests/Query.test.ts b/tests/Query.test.ts
index 8bcbaab4..c8bb3223 100644
--- a/tests/Query.test.ts
+++ b/tests/Query.test.ts
@@ -189,6 +189,7 @@ describe('Query parsing', () => {
             'group by scheduled',
             'group by start',
             'group by status',
+            'group by status.name',
             'group by status.type',
             'group by tags',
         ];
@@ -266,6 +267,7 @@ describe('Query parsing', () => {
     it('should parse ambiguous group by queries correctly', () => {
         expect(new Query({ source: 'group by status' }).grouping[0].property).toEqual('status');
         expect(new Query({ source: 'group by status.name' }).grouping[0].property).toEqual('status.name');
+        expect(new Query({ source: 'group by status.type' }).grouping[0].property).toEqual('status.type');
     });
 });
 
diff --git a/tests/Query/Filter/StatusTypeField.test.ts b/tests/Query/Filter/StatusTypeField.test.ts
index b643a3d7..abd77790 100644
--- a/tests/Query/Filter/StatusTypeField.test.ts
+++ b/tests/Query/Filter/StatusTypeField.test.ts
@@ -108,3 +108,24 @@ describe('sorting by status.name', () => {
         expectTaskComparesAfter(sorter, emptTask, inprTask);
     });
 });
+
+describe('grouping by status.type', () => {
+    it('supports Field grouping methods correctly', () => {
+        const field = new StatusTypeField();
+        expect(field.supportsGrouping()).toEqual(true);
+    });
+
+    it('group by status.type', () => {
+        // Arrange
+        const grouper = new StatusTypeField().createGrouper();
+
+        // // Assert
+        expect(grouper.grouper(inprTask)).toEqual(['1 IN_PROGRESS']);
+        expect(grouper.grouper(todoTask)).toEqual(['2 TODO']);
+        expect(grouper.grouper(unknTask)).toEqual(['2 TODO']);
+        expect(grouper.grouper(doneTask)).toEqual(['3 DONE']);
+        expect(grouper.grouper(cancTask)).toEqual(['4 CANCELLED']);
+        expect(grouper.grouper(non_Task)).toEqual(['5 NON_TASK']);
+        expect(grouper.grouper(emptTask)).toEqual(['6 EMPTY']); // won't be seen by users
+    });
+});