Repository: test-results-reporter/reporter (js)

Issue:
Do we have an option to mention user groups instead of users in slack integrations?
### Discussed in https://github.com/test-results-reporter/reporter/discussions/163

<div type='discussions-op-text'>

<sup>Originally posted by **akhilaananthkumar** April  5, 2024</sup>
```json
{
  "name": "mentions",
  "inputs": {
    "users": [
      {
        "name": "Jon",
        "slack_uid": "ABCDEFGH"
      }
    ]
  }
}
```

Instead of using **member id** value in slack_uid  field, is there a way to use **group id**?

Slack group id - https://slack.com/intl/en-au/help/articles/212906697-Create-a-user-group</div>

Pull request:
Support slack group name mentions
Closes #164 

**Summary:**
- Support to add slack group id to mentions in test results reporter.
- A new key `slack_gid` for slack group id is added.
- Supports special mentions (to be provided in lower case only and without `@`) - `here`, `channel`, `everyone` 
- Note: for a given user, only one of either `slack_uid` or `slack_gid` is allowed.

Patch:
diff --git a/src/extensions/mentions.js b/src/extensions/mentions.js
index 594c8fe..0c34fae 100644
--- a/src/extensions/mentions.js
+++ b/src/extensions/mentions.js
@@ -34,9 +34,20 @@ function attachForTeam({ extension, payload }) {
   }
 }
 
+function formatSlackMentions({slack_uid, slack_gid}) {
+  if (slack_gid && slack_uid) {
+    throw new Error(`Error in slack extension configuration. Either slack user or group Id is allowed`);
+  }
+  if (slack_uid) {
+    return `<@${slack_uid}>`
+  }
+  const tagPrefix = ["here", "everyone", "channel"].includes(slack_gid.toLowerCase()) ? "" : "subteam^";
+  return `<!${tagPrefix}${slack_gid}>`
+}
+
 function attachForSlack({ extension, payload }) {
   const users = getUsers(extension);
-  const user_ids = users.map(user => `<@${user.slack_uid}>`);
+  const user_ids = users.map(formatSlackMentions);
   if (users.length > 0) {
     addSlackExtension({ payload, extension, text: user_ids.join(' ｜ ') });
   }


Test patch:
diff --git a/test/ext.mentions.spec.js b/test/ext.mentions.spec.js
index 4146187..6b90a6c 100644
--- a/test/ext.mentions.spec.js
+++ b/test/ext.mentions.spec.js
@@ -148,6 +148,90 @@ describe('extensions - mentions', () => {
     assert.equal(mock.getInteraction(id).exercised, true);
   });
 
+  it('should mention group name in slack', async () => {
+    const id = mock.addInteraction('post test-summary with mentions group name to slack');
+    await publish({
+      config: {
+        "reports": [
+          {
+            "targets": [
+              {
+                "name": "slack",
+                "inputs": {
+                  "url": "http://localhost:9393/message"
+                },
+                "extensions": [
+                  {
+                    "name": "mentions",
+                    "inputs": {
+                      "users": [
+                        {
+                          "name": "mom",
+                          "slack_gid": "ULA15K66M"
+                        }
+                      ]
+                    }
+                  }
+                ]
+              }
+            ],
+            "results": [
+              {
+                "type": "testng",
+                "files": [
+                  "test/data/testng/single-suite-failures.xml"
+                ]
+              }
+            ]
+          }
+        ]
+      }
+    });
+    assert.equal(mock.getInteraction(id).exercised, true);
+  });
+
+  it('should mention special group name in slack', async () => {
+    const id = mock.addInteraction('post test-summary with mentions special group name to slack');
+    await publish({
+      config: {
+        "reports": [
+          {
+            "targets": [
+              {
+                "name": "slack",
+                "inputs": {
+                  "url": "http://localhost:9393/message"
+                },
+                "extensions": [
+                  {
+                    "name": "mentions",
+                    "inputs": {
+                      "users": [
+                        {
+                          "name": "mom",
+                          "slack_gid": "here"
+                        }
+                      ]
+                    }
+                  }
+                ]
+              }
+            ],
+            "results": [
+              {
+                "type": "testng",
+                "files": [
+                  "test/data/testng/single-suite-failures.xml"
+                ]
+              }
+            ]
+          }
+        ]
+      }
+    });
+    assert.equal(mock.getInteraction(id).exercised, true);
+  });
+
   it('should mention users with schedule rotation in slack', async () => {
     const id = mock.addInteraction('post test-summary with mentions to slack');
     await publish({
diff --git a/test/mocks/slack.mock.js b/test/mocks/slack.mock.js
index fcb49c7..6657d51 100644
--- a/test/mocks/slack.mock.js
+++ b/test/mocks/slack.mock.js
@@ -385,6 +385,69 @@ addInteractionHandler('post test-summary with mentions to slack', () => {
   }
 });
 
+
+addInteractionHandler('post test-summary with mentions group name to slack', () => {
+  return {
+    request: {
+      method: 'POST',
+      path: '/message',
+      body: {
+        "attachments": [
+          {
+            "color": "#DC143C",
+            "blocks": [
+              {
+                "@DATA:TEMPLATE@": "SLACK_ROOT_SINGLE_SUITE_FAILURE"
+              },
+              {
+                "type": "section",
+                "text": {
+                  "type": "mrkdwn",
+                  "text": "<!subteam^ULA15K66M>"
+                }
+              }
+            ]
+          }
+        ]
+      }
+    },
+    response: {
+      status: 200
+    }
+  }
+});
+
+addInteractionHandler('post test-summary with mentions special group name to slack', () => {
+  return {
+    request: {
+      method: 'POST',
+      path: '/message',
+      body: {
+        "attachments": [
+          {
+            "color": "#DC143C",
+            "blocks": [
+              {
+                "@DATA:TEMPLATE@": "SLACK_ROOT_SINGLE_SUITE_FAILURE"
+              },
+              {
+                "type": "section",
+                "text": {
+                  "type": "mrkdwn",
+                  "text": "<!here>"
+                }
+              }
+            ]
+          }
+        ]
+      }
+    },
+    response: {
+      status: 200
+    }
+  }
+});
+
 addInteractionHandler('post test-summary to slack with qc-test-summary', (ctx) => {
   return {
     request: {