Repository: obsidian-tasks-group/obsidian-tasks (ts)

Issue:
Blocking query doesn't recognize "no entry" emoji from Emoji Shortcodes plugin
### Please check that this issue hasn't been reported before.

- [X] I searched previous [Bug Reports](https://github.com/obsidian-tasks-group/obsidian-tasks/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+bug%22+sort%3Acomments-desc) didn't find any similar reports.

### Expected Behavior

When I add a  ⛔ character with the Emoji Shortcodes plugin (using the `:no_entry:` shortcode), it should indicate that this task is blocked.

### Current behaviour

The Tasks plugin reads that character as a part of the task. Editing the task shows it as part of the name. If you use the edit menu to add the block in, it adds a _different_ "No Entry" emoji. I had Ruby dump the bytes of both characters (in decimal):

- Emoji from Emoji Shortcodes: ⛔, [226, 155, 148]
- Emoji from Tasks: ⛔️, [226, 155, 148, 239, 184, 143]

I'm not an expert on encodings, so I'm not sure what the three extra bytes indicate. UTF-8 vs UTF-16?

To be perfectly frank, I'm not sure if this is an issue with Tasks or Emoji Shortcodes. But every other emoji I've tried with shortcodes lines up with what Tasks is expecting.


### Steps to reproduce

1. Install Tasks and Emoji Shortcodes plugins
2. Create a task with an ID
3. Create a task using the `:no_entry:` shortcode  and put the first tasks ID after it
4. Write a `is blocking` or `is blocked` query and tasks do not show up

### Which Operating Systems are you using?

- [ ] Android
- [ ] iPhone/iPad
- [ ] Linux
- [X] macOS
- [ ] Windows

### Obsidian Version

1.5.8

### Tasks Plugin Version

6.1.0

### Checks

- [ ] I have tried it with all other plugins disabled and the error still occurs

### Possible solution

When checking for emoji, look for both versions of the character.

Pull request:
fix: Enable Tasks to read "no entry" emoji from Emoji Shortcodes plugin
<!--- Provide a general summary of your changes in the Title above -->

# Description

Enable Tasks to read dependsOn emoji - ⛔ - that do not have the Variant Selector 16 (VS16) character.

That invisible character was accidentally included in the Tasks plugin source code, meaning Tasks would only parse dependsOn values with that character.

_Note: This does not yet stop Tasks writing out VS16 for this emoji - I will do that as a separate PR as it's quite a large change._

## Motivation and Context

Fix #2693.


## How has this been tested?

- Adding new tests, which initially failed
- Exploratory testing to confirm that I could reproduce the bug before the fix, and that it was fixed afterwars
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, tests ran to see how -->
<!--- your change affects other areas of the code, etc. -->

## Screenshots (if appropriate)

## 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] **Bug fix** (prefix: `fix` - non-breaking change which fixes an issue)

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://publish.obsidian.md/tasks-contributing/Documentation/About+Documentation) accordingly.
- [x] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).

## Terms

<!--
By submitting this pull request, you must agree to follow our
[contributing guide](https://publish.obsidian.md/tasks-contributing) 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://publish.obsidian.md/tasks-contributing)
- [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/src/TaskSerializer/DefaultTaskSerializer.ts b/src/TaskSerializer/DefaultTaskSerializer.ts
index 584f76ce..3a7a13d2 100644
--- a/src/TaskSerializer/DefaultTaskSerializer.ts
+++ b/src/TaskSerializer/DefaultTaskSerializer.ts
@@ -69,7 +69,7 @@ export const DEFAULT_SYMBOLS: DefaultTaskSerializerSymbols = {
     doneDateSymbol: '✅',
     cancelledDateSymbol: '❌',
     recurrenceSymbol: '🔁',
-    dependsOnSymbol: '⛔️',
+    dependsOnSymbol: '⛔️', // TODO Remove Variant Selector 16 from this string
     idSymbol: '🆔',
     TaskFormatRegularExpressions: {
         // The following regex's end with `$` because they will be matched and
@@ -83,7 +83,10 @@ export const DEFAULT_SYMBOLS: DefaultTaskSerializerSymbols = {
         doneDateRegex: /✅ *(\d{4}-\d{2}-\d{2})$/u,
         cancelledDateRegex: /❌ *(\d{4}-\d{2}-\d{2})$/u,
         recurrenceRegex: /🔁 ?([a-zA-Z0-9, !]+)$/iu,
-        dependsOnRegex: new RegExp('⛔️ *(' + taskIdRegex.source + '( *, *' + taskIdRegex.source + ' *)*)$', 'iu'),
+        dependsOnRegex: new RegExp(
+            '⛔\uFE0F? *(' + taskIdRegex.source + '( *, *' + taskIdRegex.source + ' *)*)$',
+            'iu',
+        ),
         idRegex: new RegExp('🆔 *(' + taskIdRegex.source + ')$', 'iu'),
     },
 } as const;


Test patch:
diff --git a/tests/TaskSerializer/DefaultTaskSerializer.test.ts b/tests/TaskSerializer/DefaultTaskSerializer.test.ts
index e8cdc85c..cf555355 100644
--- a/tests/TaskSerializer/DefaultTaskSerializer.test.ts
+++ b/tests/TaskSerializer/DefaultTaskSerializer.test.ts
@@ -5,7 +5,11 @@ import moment from 'moment';
 import type { Settings } from '../../src/Config/Settings';
 import { DefaultTaskSerializer } from '../../src/TaskSerializer';
 import { RecurrenceBuilder } from '../TestingTools/RecurrenceBuilder';
-import { DEFAULT_SYMBOLS, type DefaultTaskSerializerSymbols } from '../../src/TaskSerializer/DefaultTaskSerializer';
+import {
+    DEFAULT_SYMBOLS,
+    type DefaultTaskSerializerSymbols,
+    allTaskPluginEmojis,
+} from '../../src/TaskSerializer/DefaultTaskSerializer';
 import { TaskBuilder } from '../TestingTools/TaskBuilder';
 import { Priority } from '../../src/Task/Priority';
 
@@ -30,6 +34,19 @@ function hasVariantSelector16(text: string) {
     return text.match(vs16Regex) !== null;
 }
 
+describe('validate emojis', () => {
+    // If these tests fail, paste the problem emoji in to https://apps.timwhitlock.info/unicode/inspect
+    it.each(allTaskPluginEmojis())('emoji does not contain Variant Selector 16: "%s"', (emoji: string) => {
+        if (emoji === DEFAULT_SYMBOLS.dependsOnSymbol) {
+            // TODO Remove the VS16 from the dependsOn symbol:
+            //      see https://github.com/obsidian-tasks-group/obsidian-tasks/issues/2693
+            expect(hasVariantSelector16(emoji)).toBe(true);
+        } else {
+            expect(hasVariantSelector16(emoji)).toBe(false);
+        }
+    });
+});
+
 // NEW_TASK_FIELD_EDIT_REQUIRED
 
 describe.each(symbolMap)("DefaultTaskSerializer with '$taskFormat' symbols", ({ symbols }) => {
@@ -88,7 +105,7 @@ describe.each(symbolMap)("DefaultTaskSerializer with '$taskFormat' symbols", ({
             });
 
             it('should parse a high priority with Variant Selector 16', () => {
-                // This test showed the existing of https://github.com/obsidian-tasks-group/obsidian-tasks/issues/2273
+                // This test showed the existence of https://github.com/obsidian-tasks-group/obsidian-tasks/issues/2273
                 const line = '⏫️'; // There is a hidden Variant Selector 16 character at the end of this string
                 expect(hasVariantSelector16(line)).toBe(true);
 
@@ -111,6 +128,23 @@ describe.each(symbolMap)("DefaultTaskSerializer with '$taskFormat' symbols", ({
                 expect(taskDetails).toMatchTaskDetails({ dependsOn: ['F12345'] });
             });
 
+            it('should parse depends on one task - without Variant Selector 16', () => {
+                // This test showed the existence of https://github.com/obsidian-tasks-group/obsidian-tasks/issues/2693
+                const id = '⛔ F12345';
+                expect(hasVariantSelector16(id)).toBe(false);
+
+                const taskDetails = deserialize(id);
+                expect(taskDetails).toMatchTaskDetails({ dependsOn: ['F12345'] });
+            });
+
+            it('should parse depends on one task - with Variant Selector 16', () => {
+                const id = '⛔️ F12345'; // There is a hidden Variant Selector 16 character at the end of this string
+                expect(hasVariantSelector16(id)).toBe(true);
+
+                const taskDetails = deserialize(id);
+                expect(taskDetails).toMatchTaskDetails({ dependsOn: ['F12345'] });
+            });
+
             it('should parse depends on two tasks', () => {
                 const id = `${dependsOnSymbol} 123456,abC123`;
                 const taskDetails = deserialize(id);