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

Issue:
Unicode Variant selectors on some emojis work, while others are parsed incorrectly
### 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.

### Steps to reproduce

1. Launch an Obsidian vault the latest Tasks 7.13.0 plugin
2. Create a task by manually entering scheduled or due date emojis (hour glass not done, or calendar) using an emoji picker, not the prompt
3. Create a tasks query that uses the emojis, e.g.
```tasks
not done
has scheduled date
sort by description
group by scheduled
```
Result:
Recognized: `- [ ] test ⏳ 2024-11-18`, Not recognized: `- [ ] test ⏳️ 2024-11-18`

### Expected Behavior

Tasks should ignore emoji variants like what is described in https://github.com/obsidian-tasks-group/obsidian-tasks/issues/2273, except on _all_ emojis. Emoji variants should have no effect on its parsing behavior.

### Current behaviour

Currently, Tasks ignores the variant selector on the priority arrows (a previous issue), however it does not ignore it on other emojis like Scheduled. The solution appears to be focused on a specific emoji case, when really it can show up in all emojis. I was initially confused as to why tasks wouldn't show up using a filter, until I played around with different methods of getting the emoji and realized that it must be a variant selector. Sure enough, I was getting `\ufe0f\u23f3` on the hour glass emoji that didn't work, and just `\ufe0f` on the one that did.

### Which Operating Systems are you using?

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

### Obsidian Version

1.6.7

### Tasks Plugin Version

7.13.0

### Checks

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

### Possible solution

Implement a solution similar to https://github.com/obsidian-tasks-group/obsidian-tasks/pull/2695, except ignoring variant selectors in _all_ emojis.

Pull request description:
fix: Handle any hidden VS16 character on all emojis
# Types of changes

Changes visible to users:

- [x] **Bug fix** (prefix: `fix` - non-breaking change which fixes an issue)
  - Issue/discussion: #3179

Internal changes:

- [x] **Tests** (prefix: `test` - additions and improvements to unit tests and the smoke tests)

## Description

Tasks can now cope with any emojis being followed by VS16 Variation Selectors, not just priorities and depends-on.

## Motivation and Context

Fix #3179

## How has this been tested?

- Automated tests
- Exploratory testing

## Checklist

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [x] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).

## Terms

- [x] My contribution follows 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 2ee9d131..ccb5a4eb 100644
--- a/src/TaskSerializer/DefaultTaskSerializer.ts
+++ b/src/TaskSerializer/DefaultTaskSerializer.ts
@@ -60,10 +60,13 @@ function dateFieldRegex(symbols: string) {
 }
 
 function fieldRegex(symbols: string, valueRegexString: string) {
-    let source = symbols;
+    // \uFE0F? allows an optional Variant Selector 16 on emojis.
+    let source = symbols + '\uFE0F?';
     if (valueRegexString !== '') {
         source += ' *' + valueRegexString;
     }
+    // The regexes end with `$` because they will be matched and
+    // removed from the end until none are left.
     source += '$';
     return new RegExp(source, 'u');
 }
@@ -93,10 +96,7 @@ export const DEFAULT_SYMBOLS: DefaultTaskSerializerSymbols = {
     dependsOnSymbol: '⛔',
     idSymbol: '🆔',
     TaskFormatRegularExpressions: {
-        // The following regex's end with `$` because they will be matched and
-        // removed from the end until none are left.
-        // \uFE0F? allows an optional Variant Selector 16 on emojis.
-        priorityRegex: fieldRegex('([🔺⏫🔼🔽⏬])\uFE0F?', ''),
+        priorityRegex: fieldRegex('([🔺⏫🔼🔽⏬])', ''),
         startDateRegex: dateFieldRegex('🛫'),
         createdDateRegex: dateFieldRegex('➕'),
         scheduledDateRegex: dateFieldRegex('[⏳⌛]'),
@@ -105,7 +105,7 @@ export const DEFAULT_SYMBOLS: DefaultTaskSerializerSymbols = {
         cancelledDateRegex: dateFieldRegex('❌'),
         recurrenceRegex: fieldRegex('🔁', '([a-zA-Z0-9, !]+)'),
         onCompletionRegex: fieldRegex('🏁', '([a-zA-Z]+)'),
-        dependsOnRegex: fieldRegex('⛔\uFE0F?', '(' + taskIdSequenceRegex.source + ')'),
+        dependsOnRegex: fieldRegex('⛔', '(' + taskIdSequenceRegex.source + ')'),
         idRegex: fieldRegex('🆔', '(' + taskIdRegex.source + ')'),
     },
 } as const;


Test patch:
diff --git a/tests/TaskSerializer/DefaultTaskSerializer.test.ts b/tests/TaskSerializer/DefaultTaskSerializer.test.ts
index dd432123..6f501096 100644
--- a/tests/TaskSerializer/DefaultTaskSerializer.test.ts
+++ b/tests/TaskSerializer/DefaultTaskSerializer.test.ts
@@ -66,16 +66,16 @@ describe('validate emoji regular expressions', () => {
         expect(generateRegexApprovalTest()).toMatchInlineSnapshot(`
             "
             priorityRegex: /([🔺⏫🔼🔽⏬])\\ufe0f?$/u
-            startDateRegex: /🛫 *(\\d{4}-\\d{2}-\\d{2})$/u
-            createdDateRegex: /➕ *(\\d{4}-\\d{2}-\\d{2})$/u
-            scheduledDateRegex: /[⏳⌛] *(\\d{4}-\\d{2}-\\d{2})$/u
-            dueDateRegex: /[📅📆🗓] *(\\d{4}-\\d{2}-\\d{2})$/u
-            doneDateRegex: /✅ *(\\d{4}-\\d{2}-\\d{2})$/u
-            cancelledDateRegex: /❌ *(\\d{4}-\\d{2}-\\d{2})$/u
-            recurrenceRegex: /🔁 *([a-zA-Z0-9, !]+)$/u
-            onCompletionRegex: /🏁 *([a-zA-Z]+)$/u
+            startDateRegex: /🛫\\ufe0f? *(\\d{4}-\\d{2}-\\d{2})$/u
+            createdDateRegex: /➕\\ufe0f? *(\\d{4}-\\d{2}-\\d{2})$/u
+            scheduledDateRegex: /[⏳⌛]\\ufe0f? *(\\d{4}-\\d{2}-\\d{2})$/u
+            dueDateRegex: /[📅📆🗓]\\ufe0f? *(\\d{4}-\\d{2}-\\d{2})$/u
+            doneDateRegex: /✅\\ufe0f? *(\\d{4}-\\d{2}-\\d{2})$/u
+            cancelledDateRegex: /❌\\ufe0f? *(\\d{4}-\\d{2}-\\d{2})$/u
+            recurrenceRegex: /🔁\\ufe0f? *([a-zA-Z0-9, !]+)$/u
+            onCompletionRegex: /🏁\\ufe0f? *([a-zA-Z]+)$/u
             dependsOnRegex: /⛔\\ufe0f? *([a-zA-Z0-9-_]+( *, *[a-zA-Z0-9-_]+ *)*)$/u
-            idRegex: /🆔 *([a-zA-Z0-9-_]+)$/u
+            idRegex: /🆔\\ufe0f? *([a-zA-Z0-9-_]+)$/u
             "
         `);
     });
@@ -122,6 +122,15 @@ describe.each(symbolMap)("DefaultTaskSerializer with '$taskFormat' symbols", ({
                 expect(taskDetails).toMatchTaskDetails({ ['scheduledDate']: moment('2021-06-20', 'YYYY-MM-DD') });
             });
 
+            it('should parse a scheduledDate - with Variation Selector', () => {
+                // This test showed the existence of https://github.com/obsidian-tasks-group/obsidian-tasks/issues/3179
+                const input = '⏳️ 2024-11-18';
+                expect(hasVariantSelector16(input)).toBe(true);
+
+                const taskDetails = deserialize(input);
+                expect(taskDetails).toMatchTaskDetails({ ['scheduledDate']: moment('2024-11-18', 'YYYY-MM-DD') });
+            });
+
             it('should parse a dueDate - with non-standard emoji 1', () => {
                 const taskDetails = deserialize('📆 2021-06-20');
                 expect(taskDetails).toMatchTaskDetails({ ['dueDate']: moment('2021-06-20', 'YYYY-MM-DD') });