Repository: 8398a7/action-slack (ts)

Issue:
Matrix seems to confuse action-slack...
For a workflow using a matrix like:

build:
    strategy:
      matrix:
        os: [ubuntu-18.04, ubuntu-20.04]
    runs-on: ${{ matrix.os }}

I get the fields AS_JOB and AS_TOOK undefined. See attached screenshot. Without matrix it all works fine.

![Screen Shot 2020-06-29 at 10 05 20](https://user-images.githubusercontent.com/2265419/86017969-004faf80-ba25-11ea-9d40-e26682d7ed43.png)


Pull request:
Issue/86
close #86

Patch:
diff --git a/docs/content/fields.md b/docs/content/fields.md
index 43d4edf..8ebf457 100644
--- a/docs/content/fields.md
+++ b/docs/content/fields.md
@@ -4,6 +4,22 @@ metaTitle: Fields | action-slack
 metaDescription: This explains the values that can be specified in Fields.
 ---
 
+caution: Additional configuration is required to work with matrix.
+
+Don't forget to add `MATRIX_CONTEXT`.  
+Not required if the fields do not contain jobs or tooks.
+
+```yaml
+steps:
+  - uses: 8398a7/action-slack@v3
+    with:
+      fields: job,took
+    env:
+      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # required
+      SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required
+      MATRIX_CONTEXT: ${{ toJson(matrix) }} # required
+```
+
 If you have more than one, please enter it in csv format.  
 Corresponding types are as follows.
 
diff --git a/src/client.ts b/src/client.ts
index bfc2390..376fc6a 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -55,7 +55,21 @@ export class Client {
       throw new Error('Specify secrets.SLACK_WEBHOOK_URL');
     }
     this.webhook = new IncomingWebhook(webhookUrl);
-    this.fieldFactory = new FieldFactory(this.with.fields, this.github);
+    this.fieldFactory = new FieldFactory(
+      this.with.fields,
+      this.jobName,
+      this.github,
+    );
+  }
+
+  private get jobName() {
+    if (
+      process.env.MATRIX_CONTEXT == null ||
+      process.env.MATRIX_CONTEXT === 'null'
+    )
+      return context.job;
+    const os = JSON.parse(process.env.MATRIX_CONTEXT).os;
+    return os !== '' ? `${context.job} (${os})` : context.job;
   }
 
   async success(text: string) {
diff --git a/src/fields.ts b/src/fields.ts
index a56c96d..4f5257b 100644
--- a/src/fields.ts
+++ b/src/fields.ts
@@ -10,9 +10,11 @@ export interface Field {
 export class FieldFactory {
   private github?: GitHub;
   private fields: string[];
+  private jobName: string;
 
-  constructor(fields: string, github?: GitHub) {
+  constructor(fields: string, jobName: string, github?: GitHub) {
     this.fields = fields.replace(/ /g, '').split(',');
+    this.jobName = jobName;
     this.github = github;
   }
 
@@ -95,7 +97,7 @@ export class FieldFactory {
       repo: context.repo.repo,
       run_id: context.runId,
     });
-    const currentJob = resp?.data.jobs.find(job => job.name === context.job);
+    const currentJob = resp?.data.jobs.find(job => job.name === this.jobName);
     let time =
       new Date().getTime() - new Date(currentJob?.started_at ?? '').getTime();
     const h = Math.floor(time / (1000 * 60 * 60));
@@ -126,9 +128,9 @@ export class FieldFactory {
       repo: context.repo.repo,
       run_id: context.runId,
     });
-    const jobId = resp?.data.jobs.find(job => job.name === context.job)?.id;
+    const jobId = resp?.data.jobs.find(job => job.name === this.jobName)?.id;
 
-    const value = `<https://github.com/${owner}/${context.repo.repo}/runs/${jobId}|${context.job}>`;
+    const value = `<https://github.com/${owner}/${context.repo.repo}/runs/${jobId}|${this.jobName}>`;
     process.env.AS_JOB = value;
     return value;
   }


Test patch:
diff --git a/__tests__/client.test.ts b/__tests__/client.test.ts
index a3e1425..b75c253 100644
--- a/__tests__/client.test.ts
+++ b/__tests__/client.test.ts
@@ -1,6 +1,4 @@
 import nock from 'nock';
-import { readFileSync } from 'fs';
-import { resolve } from 'path';
 
 process.env.GITHUB_WORKFLOW = 'PR Checks';
 process.env.GITHUB_SHA = 'b24f03a32e093fe8d55e23cfd0bb314069633b2f';
@@ -10,6 +8,15 @@ process.env.GITHUB_TOKEN = 'test-token';
 process.env.GITHUB_RUN_ID = '1';
 process.env.GITHUB_JOB = 'notification';
 
+import {
+  setupNockCommit,
+  setupNockJobs,
+  successMsg,
+  failMsg,
+  cancelMsg,
+  getTemplate,
+  getApiFixture,
+} from './helper.test';
 import {
   Client,
   With,
@@ -17,154 +24,12 @@ import {
   Failure,
   Cancelled,
   Always,
-  Field,
-  GitHub,
 } from '../src/client';
-import { FieldFactory } from '../src/fields';
-
-const repo = (): Field => {
-  return {
-    short: true,
-    title: 'repo',
-    value: '<https://github.com/8398a7/action-slack|8398a7/action-slack>',
-  };
-};
-
-const message = (): Field => {
-  const obj: any = getApiFixture('repos.commits.get');
-  return {
-    short: true,
-    title: 'message',
-    value: `<${obj.html_url}|[#19] support for multiple user mentions>`,
-  };
-};
-
-const commit = (): Field => {
-  return {
-    short: true,
-    title: 'commit',
-    value: `<https://github.com/8398a7/action-slack/commit/${
-      process.env.GITHUB_SHA
-    }|${process.env.GITHUB_SHA?.slice(0, 8)}>`,
-  };
-};
-
-const author = (): Field => {
-  return { short: true, title: 'author', value: '839<8398a7@gmail.com>' };
-};
-
-const eventName = (): Field => {
-  return {
-    short: true,
-    title: 'eventName',
-    value: process.env.GITHUB_EVENT_NAME as string,
-  };
-};
-
-const ref = (): Field => {
-  return { short: true, title: 'ref', value: process.env.GITHUB_REF as string };
-};
-
-const workflow = (sha?: string): Field => {
-  return {
-    short: true,
-    title: 'workflow',
-    value: `<https://github.com/8398a7/action-slack/commit/${
-      sha ?? process.env.GITHUB_SHA
-    }/checks|${process.env.GITHUB_WORKFLOW as string}>`,
-  };
-};
-
-const action = (sha?: string): Field => {
-  return {
-    short: true,
-    title: 'action',
-    value: `<https://github.com/8398a7/action-slack/commit/${
-      sha ?? process.env.GITHUB_SHA
-    }/checks|action>`,
-  };
-};
-
-const job = (): Field => {
-  return {
-    short: true,
-    title: 'job',
-    value: `<https://github.com/8398a7/action-slack/runs/762195612|${process.env.GITHUB_JOB}>`,
-  };
-};
-
-const took = (): Field => {
-  return {
-    short: true,
-    title: 'took',
-    value: '1 hour 1 min 1 sec',
-  };
-};
-
-const fixedFields = (fields: string, sha?: string) => {
-  const ff = new FieldFactory(fields);
-  return ff.filterField(
-    [
-      ff.includes('repo') ? repo() : undefined,
-      ff.includes('message') ? message() : undefined,
-      ff.includes('commit') ? commit() : undefined,
-      ff.includes('author') ? author() : undefined,
-      ff.includes('action') ? action(sha) : undefined,
-      ff.includes('job') ? job() : undefined,
-      ff.includes('took') ? took() : undefined,
-      ff.includes('eventName') ? eventName() : undefined,
-      ff.includes('ref') ? ref() : undefined,
-      ff.includes('workflow') ? workflow(sha) : undefined,
-    ],
-    undefined,
-  );
-};
-
-const getTemplate: any = (fields: string, text: string, sha?: string) => {
-  return {
-    text,
-    attachments: [
-      {
-        author_name: '',
-        color: '',
-        fields: fixedFields(fields, sha),
-      },
-    ],
-    username: '',
-    icon_emoji: '',
-    icon_url: '',
-    channel: '',
-  };
-};
-
-const successMsg = ':white_check_mark: Succeeded GitHub Actions';
-const cancelMsg = ':warning: Canceled GitHub Actions';
-const failMsg = ':no_entry: Failed GitHub Actions';
-const getApiFixture = (name: string): any =>
-  JSON.parse(
-    readFileSync(resolve(__dirname, 'fixtures', `${name}.json`)).toString(),
-  );
 
 beforeAll(() => {
   nock.disableNetConnect();
-  nock('https://api.github.com')
-    .persist()
-    .get(`/repos/8398a7/action-slack/commits/${process.env.GITHUB_SHA}`)
-    .reply(200, () => getApiFixture('repos.commits.get'));
-  nock('https://api.github.com')
-    .persist()
-    .get(
-      `/repos/8398a7/action-slack/actions/runs/${process.env.GITHUB_RUN_ID}/jobs`,
-    )
-    .reply(200, () => {
-      const obj = getApiFixture('actions.runs.jobs');
-      const now = new Date();
-      now.setHours(now.getHours() - 1);
-      now.setMinutes(now.getMinutes() - 1);
-      now.setSeconds(now.getSeconds() - 1);
-      obj.jobs[0].started_at = now.toISOString();
-      return obj;
-    });
+  setupNockCommit(process.env.GITHUB_SHA as string);
+  setupNockJobs(process.env.GITHUB_RUN_ID as string, 'actions.runs.jobs');
 });
 afterAll(() => {
   nock.cleanAll();
@@ -194,7 +59,7 @@ describe('8398a7/action-slack', () => {
           'repo,message,commit,author,job,action,eventName,ref,workflow,took',
       };
       const client = new Client(withParams, process.env.GITHUB_TOKEN, '');
-      const payload = getTemplate(withParams.fields, `${successMsg}\n`);
+      const payload = getTemplate(withParams.fields, successMsg);
       payload.attachments[0].color = 'good';
       expect(await client.success('')).toStrictEqual(payload);
     });
@@ -214,7 +79,7 @@ describe('8398a7/action-slack', () => {
         fields: '',
       };
       const client = new Client(withParams, process.env.GITHUB_TOKEN, '');
-      const payload = getTemplate(withParams.fields, `${successMsg}\n`);
+      const payload = getTemplate(withParams.fields, successMsg);
       payload.attachments[0].color = 'good';
       expect(await client.success('')).toStrictEqual(payload);
     });
@@ -231,7 +96,7 @@ describe('8398a7/action-slack', () => {
         fields: '',
       };
       const client = new Client(withParams, process.env.GITHUB_TOKEN, '');
-      const payload = getTemplate(withParams.fields, `${failMsg}\n`);
+      const payload = getTemplate(withParams.fields, failMsg);
       payload.attachments[0].color = 'danger';
       expect(await client.fail('')).toStrictEqual(payload);
     });
@@ -248,7 +113,7 @@ describe('8398a7/action-slack', () => {
         fields: '',
       };
       const client = new Client(withParams, process.env.GITHUB_TOKEN, '');
-      const payload = getTemplate(withParams.fields, `${cancelMsg}\n`);
+      const payload = getTemplate(withParams.fields, cancelMsg);
       payload.attachments[0].color = 'warning';
       expect(await client.cancel('')).toStrictEqual(payload);
     });
@@ -613,7 +478,7 @@ describe('8398a7/action-slack', () => {
       fields: 'message,author',
     };
     const client = new Client(withParams, undefined, '');
-    const payload = getTemplate(withParams.fields, `${successMsg}\n`);
+    const payload = getTemplate(withParams.fields, successMsg);
     payload.attachments[0].color = 'good';
     payload.attachments[0].fields = payload.attachments[0].fields.filter(
       (field: any) => !['message', 'author'].includes(field.title),
diff --git a/__tests__/fixtures/actions.matrix-runs.jobs.json b/__tests__/fixtures/actions.matrix-runs.jobs.json
new file mode 100644
index 0000000..e46f889
--- /dev/null
+++ b/__tests__/fixtures/actions.matrix-runs.jobs.json
@@ -0,0 +1,30 @@
+{
+  "total_count": 1,
+  "jobs": [
+    {
+      "id": 762195612,
+      "run_id": 132308092,
+      "run_url": "https://api.github.com/repos/8398a7/action-slack/actions/runs/132308092",
+      "node_id": "MDg6Q2hlY2tSdW43NjIxOTU2MTI=",
+      "head_sha": "cd15a66fc57d465564195faeb5308a63b4317cde",
+      "url": "https://api.github.com/repos/8398a7/action-slack/actions/jobs/762195612",
+      "html_url": "https://github.com/8398a7/action-slack/runs/762195612",
+      "status": "in_progress",
+      "conclusion": null,
+      "started_at": "2020-06-11T15:05:49Z",
+      "completed_at": null,
+      "name": "notification (ubuntu-18.04)",
+      "steps": [
+        {
+          "name": "Set up job",
+          "status": "in_progress",
+          "conclusion": null,
+          "number": 1,
+          "started_at": "2020-06-11T15:05:49.000Z",
+          "completed_at": null
+        }
+      ],
+      "check_run_url": "https://api.github.com/repos/8398a7/action-slack/check-runs/762195612"
+    }
+  ]
+}
diff --git a/__tests__/helper.test.ts b/__tests__/helper.test.ts
new file mode 100644
index 0000000..838f756
--- /dev/null
+++ b/__tests__/helper.test.ts
@@ -0,0 +1,163 @@
+import nock from 'nock';
+import { readFileSync } from 'fs';
+import { resolve } from 'path';
+import { Field } from '../src/client';
+import { FieldFactory } from '../src/fields';
+
+process.env.GITHUB_WORKFLOW = 'PR Checks';
+process.env.GITHUB_SHA = 'b24f03a32e093fe8d55e23cfd0bb314069633b2f';
+process.env.GITHUB_REF = 'refs/heads/feature/19';
+process.env.GITHUB_EVENT_NAME = 'push';
+process.env.GITHUB_TOKEN = 'test-token';
+process.env.GITHUB_JOB = 'notification';
+
+export const repo = (): Field => {
+  return {
+    short: true,
+    title: 'repo',
+    value: '<https://github.com/8398a7/action-slack|8398a7/action-slack>',
+  };
+};
+
+export const message = (): Field => {
+  const obj: any = getApiFixture('repos.commits.get');
+  return {
+    short: true,
+    title: 'message',
+    value: `<${obj.html_url}|[#19] support for multiple user mentions>`,
+  };
+};
+
+export const commit = (): Field => {
+  return {
+    short: true,
+    title: 'commit',
+    value: `<https://github.com/8398a7/action-slack/commit/${
+      process.env.GITHUB_SHA
+    }|${process.env.GITHUB_SHA?.slice(0, 8)}>`,
+  };
+};
+
+export const author = (): Field => {
+  return { short: true, title: 'author', value: '839<8398a7@gmail.com>' };
+};
+
+export const eventName = (): Field => {
+  return {
+    short: true,
+    title: 'eventName',
+    value: process.env.GITHUB_EVENT_NAME as string,
+  };
+};
+
+export const ref = (): Field => {
+  return { short: true, title: 'ref', value: process.env.GITHUB_REF as string };
+};
+
+export const workflow = (sha?: string): Field => {
+  return {
+    short: true,
+    title: 'workflow',
+    value: `<https://github.com/8398a7/action-slack/commit/${
+      sha ?? process.env.GITHUB_SHA
+    }/checks|${process.env.GITHUB_WORKFLOW as string}>`,
+  };
+};
+
+export const action = (sha?: string): Field => {
+  return {
+    short: true,
+    title: 'action',
+    value: `<https://github.com/8398a7/action-slack/commit/${
+      sha ?? process.env.GITHUB_SHA
+    }/checks|action>`,
+  };
+};
+
+export const job = (): Field => {
+  return {
+    short: true,
+    title: 'job',
+    value: `<https://github.com/8398a7/action-slack/runs/762195612|${process.env.GITHUB_JOB}>`,
+  };
+};
+
+export const took = (): Field => {
+  return {
+    short: true,
+    title: 'took',
+    value: '1 hour 1 min 1 sec',
+  };
+};
+
+export const fixedFields = (fields: string, sha?: string) => {
+  const ff = new FieldFactory(fields, process.env.GITHUB_JOB as string);
+  return ff.filterField(
+    [
+      ff.includes('repo') ? repo() : undefined,
+      ff.includes('message') ? message() : undefined,
+      ff.includes('commit') ? commit() : undefined,
+      ff.includes('author') ? author() : undefined,
+      ff.includes('action') ? action(sha) : undefined,
+      ff.includes('job') ? job() : undefined,
+      ff.includes('took') ? took() : undefined,
+      ff.includes('eventName') ? eventName() : undefined,
+      ff.includes('ref') ? ref() : undefined,
+      ff.includes('workflow') ? workflow(sha) : undefined,
+    ],
+    undefined,
+  );
+};
+
+export const getTemplate: any = (
+  fields: string,
+  text: string,
+  sha?: string,
+) => {
+  return {
+    text,
+    attachments: [
+      {
+        author_name: '',
+        color: '',
+        fields: fixedFields(fields, sha),
+      },
+    ],
+    username: '',
+    icon_emoji: '',
+    icon_url: '',
+    channel: '',
+  };
+};
+
+export const setupNockCommit = (sha: string) =>
+  nock('https://api.github.com')
+    .persist()
+    .get(`/repos/8398a7/action-slack/commits/${sha}`)
+    .reply(200, () => getApiFixture('repos.commits.get'));
+
+export const setupNockJobs = (runId: string, fixture: string) =>
+  nock('https://api.github.com')
+    .persist()
+    .get(`/repos/8398a7/action-slack/actions/runs/${runId}/jobs`)
+    .reply(200, () => {
+      const obj = getApiFixture(fixture);
+      const now = new Date();
+      now.setHours(now.getHours() - 1);
+      now.setMinutes(now.getMinutes() - 1);
+      now.setSeconds(now.getSeconds() - 1);
+      obj.jobs[0].started_at = now.toISOString();
+      return obj;
+    });
+
+export const successMsg = ':white_check_mark: Succeeded GitHub Actions\n';
+export const cancelMsg = ':warning: Canceled GitHub Actions\n';
+export const failMsg = ':no_entry: Failed GitHub Actions\n';
+export const getApiFixture = (name: string): any =>
+  JSON.parse(
+    readFileSync(resolve(__dirname, 'fixtures', `${name}.json`)).toString(),
+  );
+
+describe('TODO - Add a test suite', () => {
+  it('TODO - Add a test', async () => {});
+});
diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts
deleted file mode 100644
index 48f15ff..0000000
--- a/__tests__/main.test.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-describe('TODO - Add a test suite', () => {
-  it('TODO - Add a test', async () => {});
-});
diff --git a/__tests__/matrix.test.ts b/__tests__/matrix.test.ts
new file mode 100644
index 0000000..c3b2492
--- /dev/null
+++ b/__tests__/matrix.test.ts
@@ -0,0 +1,77 @@
+import nock from 'nock';
+
+process.env.GITHUB_WORKFLOW = 'PR Checks';
+process.env.GITHUB_SHA = 'b24f03a32e093fe8d55e23cfd0bb314069633b2f';
+process.env.GITHUB_REF = 'refs/heads/feature/19';
+process.env.GITHUB_EVENT_NAME = 'push';
+process.env.GITHUB_TOKEN = 'test-token';
+process.env.GITHUB_RUN_ID = '2';
+process.env.GITHUB_JOB = 'notification';
+process.env.MATRIX_CONTEXT = '{"os": "ubuntu-18.04"}';
+
+import {
+  setupNockCommit,
+  setupNockJobs,
+  successMsg,
+  getTemplate,
+} from './helper.test';
+import { Client, With } from '../src/client';
+
+beforeAll(() => {
+  nock.disableNetConnect();
+  setupNockCommit(process.env.GITHUB_SHA as string);
+  setupNockJobs(
+    process.env.GITHUB_RUN_ID as string,
+    'actions.matrix-runs.jobs',
+  );
+});
+afterAll(() => {
+  nock.cleanAll();
+  nock.enableNetConnect();
+});
+
+describe('MATRIX_CONTEXT', () => {
+  beforeEach(() => {
+    process.env.GITHUB_REPOSITORY = '8398a7/action-slack';
+    process.env.GITHUB_EVENT_NAME = 'push';
+    const github = require('@actions/github');
+    github.context.payload = {};
+  });
+
+  it('runs in matrix', async () => {
+    const withParams: With = {
+      status: '',
+      mention: '',
+      author_name: '',
+      if_mention: '',
+      username: '',
+      icon_emoji: '',
+      icon_url: '',
+      channel: '',
+      fields: 'job,took',
+    };
+    const client = new Client(withParams, process.env.GITHUB_TOKEN, '');
+    expect(await client.success('')).toStrictEqual({
+      text: successMsg,
+      attachments: [
+        {
+          author_name: '',
+          color: 'good',
+          fields: [
+            {
+              short: true,
+              title: 'job',
+              value:
+                '<https://github.com/8398a7/action-slack/runs/762195612|notification (ubuntu-18.04)>',
+            },
+            { short: true, title: 'took', value: '1 hour 1 min 1 sec' },
+          ],
+        },
+      ],
+      username: '',
+      icon_emoji: '',
+      icon_url: '',
+      channel: '',
+    });
+  });
+});