From ec96ff65b0e5f3912ceaa2d80284a19077fafa4f Mon Sep 17 00:00:00 2001 From: Geoffrey Testelin Date: Sun, 28 Feb 2021 12:15:08 +0100 Subject: [PATCH] feat(assignees): add 6 new options to avoid stale for assignees (#327) * feat(assignees): add new option to avoid stale for assignees closes #271 * test: add more coverage * docs: fix readme format issue * docs: reorder and enhance typo * docs(contributing): add more information about the npm scripts --- CONTRIBUTING.md | 26 + README.md | 55 +- __tests__/assignees.spec.ts | 413 ++++++++++++ .../constants/default-processor-options.ts | 8 +- __tests__/functions/generate-iissue.ts | 19 + __tests__/functions/generate-issue.ts | 13 +- __tests__/milestones.spec.ts | 1 - action.yml | 24 + dist/index.js | 273 ++++++-- package-lock.json | 61 ++ package.json | 3 + src/classes/assignees.spec.ts | 594 ++++++++++++++++++ src/classes/assignees.ts | 251 ++++++++ src/classes/issue.spec.ts | 126 +++- src/classes/issue.ts | 19 +- src/classes/issues-processor.ts | 99 ++- src/classes/loggers/issue-logger.spec.ts | 117 +++- src/classes/loggers/issue-logger.ts | 39 +- src/classes/milestones.spec.ts | 49 +- src/classes/milestones.ts | 4 +- src/interfaces/assignee.ts | 3 + src/interfaces/issue.ts | 2 + src/interfaces/issues-processor-options.ts | 6 + src/main.ts | 8 +- 24 files changed, 1995 insertions(+), 218 deletions(-) create mode 100644 __tests__/assignees.spec.ts create mode 100644 __tests__/functions/generate-iissue.ts create mode 100644 src/classes/assignees.spec.ts create mode 100644 src/classes/assignees.ts create mode 100644 src/interfaces/assignee.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 34b5a69d6..bfe57d638 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,3 +17,29 @@ Run the tests :heavy_check_mark: ```bash $ npm test ``` + +Run the tests and display only the first failing tests :heavy_check_mark: + +```bash +$ npm test:only-errors +``` + +Run the tests with the watch mode :heavy_check_mark: + +```bash +$ npm test:watch +``` + +Run the linter and fix (almost) every issue for you :heavy_check_mark: + +```bash +$ npm lint:all:fix +``` + +### Before creating a PR + +Build, lint, package and test everything. + +```bash +$ npm all +``` diff --git a/README.md b/README.md index 5c1852e54..512ed4703 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,6 @@ Warns and then closes issues and PRs that have had no activity for a specified a | `close-pr-label` | Label to apply on the closing PR. | Optional | | `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | Optional | | `exempt-pr-labels` | Labels on the PR exempted from being marked as stale. | Optional | -| `exempt-milestones` | Milestones on an issue or a PR exempted from being marked as stale. | Optional | -| `exempt-issue-milestones` | Milestones on an issue exempted from being marked as stale (override `exempt-milestones`). | Optional | -| `exempt-pr-milestones` | Milestones on the PR exempted from being marked as stale (override `exempt-milestones`). | Optional | -| `exempt-all-milestones` | Exempt all issues and PRs with milestones from being marked as stale. (priority over `exempt-milestones` rules) | Optional | -| `exempt-all-issue-milestones` | Exempt all issues with milestones from being marked as stale. (override `exempt-all-milestones`). | Optional | -| `exempt-all-pr-milestones` | Exempt all PRs with milestones from being marked as stale. (override `exempt-all-milestones`). | Optional | | `only-labels` | Only labels checked for stale issue/PR. | Optional | | `operations-per-run` | Maximum number of operations per run (GitHub API CRUD related). _Defaults to **30**_ | Optional | | `remove-stale-when-updated` | Remove stale label from issue/PR on updates or comments. _Defaults to **true**_ | Optional | @@ -38,10 +32,22 @@ Warns and then closes issues and PRs that have had no activity for a specified a | `skip-stale-pr-message` | Skip adding stale message on stale PR. _Defaults to **false**_ | Optional | | `start-date` | The date used to skip the stale action on issue/PR created before it (ISO 8601 or RFC 2822). | Optional | | `delete-branch` | Delete the git branch after closing a stale pull request. _Defaults to **false**_ | Optional | +| `exempt-milestones` | Milestones on an issue or a PR exempted from being marked as stale. | Optional | +| `exempt-issue-milestones` | Milestones on an issue exempted from being marked as stale (override `exempt-milestones`). | Optional | +| `exempt-pr-milestones` | Milestones on the PR exempted from being marked as stale (override `exempt-milestones`). | Optional | +| `exempt-all-milestones` | Exempt all issues and PRs with milestones from being marked as stale. (priority over `exempt-milestones` rules) | Optional | +| `exempt-all-issue-milestones` | Exempt all issues with milestones from being marked as stale. (override `exempt-all-milestones`). | Optional | +| `exempt-all-pr-milestones` | Exempt all PRs with milestones from being marked as stale. (override `exempt-all-milestones`). | Optional | +| `exempt-assignees` | Assignees on an issue or a PR exempted from being marked as stale. | Optional | +| `exempt-issue-assignees` | Assignees on an issue exempted from being marked as stale (override `exempt-assignees`). | Optional | +| `exempt-pr-assignees` | Assignees on the PR exempted from being marked as stale (override `exempt-assignees`). | Optional | +| `exempt-all-assignees` | Exempt all issues and PRs with assignees from being marked as stale. (priority over `exempt-assignees` rules) | Optional | +| `exempt-all-issue-assignees` | Exempt all issues with assignees from being marked as stale. (override `exempt-all-assignees`). | Optional | +| `exempt-all-pr-assignees` | Exempt all PRs with assignees from being marked as stale. (override `exempt-all-assignees`). | Optional | ### Usage -See [action.yml](./action.yml) For comprehensive list of options. +See also [action.yml](./action.yml) for a comprehensive list of all the options. Basic: @@ -203,6 +209,41 @@ jobs: exempt-all-pr-milestones: true ``` +Avoid stale for specific assignees: + +```yaml +name: 'Close stale issues and PRs' +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + exempt-issue-assignees: 'marco,polo' + exempt-pr-assignees: 'marco' +``` + +Avoid stale for all PR with assignees: + +```yaml +name: 'Close stale issues and PRs' +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + exempt-all-pr-assignees: true +``` + ### Debugging To see the debug output from this action, you must set the secret `ACTIONS_STEP_DEBUG` to `true` in your repository. diff --git a/__tests__/assignees.spec.ts b/__tests__/assignees.spec.ts new file mode 100644 index 000000000..c60dac7a2 --- /dev/null +++ b/__tests__/assignees.spec.ts @@ -0,0 +1,413 @@ +import {Issue} from '../src/classes/issue'; +import {IssuesProcessor} from '../src/classes/issues-processor'; +import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options'; +import {DefaultProcessorOptions} from './constants/default-processor-options'; +import {generateIssue} from './functions/generate-issue'; + +interface ITestData { + id: number; + isPullRequest: boolean; + assignees: string[]; + exemptAllAssignees: boolean; + exemptAllIssueAssignees: boolean | undefined; + exemptAllPrAssignees: boolean | undefined; + exemptAssignees: string; + exemptIssueAssignees: string; + exemptPrAssignees: string; + shouldStale: boolean; + description: string; +} + +describe('assignees options', (): void => { + let opts: IIssuesProcessorOptions; + let testIssueList: Issue[]; + let processor: IssuesProcessor; + + const setTestIssueList = ( + isPullRequest: boolean, + assignees: string[], + id: number + ) => { + testIssueList = [ + generateIssue( + opts, + id, + 'My first issue', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + isPullRequest, + undefined, + undefined, + undefined, + undefined, + assignees + ) + ]; + }; + + const setProcessor = () => { + processor = new IssuesProcessor( + opts, + async () => 'abot', + async p => (p === 1 ? testIssueList : []), + async () => [], + async () => new Date().toDateString() + ); + }; + + beforeEach((): void => { + opts = {...DefaultProcessorOptions}; + }); + + describe.each` + id | isPullRequest | assignees | exemptAllAssignees | exemptAllIssueAssignees | exemptAllPrAssignees | exemptAssignees | exemptIssueAssignees | exemptPrAssignees | shouldStale | description + ${100} | ${false} | ${[]} | ${false} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee'} + ${101} | ${false} | ${[]} | ${true} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and only exemptAllAssignees is enabled'} + ${102} | ${false} | ${[]} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and only exemptAllIssueAssignees is enabled'} + ${103} | ${false} | ${[]} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and only exemptAllPrAssignees is enabled'} + ${104} | ${false} | ${[]} | ${true} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is disabled'} + ${105} | ${false} | ${[]} | ${true} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is enabled'} + ${106} | ${false} | ${[]} | ${true} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is disabled'} + ${107} | ${false} | ${[]} | ${true} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is enabled'} + ${108} | ${false} | ${[]} | ${false} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is disabled'} + ${109} | ${false} | ${[]} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is enabled'} + ${200} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'} + ${201} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is enabled'} + ${202} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and only exemptAllAssignees is enabled'} + ${203} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and only exemptAllIssueAssignees is enabled'} + ${204} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and only exemptAllPrAssignees is enabled'} + ${205} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is disabled'} + ${206} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is enabled'} + ${207} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is disabled'} + ${208} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is enabled'} + ${209} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is disabled'} + ${210} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is enabled'} + ${211} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'} + ${212} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is enabled'} + ${213} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'} + ${300} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'} + ${301} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'} + ${302} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAssignees has a different assignee'} + ${303} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'} + ${304} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'} + ${305} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees has a different assignee'} + ${306} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'} + ${307} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'} + ${308} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'} + ${309} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees has a different assignee'} + ${310} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'} + ${311} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'} + ${312} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'} + ${313} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'} + ${400} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'} + ${401} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'} + ${402} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAssignees has the same assignee'} + ${403} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'} + ${404} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'} + ${405} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees has the same assignee'} + ${406} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'} + ${407} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'} + ${408} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'} + ${409} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees has the same assignee'} + ${410} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'} + ${411} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'} + ${412} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'} + ${413} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'} + ${500} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${501} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${502} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${503} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${504} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${505} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${506} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${507} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${508} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${509} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${510} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${511} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${512} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${513} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${600} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${601} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${602} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${603} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${604} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${605} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${606} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${607} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${608} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${609} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${610} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${611} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${612} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${613} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${700} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${701} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${702} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${703} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${704} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${705} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${706} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${707} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${708} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${709} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${710} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${711} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${712} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${713} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${800} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${801} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${802} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${803} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${804} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${805} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${806} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${807} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${808} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${809} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${810} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${811} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${812} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${813} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${900} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${901} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${902} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${903} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${904} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${905} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${906} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${907} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${908} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${909} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${910} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${911} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${912} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${913} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${1000} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1001} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1002} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1003} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1004} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1005} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1006} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1007} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1008} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1009} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1010} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1011} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1012} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1013} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${1100} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1101} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1102} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1103} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1104} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1105} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1106} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1107} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1108} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1109} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1110} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1111} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1112} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1113} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${1200} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1201} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1202} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1203} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1204} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1205} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1206} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1207} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1208} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1209} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1210} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1211} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1212} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1213} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${1300} | ${true} | ${[]} | ${false} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee'} + ${1301} | ${true} | ${[]} | ${true} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and only exemptAllAssignees is enabled'} + ${1302} | ${true} | ${[]} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and only exemptAllIssueAssignees is enabled'} + ${1303} | ${true} | ${[]} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and only exemptAllPrAssignees is enabled'} + ${1304} | ${true} | ${[]} | ${true} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is disabled'} + ${1305} | ${true} | ${[]} | ${true} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is enabled'} + ${1306} | ${true} | ${[]} | ${true} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is disabled'} + ${1307} | ${true} | ${[]} | ${true} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is enabled'} + ${1308} | ${true} | ${[]} | ${false} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is disabled'} + ${1309} | ${true} | ${[]} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is enabled'} + ${1400} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'} + ${1401} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is enabled'} + ${1402} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and only exemptAllAssignees is enabled'} + ${1403} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and only exemptAllIssueAssignees is enabled'} + ${1404} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and only exemptAllPrAssignees is enabled'} + ${1405} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is disabled'} + ${1406} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is enabled'} + ${1407} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is disabled'} + ${1408} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is enabled'} + ${1409} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is disabled'} + ${1410} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is enabled'} + ${1411} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'} + ${1412} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is enabled'} + ${1413} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'} + ${1500} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'} + ${1501} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'} + ${1502} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAssignees has a different assignee'} + ${1503} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'} + ${1504} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'} + ${1505} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees has a different assignee'} + ${1506} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'} + ${1507} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'} + ${1508} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'} + ${1509} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees has a different assignee'} + ${1510} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'} + ${1511} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'} + ${1513} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'} + ${1600} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'} + ${1601} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'} + ${1602} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAssignees has the same assignee'} + ${1603} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'} + ${1604} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'} + ${1605} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees has the same assignee'} + ${1606} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'} + ${1607} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'} + ${1608} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'} + ${1609} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees has the same assignee'} + ${1610} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'} + ${1611} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'} + ${1612} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'} + ${1613} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'} + ${1701} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1702} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1703} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1704} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1705} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1706} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1707} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1708} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1709} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1710} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1711} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'} + ${1800} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1801} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1802} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1803} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1804} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1805} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1806} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1807} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1808} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1809} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1810} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'} + ${1900} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1901} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1902} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1903} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1904} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1905} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1906} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1907} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1908} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1909} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1910} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${1911} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'} + ${2000} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2001} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2002} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2003} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2004} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2005} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2006} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2007} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2008} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2009} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2010} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'} + ${2100} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2101} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2102} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2103} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2104} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2105} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2106} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2107} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2108} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2109} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2110} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'} + ${2200} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2201} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2202} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2203} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2204} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2205} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2206} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2207} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2208} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2209} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2210} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2311} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2312} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2313} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'} + ${2300} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2301} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2302} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2303} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2304} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2305} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2306} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2307} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2308} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2309} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2310} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2311} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'} + ${2400} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2401} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2402} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2403} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2404} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2405} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2406} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2407} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2408} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2409} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2410} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2411} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2412} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + ${2413} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'} + `( + '$description', + ({ + id, + isPullRequest, + assignees, + exemptAllAssignees, + exemptAllIssueAssignees, + exemptAllPrAssignees, + exemptAssignees, + exemptIssueAssignees, + exemptPrAssignees, + shouldStale + }: ITestData): void => { + beforeEach((): void => { + opts.exemptAllAssignees = exemptAllAssignees; + opts.exemptAllIssueAssignees = exemptAllIssueAssignees; + opts.exemptAllPrAssignees = exemptAllPrAssignees; + opts.exemptAssignees = exemptAssignees; + opts.exemptIssueAssignees = exemptIssueAssignees; + opts.exemptPrAssignees = exemptPrAssignees; + setTestIssueList(isPullRequest, assignees, id); + setProcessor(); + }); + + test(`should${ + shouldStale ? '' : ' not' + } be marked as stale`, async () => { + expect.assertions(3); + + await processor.processIssues(1); + + expect(processor.staleIssues).toHaveLength(shouldStale ? 1 : 0); + expect(processor.closedIssues).toHaveLength(0); + expect(processor.removedLabelIssues).toHaveLength(0); + }); + } + ); +}); diff --git a/__tests__/constants/default-processor-options.ts b/__tests__/constants/default-processor-options.ts index 941fac41f..23fe99b1b 100644 --- a/__tests__/constants/default-processor-options.ts +++ b/__tests__/constants/default-processor-options.ts @@ -32,5 +32,11 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({ exemptPrMilestones: '', exemptAllMilestones: false, exemptAllIssueMilestones: undefined, - exemptAllPrMilestones: undefined + exemptAllPrMilestones: undefined, + exemptAssignees: '', + exemptIssueAssignees: '', + exemptPrAssignees: '', + exemptAllAssignees: false, + exemptAllIssueAssignees: undefined, + exemptAllPrAssignees: undefined }); diff --git a/__tests__/functions/generate-iissue.ts b/__tests__/functions/generate-iissue.ts new file mode 100644 index 000000000..b3a8c5932 --- /dev/null +++ b/__tests__/functions/generate-iissue.ts @@ -0,0 +1,19 @@ +import {IIssue} from '../../src/interfaces/issue'; + +export function generateIIssue( + partialIssue?: Readonly> +): IIssue { + return { + milestone: undefined, + assignees: [], + labels: [], + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + number: Math.round(Math.random() * 5000), + pull_request: null, + title: 'dummy-title', + locked: false, + state: 'dummy-state', + ...partialIssue + }; +} diff --git a/__tests__/functions/generate-issue.ts b/__tests__/functions/generate-issue.ts index 90aabc04b..3b3fe433d 100644 --- a/__tests__/functions/generate-issue.ts +++ b/__tests__/functions/generate-issue.ts @@ -1,4 +1,5 @@ import {Issue} from '../../src/classes/issue'; +import {IAssignee} from '../../src/interfaces/assignee'; import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options'; import {IsoDateString} from '../../src/types/iso-date-string'; @@ -12,7 +13,8 @@ export function generateIssue( labels: string[] = [], isClosed = false, isLocked = false, - milestone: string | undefined = undefined + milestone: string | undefined = undefined, + assignees: string[] = [] ): Issue { return new Issue(options, { number: id, @@ -29,6 +31,13 @@ export function generateIssue( ? { title: milestone } - : undefined + : undefined, + assignees: assignees.map( + (assignee: Readonly): IAssignee => { + return { + login: assignee + }; + } + ) }); } diff --git a/__tests__/milestones.spec.ts b/__tests__/milestones.spec.ts index d19412d8c..faa7c9b3f 100644 --- a/__tests__/milestones.spec.ts +++ b/__tests__/milestones.spec.ts @@ -9,7 +9,6 @@ interface ITestData { milestone: string; name: string; shouldStale: boolean; - exemptAllMilestones: boolean; } describe('milestones options', (): void => { diff --git a/action.yml b/action.yml index 4847e7826..cbf8ade83 100644 --- a/action.yml +++ b/action.yml @@ -120,6 +120,30 @@ inputs: description: 'The date used to skip the stale action on issue/pull request created before it (ISO 8601 or RFC 2822).' default: '' required: false + exempt-assignees: + description: 'The assignees which exempt an issue or a pull request from being marked as stale. Separate multiple assignees with commas (eg. "user1,user2")' + default: '' + required: false + exempt-issue-assignees: + description: 'The assignees which exempt an issue from being marked as stale. Separate multiple assignees with commas (eg. "user1,user2"). Override "exempt-assignees" option regarding only the issues.' + default: '' + required: false + exempt-pr-assignees: + description: 'The assignees which exempt a pull request from being marked as stale. Separate multiple assignees with commas (eg. "user1,user2"). Override "exempt-assignees" option regarding only the pull requests.' + default: '' + required: false + exempt-all-assignees: + description: 'Exempt all issues and pull requests with assignees from being marked as stale. Default to false.' + default: 'false' + required: false + exempt-all-issue-assignees: + description: 'Exempt all issues with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the issues.' + default: '' + required: false + exempt-all-pr-assignees: + description: 'Exempt all pull requests with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the pull requests.' + default: '' + required: false runs: using: 'node12' main: 'dist/index.js' diff --git a/dist/index.js b/dist/index.js index ad4c58daf..9465aefc2 100644 --- a/dist/index.js +++ b/dist/index.js @@ -2,6 +2,146 @@ module.exports = /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ +/***/ 7236: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.Assignees = void 0; +const lodash_deburr_1 = __importDefault(__nccwpck_require__(1601)); +const words_to_list_1 = __nccwpck_require__(1883); +const issue_logger_1 = __nccwpck_require__(2984); +class Assignees { + constructor(options, issue) { + this._options = options; + this._issue = issue; + this._issueLogger = new issue_logger_1.IssueLogger(issue); + } + static _cleanAssignee(assignee) { + return lodash_deburr_1.default(assignee.toLowerCase()); + } + shouldExemptAssignees() { + if (!this._issue.hasAssignees) { + this._issueLogger.info('This $$type has no assignee'); + this._logSkip(); + return false; + } + if (this._shouldExemptAllAssignees()) { + this._issueLogger.info('Skipping $$type because it has an exempt assignee'); + return true; + } + const exemptAssignees = this._getExemptAssignees(); + if (exemptAssignees.length === 0) { + this._issueLogger.info(`No option was specified to skip the stale process for this $$type`); + this._logSkip(); + return false; + } + this._issueLogger.info(`Found ${exemptAssignees.length} assignee${exemptAssignees.length > 1 ? 's' : ''} on this $$type`); + const hasExemptAssignee = exemptAssignees.some((exemptAssignee) => this._hasAssignee(exemptAssignee)); + if (!hasExemptAssignee) { + this._issueLogger.info('No assignee on this $$type can exempt the stale process'); + this._logSkip(); + } + else { + this._issueLogger.info('Skipping this $$type because it has an exempt assignee'); + } + return hasExemptAssignee; + } + _getExemptAssignees() { + return this._issue.isPullRequest + ? this._getExemptPullRequestAssignees() + : this._getExemptIssueAssignees(); + } + _getExemptIssueAssignees() { + if (this._options.exemptIssueAssignees === '') { + this._issueLogger.info('The option "exemptIssueAssignees" is disabled. No specific assignee can skip the stale process for this $$type'); + if (this._options.exemptAssignees === '') { + this._issueLogger.info('The option "exemptAssignees" is disabled. No specific assignee can skip the stale process for this $$type'); + return []; + } + const exemptAssignees = words_to_list_1.wordsToList(this._options.exemptAssignees); + this._issueLogger.info(`The option "exemptAssignees" is set. ${exemptAssignees.length} assignee${exemptAssignees.length === 1 ? '' : 's'} can skip the stale process for this $$type`); + return exemptAssignees; + } + const exemptAssignees = words_to_list_1.wordsToList(this._options.exemptIssueAssignees); + this._issueLogger.info(`The option "exemptIssueAssignees" is set. ${exemptAssignees.length} assignee${exemptAssignees.length === 1 ? '' : 's'} can skip the stale process for this $$type`); + return exemptAssignees; + } + _getExemptPullRequestAssignees() { + if (this._options.exemptPrAssignees === '') { + this._issueLogger.info('The option "exemptPrAssignees" is disabled. No specific assignee can skip the stale process for this $$type'); + if (this._options.exemptAssignees === '') { + this._issueLogger.info('The option "exemptAssignees" is disabled. No specific assignee can skip the stale process for this $$type'); + return []; + } + const exemptAssignees = words_to_list_1.wordsToList(this._options.exemptAssignees); + this._issueLogger.info(`The option "exemptAssignees" is set. ${exemptAssignees.length} assignee${exemptAssignees.length === 1 ? '' : 's'} can skip the stale process for this $$type`); + return exemptAssignees; + } + const exemptAssignees = words_to_list_1.wordsToList(this._options.exemptPrAssignees); + this._issueLogger.info(`The option "exemptPrAssignees" is set. ${exemptAssignees.length} assignee${exemptAssignees.length === 1 ? '' : 's'} can skip the stale process for this $$type`); + return exemptAssignees; + } + _hasAssignee(assignee) { + const cleanAssignee = Assignees._cleanAssignee(assignee); + return this._issue.assignees.some((issueAssignee) => { + const isSameAssignee = cleanAssignee === Assignees._cleanAssignee(issueAssignee.login); + if (isSameAssignee) { + this._issueLogger.info(`@${issueAssignee.login} is assigned on this $$type and is an exempt assignee`); + } + return isSameAssignee; + }); + } + _shouldExemptAllAssignees() { + return this._issue.isPullRequest + ? this._shouldExemptAllPullRequestAssignees() + : this._shouldExemptAllIssueAssignees(); + } + _shouldExemptAllIssueAssignees() { + if (this._options.exemptAllIssueAssignees === true) { + this._issueLogger.info('The option "exemptAllIssueAssignees" is enabled. Any assignee on this $$type will skip the stale process'); + return true; + } + else if (this._options.exemptAllIssueAssignees === false) { + this._issueLogger.info('The option "exemptAllIssueAssignees" is disabled. Only some specific assignees on this $$type will skip the stale process'); + return false; + } + this._logExemptAllAssigneesOption(); + return this._options.exemptAllAssignees; + } + _shouldExemptAllPullRequestAssignees() { + if (this._options.exemptAllPrAssignees === true) { + this._issueLogger.info('The option "exemptAllPrAssignees" is enabled. Any assignee on this $$type will skip the stale process'); + return true; + } + else if (this._options.exemptAllPrAssignees === false) { + this._issueLogger.info('The option "exemptAllPrAssignees" is disabled. Only some specific assignees on this $$type will skip the stale process'); + return false; + } + this._logExemptAllAssigneesOption(); + return this._options.exemptAllAssignees; + } + _logExemptAllAssigneesOption() { + if (this._options.exemptAllAssignees) { + this._issueLogger.info('The option "exemptAllAssignees" is enabled. Any assignee on this $$type will skip the stale process'); + } + else { + this._issueLogger.info('The option "exemptAllAssignees" is disabled. Only some specific assignees on this $$type will skip the stale process'); + } + } + _logSkip() { + this._issueLogger.info('Skip the assignees checks'); + } +} +exports.Assignees = Assignees; + + +/***/ }), + /***/ 4783: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { @@ -23,10 +163,18 @@ class Issue { this.state = issue.state; this.locked = issue.locked; this.milestone = issue.milestone; - this.isPullRequest = is_pull_request_1.isPullRequest(this); - this.staleLabel = this._getStaleLabel(); + this.assignees = issue.assignees; this.isStale = is_labeled_1.isLabeled(this, this.staleLabel); } + get isPullRequest() { + return is_pull_request_1.isPullRequest(this); + } + get staleLabel() { + return this._getStaleLabel(); + } + get hasAssignees() { + return this.assignees.length > 0; + } _getStaleLabel() { return this.isPullRequest ? this._options.stalePrLabel @@ -63,6 +211,7 @@ const is_labeled_1 = __nccwpck_require__(6792); const is_pull_request_1 = __nccwpck_require__(5400); const should_mark_when_stale_1 = __nccwpck_require__(2461); const words_to_list_1 = __nccwpck_require__(1883); +const assignees_1 = __nccwpck_require__(7236); const issue_1 = __nccwpck_require__(4783); const issue_logger_1 = __nccwpck_require__(2984); const logger_1 = __nccwpck_require__(6212); @@ -115,7 +264,7 @@ class IssuesProcessor { } for (const issue of issues.values()) { const issueLogger = new issue_logger_1.IssueLogger(issue); - issueLogger.info(`Found issue: issue #${issue.number} last updated ${issue.updated_at} (is pr? ${issue.isPullRequest})`); + issueLogger.info(`Found this $$type last updated ${issue.updated_at}`); // calculate string based messages for this issue const staleMessage = issue.isPullRequest ? this.options.stalePrMessage @@ -136,23 +285,18 @@ class IssuesProcessor { const daysBeforeStale = issue.isPullRequest ? this._getDaysBeforePrStale() : this._getDaysBeforeIssueStale(); - if (issue.isPullRequest) { - issueLogger.info(`Days before pull request stale: ${daysBeforeStale}`); - } - else { - issueLogger.info(`Days before issue stale: ${daysBeforeStale}`); - } + issueLogger.info(`Days before $$type stale: ${daysBeforeStale}`); const shouldMarkAsStale = should_mark_when_stale_1.shouldMarkWhenStale(daysBeforeStale); if (!staleMessage && shouldMarkAsStale) { - issueLogger.info(`Skipping ${issueType} due to empty stale message`); + issueLogger.info(`Skipping $$type due to empty stale message`); continue; } if (issue.state === 'closed') { - issueLogger.info(`Skipping ${issueType} because it is closed`); + issueLogger.info(`Skipping $$type because it is closed`); continue; // don't process closed issues } if (issue.locked) { - issueLogger.info(`Skipping ${issueType} because it is locked`); + issueLogger.info(`Skipping $$type because it is locked`); continue; // don't process locked issues } if (this.options.startDate) { @@ -164,17 +308,17 @@ class IssuesProcessor { if (!is_valid_date_1.isValidDate(createdAt)) { throw new Error(`Invalid issue field: "created_at". Expected a valid date`); } - issueLogger.info(`Issue created the ${get_humanized_date_1.getHumanizedDate(createdAt)} (${issue.created_at})`); + issueLogger.info(`$$type created the ${get_humanized_date_1.getHumanizedDate(createdAt)} (${issue.created_at})`); if (!is_date_more_recent_than_1.isDateMoreRecentThan(createdAt, startDate)) { - issueLogger.info(`Skipping ${issueType} because it was created before the specified start date`); + issueLogger.info(`Skipping $$type because it was created before the specified start date`); continue; // don't process issues which were created before the start date } } if (issue.isStale) { - issueLogger.info(`This issue has a stale label`); + issueLogger.info(`This $$type has a stale label`); } else { - issueLogger.info(`This issue hasn't a stale label`); + issueLogger.info(`This $$type hasn't a stale label`); } const exemptLabels = words_to_list_1.wordsToList(issue.isPullRequest ? this.options.exemptPrLabels @@ -184,19 +328,23 @@ class IssuesProcessor { issueLogger.info(`An exempt label was added after the stale label.`); yield this._removeStaleLabel(issue, staleLabel); } - issueLogger.info(`Skipping ${issueType} because it has an exempt label`); + issueLogger.info(`Skipping $$type because it has an exempt label`); continue; // don't process exempt issues } const milestones = new milestones_1.Milestones(this.options, issue); if (milestones.shouldExemptMilestones()) { - issueLogger.info(`Skipping ${issueType} because it has an exempt milestone`); + issueLogger.info(`Skipping $$type because it has an exempted milestone`); continue; // don't process exempt milestones } + const assignees = new assignees_1.Assignees(this.options, issue); + if (assignees.shouldExemptAssignees()) { + continue; // don't process exempt assignees + } // should this issue be marked stale? const shouldBeStale = !IssuesProcessor._updatedSince(issue.updated_at, daysBeforeStale); // determine if this issue needs to be marked stale first if (!issue.isStale && shouldBeStale && shouldMarkAsStale) { - issueLogger.info(`Marking ${issueType} stale because it was last updated on ${issue.updated_at} and it does not have a stale label`); + issueLogger.info(`Marking $$type stale because it was last updated on ${issue.updated_at} and it does not have a stale label`); yield this._markStale(issue, staleMessage, staleLabel, skipMessage); issue.isStale = true; // this issue is now considered stale } @@ -205,7 +353,7 @@ class IssuesProcessor { } // process the issue if it was marked stale if (issue.isStale) { - issueLogger.info(`Found a stale ${issueType}`); + issueLogger.info(`Found a stale $$type`); yield this._processStaleIssue(issue, issueType, staleLabel, actor, closeMessage, closeLabel); } } @@ -222,21 +370,16 @@ class IssuesProcessor { return __awaiter(this, void 0, void 0, function* () { const issueLogger = new issue_logger_1.IssueLogger(issue); const markedStaleOn = (yield this._getLabelCreationDate(issue, staleLabel)) || issue.updated_at; - issueLogger.info(`Issue #${issue.number} marked stale on: ${markedStaleOn}`); + issueLogger.info(`$$type marked stale on: ${markedStaleOn}`); const issueHasComments = yield this._hasCommentsSince(issue, markedStaleOn, actor); - issueLogger.info(`Issue #${issue.number} has been commented on: ${issueHasComments}`); + issueLogger.info(`$$type has been commented on: ${issueHasComments}`); const isPr = is_pull_request_1.isPullRequest(issue); const daysBeforeClose = isPr ? this._getDaysBeforePrClose() : this._getDaysBeforeIssueClose(); - if (isPr) { - issueLogger.info(`Days before pull request close: ${daysBeforeClose}`); - } - else { - issueLogger.info(`Days before issue close: ${daysBeforeClose}`); - } + issueLogger.info(`Days before $$type close: ${daysBeforeClose}`); const issueHasUpdate = IssuesProcessor._updatedSince(issue.updated_at, daysBeforeClose); - issueLogger.info(`Issue #${issue.number} has been updated: ${issueHasUpdate}`); + issueLogger.info(`$$type has been updated: ${issueHasUpdate}`); // should we un-stale this issue? if (this.options.removeStaleWhenUpdated && issueHasComments) { yield this._removeStaleLabel(issue, staleLabel); @@ -246,16 +389,16 @@ class IssuesProcessor { return; // nothing to do because we aren't closing stale issues } if (!issueHasComments && !issueHasUpdate) { - issueLogger.info(`Closing ${issueType} because it was last updated on ${issue.updated_at}`); + issueLogger.info(`Closing $$type because it was last updated on ${issue.updated_at}`); yield this._closeIssue(issue, closeMessage, closeLabel); if (this.options.deleteBranch && issue.pull_request) { - issueLogger.info(`Deleting branch for #${issue.number} as delete-branch option was specified`); + issueLogger.info(`Deleting branch for as delete-branch option was specified`); yield this._deleteBranch(issue); this.deletedBranchIssues.push(issue); } } else { - issueLogger.info(`Stale ${issueType} is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})`); + issueLogger.info(`Stale $$type is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})`); } }); } @@ -263,7 +406,7 @@ class IssuesProcessor { _hasCommentsSince(issue, sinceDate, actor) { return __awaiter(this, void 0, void 0, function* () { const issueLogger = new issue_logger_1.IssueLogger(issue); - issueLogger.info(`Checking for comments on issue #${issue.number} since ${sinceDate}`); + issueLogger.info(`Checking for comments on $$type since ${sinceDate}`); if (!sinceDate) { return true; } @@ -334,7 +477,7 @@ class IssuesProcessor { _markStale(issue, staleMessage, staleLabel, skipMessage) { return __awaiter(this, void 0, void 0, function* () { const issueLogger = new issue_logger_1.IssueLogger(issue); - issueLogger.info(`Marking issue #${issue.number} as stale`); + issueLogger.info(`Marking $$type as stale`); this.staleIssues.push(issue); this._operationsLeft -= 2; // if the issue is being marked stale, the updated date should be changed to right now @@ -374,7 +517,7 @@ class IssuesProcessor { _closeIssue(issue, closeMessage, closeLabel) { return __awaiter(this, void 0, void 0, function* () { const issueLogger = new issue_logger_1.IssueLogger(issue); - issueLogger.info(`Closing issue #${issue.number} for being stale`); + issueLogger.info(`Closing $$type for being stale`); this.closedIssues.push(issue); this._operationsLeft -= 1; if (this.options.debugOnly) { @@ -415,7 +558,7 @@ class IssuesProcessor { }); } catch (error) { - issueLogger.error(`Error updating an issue: ${error.message}`); + issueLogger.error(`Error updating this $$type: ${error.message}`); } }); } @@ -432,7 +575,7 @@ class IssuesProcessor { return pullRequest.data; } catch (error) { - issueLogger.error(`Error getting pull request ${issue.number}: ${error.message}`); + issueLogger.error(`Error getting this $$type: ${error.message}`); } }); } @@ -440,17 +583,17 @@ class IssuesProcessor { _deleteBranch(issue) { return __awaiter(this, void 0, void 0, function* () { const issueLogger = new issue_logger_1.IssueLogger(issue); - issueLogger.info(`Delete branch from closed issue #${issue.number} - ${issue.title}`); + issueLogger.info(`Delete branch from closed $$type - ${issue.title}`); if (this.options.debugOnly) { return; } const pullRequest = yield this._getPullRequest(issue); if (!pullRequest) { - issueLogger.info(`Not deleting branch as pull request not found for issue ${issue.number}`); + issueLogger.info(`Not deleting branch as pull request not found for this $$type`); return; } const branch = pullRequest.head.ref; - issueLogger.info(`Deleting branch ${branch} from closed issue #${issue.number}`); + issueLogger.info(`Deleting branch ${branch} from closed $$type`); this._operationsLeft -= 1; try { yield this.client.git.deleteRef({ @@ -460,7 +603,7 @@ class IssuesProcessor { }); } catch (error) { - issueLogger.error(`Error deleting branch ${branch} from issue #${issue.number}: ${error.message}`); + issueLogger.error(`Error deleting branch ${branch} from $$type: ${error.message}`); } }); } @@ -468,7 +611,7 @@ class IssuesProcessor { _removeLabel(issue, label) { return __awaiter(this, void 0, void 0, function* () { const issueLogger = new issue_logger_1.IssueLogger(issue); - issueLogger.info(`Removing label "${label}" from issue #${issue.number}`); + issueLogger.info(`Removing label "${label}" from $$type`); this.removedLabelIssues.push(issue); this._operationsLeft -= 1; // @todo remove the debug only to be able to test the code below @@ -493,7 +636,7 @@ class IssuesProcessor { _getLabelCreationDate(issue, label) { return __awaiter(this, void 0, void 0, function* () { const issueLogger = new issue_logger_1.IssueLogger(issue); - issueLogger.info(`Checking for label on issue #${issue.number}`); + issueLogger.info(`Checking for label on $$type`); this._operationsLeft -= 1; const options = this.client.issues.listEvents.endpoint.merge({ owner: github_1.context.repo.owner, @@ -534,7 +677,7 @@ class IssuesProcessor { _removeStaleLabel(issue, staleLabel) { return __awaiter(this, void 0, void 0, function* () { const issueLogger = new issue_logger_1.IssueLogger(issue); - issueLogger.info(`Issue #${issue.number} is no longer stale. Removing stale label.`); + issueLogger.info(`$$type is no longer stale. Removing stale label.`); return this._removeLabel(issue, staleLabel); }); } @@ -571,18 +714,39 @@ var __importStar = (this && this.__importStar) || function (mod) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueLogger = void 0; const core = __importStar(__nccwpck_require__(2186)); +/** + * @description + * Each log will prefix the message with the issue number + * + * @example + * warning('No stale') => "[#123] No stale" + * + * Each log method can have special tokens: + * - $$type => will replace this by either "pull request" or "issue" depending of the type of issue + * + * @example + * warning('The $$type will stale') => "The pull request will stale" + */ class IssueLogger { constructor(issue) { this._issue = issue; } warning(message) { - core.warning(this._prefixWithIssueNumber(message)); + core.warning(this._format(message)); } info(message) { - core.info(this._prefixWithIssueNumber(message)); + core.info(this._format(message)); } error(message) { - core.error(this._prefixWithIssueNumber(message)); + core.error(this._format(message)); + } + _replaceTokens(message) { + return this._replaceTypeToken(message); + } + _replaceTypeToken(message) { + return message + .replace(/^\$\$type/, this._issue.isPullRequest ? 'Pull request' : 'Issue') + .replace(/\$\$type/g, this._issue.isPullRequest ? 'pull request' : 'issue'); } _prefixWithIssueNumber(message) { return `[#${this._getIssueNumber()}] ${message}`; @@ -590,6 +754,9 @@ class IssueLogger { _getIssueNumber() { return this._issue.number; } + _format(message) { + return this._prefixWithIssueNumber(this._replaceTokens(message)); + } } exports.IssueLogger = IssueLogger; @@ -656,8 +823,8 @@ class Milestones { this._options = options; this._issue = issue; } - static _cleanMilestone(label) { - return lodash_deburr_1.default(label.toLowerCase()); + static _cleanMilestone(milestone) { + return lodash_deburr_1.default(milestone.toLowerCase()); } shouldExemptMilestones() { if (this._shouldExemptAllMilestones()) { @@ -1002,7 +1169,13 @@ function _getAndValidateArgs() { exemptPrMilestones: core.getInput('exempt-pr-milestones'), exemptAllMilestones: core.getInput('exempt-all-milestones') === 'true', exemptAllIssueMilestones: _toOptionalBoolean('exempt-all-issue-milestones'), - exemptAllPrMilestones: _toOptionalBoolean('exempt-all-pr-milestones') + exemptAllPrMilestones: _toOptionalBoolean('exempt-all-pr-milestones'), + exemptAssignees: core.getInput('exempt-assignees'), + exemptIssueAssignees: core.getInput('exempt-issue-assignees'), + exemptPrAssignees: core.getInput('exempt-pr-assignees'), + exemptAllAssignees: core.getInput('exempt-all-assignees') === 'true', + exemptAllIssueAssignees: _toOptionalBoolean('exempt-all-issue-assignees'), + exemptAllPrAssignees: _toOptionalBoolean('exempt-all-pr-assignees') }; for (const numberInput of [ 'days-before-stale', diff --git a/package-lock.json b/package-lock.json index f3cce27dc..1a4162c61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7140,6 +7140,67 @@ } } }, + "jest-silent-reporter": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/jest-silent-reporter/-/jest-silent-reporter-0.4.0.tgz", + "integrity": "sha512-X9pLv87dOba/Ou6E3MrT0HmszG56oZSykgyhIlcL9SErjikPObprxAz58831g4Ngl8TKwpU73pAfkPpjYaMSxg==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-util": "^26.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "jest-snapshot": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", diff --git a/package.json b/package.json index 288ed67f8..40ffdca69 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "lint:all:fix": "npm run format && npm run lint:fix", "pack": "ncc build", "test": "jest", + "test:only-errors": "jest --reporters jest-silent-reporter --silent", + "test:watch": "jest --watch --notify --expandf", "all": "npm run build && npm run format && npm run lint && npm run pack && npm test" }, "repository": { @@ -47,6 +49,7 @@ "eslint-plugin-jest": "^24.1.5", "jest": "^26.6.3", "jest-circus": "^26.6.3", + "jest-silent-reporter": "^0.4.0", "js-yaml": "^4.0.0", "prettier": "^2.2.1", "ts-jest": "^26.5.1", diff --git a/src/classes/assignees.spec.ts b/src/classes/assignees.spec.ts new file mode 100644 index 000000000..26b689d15 --- /dev/null +++ b/src/classes/assignees.spec.ts @@ -0,0 +1,594 @@ +import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options'; +import {generateIIssue} from '../../__tests__/functions/generate-iissue'; +import {IIssue} from '../interfaces/issue'; +import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options'; +import {Issue} from './issue'; +import {Assignees} from './assignees'; + +describe('Assignees', (): void => { + let assignees: Assignees; + let optionsInterface: IIssuesProcessorOptions; + let issue: Issue; + let issueInterface: IIssue; + + beforeEach((): void => { + optionsInterface = {...DefaultProcessorOptions}; + issueInterface = generateIIssue(); + }); + + describe('shouldExemptAssignees()', (): void => { + describe('when the given issue is not a pull request', (): void => { + beforeEach((): void => { + issueInterface.pull_request = undefined; + }); + + describe('when the given options are not configured to exempt an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptAssignees = ''; + }); + + describe('when the given options are not configured to exempt an issue with an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptIssueAssignees = ''; + }); + + describe('when the given issue does not have an assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = []; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-login' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + }); + + describe('when the given options are configured to exempt an issue with an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptIssueAssignees = + 'dummy-exempt-issue-assignee'; + }); + + describe('when the given issue does not have an assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = []; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee different than the exempt issue assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-login' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee equaling the exempt issue assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-exempt-issue-assignee' + } + ]; + }); + + it('should return true', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(true); + }); + }); + }); + }); + + describe('when the given options are configured to exempt an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptAssignees = 'dummy-exempt-assignee'; + }); + + describe('when the given options are not configured to exempt an issue with an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptIssueAssignees = ''; + }); + + describe('when the given issue does not have an assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = []; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee different than the exempt assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-login' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee equaling the exempt assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-exempt-assignee' + } + ]; + }); + + it('should return true', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(true); + }); + }); + }); + + describe('when the given options are configured to exempt an issue with an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptIssueAssignees = + 'dummy-exempt-issue-assignee'; + }); + + describe('when the given issue does not have an assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = []; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee different than the exempt issue assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-login' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee equaling the exempt issue assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-exempt-issue-assignee' + } + ]; + }); + + it('should return true', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(true); + }); + }); + + describe('when the given issue does have an assignee different than the exempt assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-login' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee equaling the exempt assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-exempt-assignee' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + }); + }); + }); + + describe('when the given issue is a pull request', (): void => { + beforeEach((): void => { + issueInterface.pull_request = {}; + }); + + describe('when the given options are not configured to exempt an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptAssignees = ''; + }); + + describe('when the given options are not configured to exempt a pull request with an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptPrAssignees = ''; + }); + + describe('when the given issue does not have an assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = []; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-login' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + }); + + describe('when the given options are configured to exempt a pull request with an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptPrAssignees = 'dummy-exempt-pr-assignee'; + }); + + describe('when the given issue does not have an assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = []; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee different than the exempt pull request assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-login' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee equaling the exempt pull request assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-exempt-pr-assignee' + } + ]; + }); + + it('should return true', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(true); + }); + }); + }); + }); + + describe('when the given options are configured to exempt an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptAssignees = 'dummy-exempt-assignee'; + }); + + describe('when the given options are not configured to exempt a pull request with an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptPrAssignees = ''; + }); + + describe('when the given issue does not have an assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = []; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee different than the exempt assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-login' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee equaling the exempt assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-exempt-assignee' + } + ]; + }); + + it('should return true', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(true); + }); + }); + }); + + describe('when the given options are configured to exempt a pull request with an assignee', (): void => { + beforeEach((): void => { + optionsInterface.exemptPrAssignees = 'dummy-exempt-pr-assignee'; + }); + + describe('when the given issue does not have an assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = []; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee different than the exempt pull request assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-login' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee equaling the exempt pull request assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-exempt-pr-assignee' + } + ]; + }); + + it('should return true', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(true); + }); + }); + + describe('when the given issue does have an assignee different than the exempt assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-login' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given issue does have an assignee equaling the exempt assignee', (): void => { + beforeEach((): void => { + issueInterface.assignees = [ + { + login: 'dummy-exempt-assignee' + } + ]; + }); + + it('should return false', (): void => { + expect.assertions(1); + issue = new Issue(optionsInterface, issueInterface); + assignees = new Assignees(optionsInterface, issue); + + const result = assignees.shouldExemptAssignees(); + + expect(result).toStrictEqual(false); + }); + }); + }); + }); + }); + }); +}); diff --git a/src/classes/assignees.ts b/src/classes/assignees.ts new file mode 100644 index 000000000..af844dcdd --- /dev/null +++ b/src/classes/assignees.ts @@ -0,0 +1,251 @@ +import deburr from 'lodash.deburr'; +import {wordsToList} from '../functions/words-to-list'; +import {IAssignee} from '../interfaces/assignee'; +import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options'; +import {Issue} from './issue'; +import {IssueLogger} from './loggers/issue-logger'; + +type CleanAssignee = string; + +export class Assignees { + private static _cleanAssignee(assignee: Readonly): CleanAssignee { + return deburr(assignee.toLowerCase()); + } + + private readonly _options: IIssuesProcessorOptions; + private readonly _issue: Issue; + private readonly _issueLogger: IssueLogger; + + constructor(options: Readonly, issue: Issue) { + this._options = options; + this._issue = issue; + this._issueLogger = new IssueLogger(issue); + } + + shouldExemptAssignees(): boolean { + if (!this._issue.hasAssignees) { + this._issueLogger.info('This $$type has no assignee'); + this._logSkip(); + + return false; + } + + if (this._shouldExemptAllAssignees()) { + this._issueLogger.info( + 'Skipping $$type because it has an exempt assignee' + ); + + return true; + } + + const exemptAssignees: string[] = this._getExemptAssignees(); + + if (exemptAssignees.length === 0) { + this._issueLogger.info( + `No option was specified to skip the stale process for this $$type` + ); + this._logSkip(); + + return false; + } + + this._issueLogger.info( + `Found ${exemptAssignees.length} assignee${ + exemptAssignees.length > 1 ? 's' : '' + } on this $$type` + ); + + const hasExemptAssignee: boolean = exemptAssignees.some( + (exemptAssignee: Readonly): boolean => + this._hasAssignee(exemptAssignee) + ); + + if (!hasExemptAssignee) { + this._issueLogger.info( + 'No assignee on this $$type can exempt the stale process' + ); + this._logSkip(); + } else { + this._issueLogger.info( + 'Skipping this $$type because it has an exempt assignee' + ); + } + + return hasExemptAssignee; + } + + private _getExemptAssignees(): string[] { + return this._issue.isPullRequest + ? this._getExemptPullRequestAssignees() + : this._getExemptIssueAssignees(); + } + + private _getExemptIssueAssignees(): string[] { + if (this._options.exemptIssueAssignees === '') { + this._issueLogger.info( + 'The option "exemptIssueAssignees" is disabled. No specific assignee can skip the stale process for this $$type' + ); + + if (this._options.exemptAssignees === '') { + this._issueLogger.info( + 'The option "exemptAssignees" is disabled. No specific assignee can skip the stale process for this $$type' + ); + + return []; + } + + const exemptAssignees: string[] = wordsToList( + this._options.exemptAssignees + ); + + this._issueLogger.info( + `The option "exemptAssignees" is set. ${ + exemptAssignees.length + } assignee${ + exemptAssignees.length === 1 ? '' : 's' + } can skip the stale process for this $$type` + ); + + return exemptAssignees; + } + + const exemptAssignees: string[] = wordsToList( + this._options.exemptIssueAssignees + ); + + this._issueLogger.info( + `The option "exemptIssueAssignees" is set. ${ + exemptAssignees.length + } assignee${ + exemptAssignees.length === 1 ? '' : 's' + } can skip the stale process for this $$type` + ); + + return exemptAssignees; + } + + private _getExemptPullRequestAssignees(): string[] { + if (this._options.exemptPrAssignees === '') { + this._issueLogger.info( + 'The option "exemptPrAssignees" is disabled. No specific assignee can skip the stale process for this $$type' + ); + + if (this._options.exemptAssignees === '') { + this._issueLogger.info( + 'The option "exemptAssignees" is disabled. No specific assignee can skip the stale process for this $$type' + ); + + return []; + } + + const exemptAssignees: string[] = wordsToList( + this._options.exemptAssignees + ); + + this._issueLogger.info( + `The option "exemptAssignees" is set. ${ + exemptAssignees.length + } assignee${ + exemptAssignees.length === 1 ? '' : 's' + } can skip the stale process for this $$type` + ); + + return exemptAssignees; + } + + const exemptAssignees: string[] = wordsToList( + this._options.exemptPrAssignees + ); + + this._issueLogger.info( + `The option "exemptPrAssignees" is set. ${ + exemptAssignees.length + } assignee${ + exemptAssignees.length === 1 ? '' : 's' + } can skip the stale process for this $$type` + ); + + return exemptAssignees; + } + + private _hasAssignee(assignee: Readonly): boolean { + const cleanAssignee: CleanAssignee = Assignees._cleanAssignee(assignee); + + return this._issue.assignees.some( + (issueAssignee: Readonly): boolean => { + const isSameAssignee: boolean = + cleanAssignee === Assignees._cleanAssignee(issueAssignee.login); + + if (isSameAssignee) { + this._issueLogger.info( + `@${issueAssignee.login} is assigned on this $$type and is an exempt assignee` + ); + } + + return isSameAssignee; + } + ); + } + + private _shouldExemptAllAssignees(): boolean { + return this._issue.isPullRequest + ? this._shouldExemptAllPullRequestAssignees() + : this._shouldExemptAllIssueAssignees(); + } + + private _shouldExemptAllIssueAssignees(): boolean { + if (this._options.exemptAllIssueAssignees === true) { + this._issueLogger.info( + 'The option "exemptAllIssueAssignees" is enabled. Any assignee on this $$type will skip the stale process' + ); + + return true; + } else if (this._options.exemptAllIssueAssignees === false) { + this._issueLogger.info( + 'The option "exemptAllIssueAssignees" is disabled. Only some specific assignees on this $$type will skip the stale process' + ); + + return false; + } + + this._logExemptAllAssigneesOption(); + + return this._options.exemptAllAssignees; + } + + private _shouldExemptAllPullRequestAssignees(): boolean { + if (this._options.exemptAllPrAssignees === true) { + this._issueLogger.info( + 'The option "exemptAllPrAssignees" is enabled. Any assignee on this $$type will skip the stale process' + ); + + return true; + } else if (this._options.exemptAllPrAssignees === false) { + this._issueLogger.info( + 'The option "exemptAllPrAssignees" is disabled. Only some specific assignees on this $$type will skip the stale process' + ); + + return false; + } + + this._logExemptAllAssigneesOption(); + + return this._options.exemptAllAssignees; + } + + private _logExemptAllAssigneesOption(): void { + if (this._options.exemptAllAssignees) { + this._issueLogger.info( + 'The option "exemptAllAssignees" is enabled. Any assignee on this $$type will skip the stale process' + ); + } else { + this._issueLogger.info( + 'The option "exemptAllAssignees" is disabled. Only some specific assignees on this $$type will skip the stale process' + ); + } + } + + private _logSkip(): void { + this._issueLogger.info('Skip the assignees checks'); + } +} diff --git a/src/classes/issue.spec.ts b/src/classes/issue.spec.ts index 105c19af4..8cbd3d91c 100644 --- a/src/classes/issue.spec.ts +++ b/src/classes/issue.spec.ts @@ -1,3 +1,4 @@ +import {IAssignee} from '../interfaces/assignee'; import {IIssue} from '../interfaces/issue'; import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options'; import {ILabel} from '../interfaces/label'; @@ -25,10 +26,7 @@ describe('Issue', (): void => { debugOnly: false, deleteBranch: false, exemptIssueLabels: '', - exemptIssueMilestones: '', - exemptMilestones: '', exemptPrLabels: '', - exemptPrMilestones: '', onlyLabels: '', operationsPerRun: 0, removeStaleWhenUpdated: false, @@ -40,9 +38,18 @@ describe('Issue', (): void => { startDate: undefined, stalePrLabel: 'dummy-stale-pr-label', staleIssueLabel: 'dummy-stale-issue-label', + exemptMilestones: '', + exemptIssueMilestones: '', + exemptPrMilestones: '', exemptAllMilestones: false, exemptAllIssueMilestones: undefined, - exemptAllPrMilestones: undefined + exemptAllPrMilestones: undefined, + exemptAssignees: '', + exemptIssueAssignees: '', + exemptPrAssignees: '', + exemptAllAssignees: false, + exemptAllIssueAssignees: undefined, + exemptAllPrAssignees: undefined }; issueInterface = { title: 'dummy-title', @@ -59,7 +66,12 @@ describe('Issue', (): void => { locked: false, milestone: { title: 'dummy-milestone' - } + }, + assignees: [ + { + login: 'dummy-login' + } + ] }; issue = new Issue(optionsInterface, issueInterface); }); @@ -125,42 +137,94 @@ describe('Issue', (): void => { } as IMilestone); }); - describe('when the given issue pull_request is not set', (): void => { + it('should set the assignees with the given issue assignees', (): void => { + expect.assertions(1); + + expect(issue.assignees).toStrictEqual([ + { + login: 'dummy-login' + } as IAssignee + ]); + }); + + describe('when the given issue does not contains the stale label', (): void => { beforeEach((): void => { issueInterface.pull_request = undefined; + issueInterface.labels = []; issue = new Issue(optionsInterface, issueInterface); }); - it('should set the isPullRequest to false', (): void => { + it('should set the isStale to false', (): void => { expect.assertions(1); - expect(issue.isPullRequest).toStrictEqual(false); + expect(issue.isStale).toStrictEqual(false); }); }); - describe('when the given issue pull_request is set', (): void => { + describe('when the given issue contains the stale label', (): void => { + beforeEach((): void => { + issueInterface.pull_request = undefined; + issueInterface.labels = [ + { + name: 'dummy-stale-issue-label' + } as ILabel + ]; + issue = new Issue(optionsInterface, issueInterface); + }); + + it('should set the isStale to true', (): void => { + expect.assertions(1); + + expect(issue.isStale).toStrictEqual(true); + }); + }); + }); + + describe('get isPullRequest', (): void => { + describe('when the issue pull_request is not set', (): void => { + beforeEach((): void => { + issueInterface.pull_request = undefined; + issue = new Issue(optionsInterface, issueInterface); + }); + + it('should return false', (): void => { + expect.assertions(1); + + const result = issue.isPullRequest; + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the issue pull_request is set', (): void => { beforeEach((): void => { issueInterface.pull_request = {}; issue = new Issue(optionsInterface, issueInterface); }); - it('should set the isPullRequest to true', (): void => { + it('should return true', (): void => { expect.assertions(1); - expect(issue.isPullRequest).toStrictEqual(true); + const result = issue.isPullRequest; + + expect(result).toStrictEqual(true); }); }); + }); - describe('when the given issue is not a pull request', (): void => { + describe('get staleLabel', (): void => { + describe('when the issue is not a pull request', (): void => { beforeEach((): void => { issueInterface.pull_request = undefined; issue = new Issue(optionsInterface, issueInterface); }); - it('should set the staleLabel with the given option staleIssueLabel', (): void => { + it('should return the issue stale label', (): void => { expect.assertions(1); - expect(issue.staleLabel).toStrictEqual('dummy-stale-issue-label'); + const result = issue.staleLabel; + + expect(result).toStrictEqual('dummy-stale-issue-label'); }); }); @@ -170,42 +234,48 @@ describe('Issue', (): void => { issue = new Issue(optionsInterface, issueInterface); }); - it('should set the staleLabel with the given option stalePrLabel', (): void => { + it('should return the pull request stale label', (): void => { expect.assertions(1); - expect(issue.staleLabel).toStrictEqual('dummy-stale-pr-label'); + const result = issue.staleLabel; + + expect(result).toStrictEqual('dummy-stale-pr-label'); }); }); + }); - describe('when the given issue does not contains the stale label', (): void => { + describe('get hasAssignees', (): void => { + describe('when the issue has no assignee', (): void => { beforeEach((): void => { - issueInterface.pull_request = undefined; - issueInterface.labels = []; + issueInterface.assignees = []; issue = new Issue(optionsInterface, issueInterface); }); - it('should set the isStale to false', (): void => { + it('should return false', (): void => { expect.assertions(1); - expect(issue.isStale).toStrictEqual(false); + const result = issue.hasAssignees; + + expect(result).toStrictEqual(false); }); }); - describe('when the given issue contains the stale label', (): void => { + describe('when the issue has at least one assignee', (): void => { beforeEach((): void => { - issueInterface.pull_request = undefined; - issueInterface.labels = [ + issueInterface.assignees = [ { - name: 'dummy-stale-issue-label' - } as ILabel + login: 'dummy-login' + } ]; issue = new Issue(optionsInterface, issueInterface); }); - it('should set the isStale to true', (): void => { + it('should return true', (): void => { expect.assertions(1); - expect(issue.isStale).toStrictEqual(true); + const result = issue.hasAssignees; + + expect(result).toStrictEqual(true); }); }); }); diff --git a/src/classes/issue.ts b/src/classes/issue.ts index af6f825c1..816de1f83 100644 --- a/src/classes/issue.ts +++ b/src/classes/issue.ts @@ -1,5 +1,6 @@ import {isLabeled} from '../functions/is-labeled'; import {isPullRequest} from '../functions/is-pull-request'; +import {IAssignee} from '../interfaces/assignee'; import {IIssue} from '../interfaces/issue'; import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options'; import {ILabel} from '../interfaces/label'; @@ -17,8 +18,7 @@ export class Issue implements IIssue { readonly state: string; readonly locked: boolean; readonly milestone: IMilestone | undefined; - readonly isPullRequest: boolean; - readonly staleLabel: string; + readonly assignees: IAssignee[]; isStale: boolean; constructor( @@ -35,12 +35,23 @@ export class Issue implements IIssue { this.state = issue.state; this.locked = issue.locked; this.milestone = issue.milestone; + this.assignees = issue.assignees; - this.isPullRequest = isPullRequest(this); - this.staleLabel = this._getStaleLabel(); this.isStale = isLabeled(this, this.staleLabel); } + get isPullRequest(): boolean { + return isPullRequest(this); + } + + get staleLabel(): string { + return this._getStaleLabel(); + } + + get hasAssignees(): boolean { + return this.assignees.length > 0; + } + private _getStaleLabel(): string { return this.isPullRequest ? this._options.stalePrLabel diff --git a/src/classes/issues-processor.ts b/src/classes/issues-processor.ts index f6c86bdce..758950bd2 100644 --- a/src/classes/issues-processor.ts +++ b/src/classes/issues-processor.ts @@ -15,6 +15,7 @@ import {IIssue} from '../interfaces/issue'; import {IIssueEvent} from '../interfaces/issue-event'; import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options'; import {IPullRequest} from '../interfaces/pull-request'; +import {Assignees} from './assignees'; import {Issue} from './issue'; import {IssueLogger} from './loggers/issue-logger'; import {Logger} from './loggers/logger'; @@ -97,9 +98,7 @@ export class IssuesProcessor { for (const issue of issues.values()) { const issueLogger: IssueLogger = new IssueLogger(issue); - issueLogger.info( - `Found issue: issue #${issue.number} last updated ${issue.updated_at} (is pr? ${issue.isPullRequest})` - ); + issueLogger.info(`Found this $$type last updated ${issue.updated_at}`); // calculate string based messages for this issue const staleMessage: string = issue.isPullRequest @@ -122,26 +121,22 @@ export class IssuesProcessor { ? this._getDaysBeforePrStale() : this._getDaysBeforeIssueStale(); - if (issue.isPullRequest) { - issueLogger.info(`Days before pull request stale: ${daysBeforeStale}`); - } else { - issueLogger.info(`Days before issue stale: ${daysBeforeStale}`); - } + issueLogger.info(`Days before $$type stale: ${daysBeforeStale}`); const shouldMarkAsStale: boolean = shouldMarkWhenStale(daysBeforeStale); if (!staleMessage && shouldMarkAsStale) { - issueLogger.info(`Skipping ${issueType} due to empty stale message`); + issueLogger.info(`Skipping $$type due to empty stale message`); continue; } if (issue.state === 'closed') { - issueLogger.info(`Skipping ${issueType} because it is closed`); + issueLogger.info(`Skipping $$type because it is closed`); continue; // don't process closed issues } if (issue.locked) { - issueLogger.info(`Skipping ${issueType} because it is locked`); + issueLogger.info(`Skipping $$type because it is locked`); continue; // don't process locked issues } @@ -164,14 +159,14 @@ export class IssuesProcessor { } issueLogger.info( - `Issue created the ${getHumanizedDate(createdAt)} (${ + `$$type created the ${getHumanizedDate(createdAt)} (${ issue.created_at })` ); if (!isDateMoreRecentThan(createdAt, startDate)) { issueLogger.info( - `Skipping ${issueType} because it was created before the specified start date` + `Skipping $$type because it was created before the specified start date` ); continue; // don't process issues which were created before the start date @@ -179,9 +174,9 @@ export class IssuesProcessor { } if (issue.isStale) { - issueLogger.info(`This issue has a stale label`); + issueLogger.info(`This $$type has a stale label`); } else { - issueLogger.info(`This issue hasn't a stale label`); + issueLogger.info(`This $$type hasn't a stale label`); } const exemptLabels: string[] = wordsToList( @@ -200,9 +195,7 @@ export class IssuesProcessor { await this._removeStaleLabel(issue, staleLabel); } - issueLogger.info( - `Skipping ${issueType} because it has an exempt label` - ); + issueLogger.info(`Skipping $$type because it has an exempt label`); continue; // don't process exempt issues } @@ -210,11 +203,17 @@ export class IssuesProcessor { if (milestones.shouldExemptMilestones()) { issueLogger.info( - `Skipping ${issueType} because it has an exempt milestone` + `Skipping $$type because it has an exempted milestone` ); continue; // don't process exempt milestones } + const assignees: Assignees = new Assignees(this.options, issue); + + if (assignees.shouldExemptAssignees()) { + continue; // don't process exempt assignees + } + // should this issue be marked stale? const shouldBeStale = !IssuesProcessor._updatedSince( issue.updated_at, @@ -224,7 +223,7 @@ export class IssuesProcessor { // determine if this issue needs to be marked stale first if (!issue.isStale && shouldBeStale && shouldMarkAsStale) { issueLogger.info( - `Marking ${issueType} stale because it was last updated on ${issue.updated_at} and it does not have a stale label` + `Marking $$type stale because it was last updated on ${issue.updated_at} and it does not have a stale label` ); await this._markStale(issue, staleMessage, staleLabel, skipMessage); issue.isStale = true; // this issue is now considered stale @@ -236,7 +235,7 @@ export class IssuesProcessor { // process the issue if it was marked stale if (issue.isStale) { - issueLogger.info(`Found a stale ${issueType}`); + issueLogger.info(`Found a stale $$type`); await this._processStaleIssue( issue, issueType, @@ -271,37 +270,27 @@ export class IssuesProcessor { const issueLogger: IssueLogger = new IssueLogger(issue); const markedStaleOn: string = (await this._getLabelCreationDate(issue, staleLabel)) || issue.updated_at; - issueLogger.info( - `Issue #${issue.number} marked stale on: ${markedStaleOn}` - ); + issueLogger.info(`$$type marked stale on: ${markedStaleOn}`); const issueHasComments: boolean = await this._hasCommentsSince( issue, markedStaleOn, actor ); - issueLogger.info( - `Issue #${issue.number} has been commented on: ${issueHasComments}` - ); + issueLogger.info(`$$type has been commented on: ${issueHasComments}`); const isPr: boolean = isPullRequest(issue); const daysBeforeClose: number = isPr ? this._getDaysBeforePrClose() : this._getDaysBeforeIssueClose(); - if (isPr) { - issueLogger.info(`Days before pull request close: ${daysBeforeClose}`); - } else { - issueLogger.info(`Days before issue close: ${daysBeforeClose}`); - } + issueLogger.info(`Days before $$type close: ${daysBeforeClose}`); const issueHasUpdate: boolean = IssuesProcessor._updatedSince( issue.updated_at, daysBeforeClose ); - issueLogger.info( - `Issue #${issue.number} has been updated: ${issueHasUpdate}` - ); + issueLogger.info(`$$type has been updated: ${issueHasUpdate}`); // should we un-stale this issue? if (this.options.removeStaleWhenUpdated && issueHasComments) { @@ -315,20 +304,20 @@ export class IssuesProcessor { if (!issueHasComments && !issueHasUpdate) { issueLogger.info( - `Closing ${issueType} because it was last updated on ${issue.updated_at}` + `Closing $$type because it was last updated on ${issue.updated_at}` ); await this._closeIssue(issue, closeMessage, closeLabel); if (this.options.deleteBranch && issue.pull_request) { issueLogger.info( - `Deleting branch for #${issue.number} as delete-branch option was specified` + `Deleting branch for as delete-branch option was specified` ); await this._deleteBranch(issue); this.deletedBranchIssues.push(issue); } } else { issueLogger.info( - `Stale ${issueType} is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})` + `Stale $$type is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})` ); } } @@ -341,9 +330,7 @@ export class IssuesProcessor { ): Promise { const issueLogger: IssueLogger = new IssueLogger(issue); - issueLogger.info( - `Checking for comments on issue #${issue.number} since ${sinceDate}` - ); + issueLogger.info(`Checking for comments on $$type since ${sinceDate}`); if (!sinceDate) { return true; @@ -433,7 +420,7 @@ export class IssuesProcessor { ): Promise { const issueLogger: IssueLogger = new IssueLogger(issue); - issueLogger.info(`Marking issue #${issue.number} as stale`); + issueLogger.info(`Marking $$type as stale`); this.staleIssues.push(issue); @@ -481,7 +468,7 @@ export class IssuesProcessor { ): Promise { const issueLogger: IssueLogger = new IssueLogger(issue); - issueLogger.info(`Closing issue #${issue.number} for being stale`); + issueLogger.info(`Closing $$type for being stale`); this.closedIssues.push(issue); @@ -525,7 +512,7 @@ export class IssuesProcessor { state: 'closed' }); } catch (error) { - issueLogger.error(`Error updating an issue: ${error.message}`); + issueLogger.error(`Error updating this $$type: ${error.message}`); } } @@ -544,9 +531,7 @@ export class IssuesProcessor { return pullRequest.data; } catch (error) { - issueLogger.error( - `Error getting pull request ${issue.number}: ${error.message}` - ); + issueLogger.error(`Error getting this $$type: ${error.message}`); } } @@ -554,9 +539,7 @@ export class IssuesProcessor { private async _deleteBranch(issue: Issue): Promise { const issueLogger: IssueLogger = new IssueLogger(issue); - issueLogger.info( - `Delete branch from closed issue #${issue.number} - ${issue.title}` - ); + issueLogger.info(`Delete branch from closed $$type - ${issue.title}`); if (this.options.debugOnly) { return; @@ -566,15 +549,13 @@ export class IssuesProcessor { if (!pullRequest) { issueLogger.info( - `Not deleting branch as pull request not found for issue ${issue.number}` + `Not deleting branch as pull request not found for this $$type` ); return; } const branch = pullRequest.head.ref; - issueLogger.info( - `Deleting branch ${branch} from closed issue #${issue.number}` - ); + issueLogger.info(`Deleting branch ${branch} from closed $$type`); this._operationsLeft -= 1; @@ -586,7 +567,7 @@ export class IssuesProcessor { }); } catch (error) { issueLogger.error( - `Error deleting branch ${branch} from issue #${issue.number}: ${error.message}` + `Error deleting branch ${branch} from $$type: ${error.message}` ); } } @@ -595,7 +576,7 @@ export class IssuesProcessor { private async _removeLabel(issue: Issue, label: string): Promise { const issueLogger: IssueLogger = new IssueLogger(issue); - issueLogger.info(`Removing label "${label}" from issue #${issue.number}`); + issueLogger.info(`Removing label "${label}" from $$type`); this.removedLabelIssues.push(issue); @@ -626,7 +607,7 @@ export class IssuesProcessor { ): Promise { const issueLogger: IssueLogger = new IssueLogger(issue); - issueLogger.info(`Checking for label on issue #${issue.number}`); + issueLogger.info(`Checking for label on $$type`); this._operationsLeft -= 1; @@ -682,9 +663,7 @@ export class IssuesProcessor { ): Promise { const issueLogger: IssueLogger = new IssueLogger(issue); - issueLogger.info( - `Issue #${issue.number} is no longer stale. Removing stale label.` - ); + issueLogger.info(`$$type is no longer stale. Removing stale label.`); return this._removeLabel(issue, staleLabel); } diff --git a/src/classes/loggers/issue-logger.spec.ts b/src/classes/loggers/issue-logger.spec.ts index 27e7f90b0..2ff48350d 100644 --- a/src/classes/loggers/issue-logger.spec.ts +++ b/src/classes/loggers/issue-logger.spec.ts @@ -1,3 +1,5 @@ +import {DefaultProcessorOptions} from '../../../__tests__/constants/default-processor-options'; +import {generateIIssue} from '../../../__tests__/functions/generate-iissue'; import {Issue} from '../issue'; import {IssueLogger} from './issue-logger'; import * as core from '@actions/core'; @@ -5,21 +7,20 @@ import * as core from '@actions/core'; describe('IssueLogger', (): void => { let issue: Issue; let issueLogger: IssueLogger; + let message: string; - beforeEach((): void => { - issue = { - number: 8 - } as Issue; - issueLogger = new IssueLogger(issue); - }); + let coreWarningSpy: jest.SpyInstance; describe('warning()', (): void => { - let message: string; - - let coreWarningSpy: jest.SpyInstance; - beforeEach((): void => { message = 'dummy-message'; + issue = new Issue( + DefaultProcessorOptions, + generateIIssue({ + number: 8 + }) + ); + issueLogger = new IssueLogger(issue); coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation(); }); @@ -35,12 +36,17 @@ describe('IssueLogger', (): void => { }); describe('info()', (): void => { - let message: string; - let coreInfoSpy: jest.SpyInstance; beforeEach((): void => { message = 'dummy-message'; + issue = new Issue( + DefaultProcessorOptions, + generateIIssue({ + number: 8 + }) + ); + issueLogger = new IssueLogger(issue); coreInfoSpy = jest.spyOn(core, 'info').mockImplementation(); }); @@ -56,12 +62,17 @@ describe('IssueLogger', (): void => { }); describe('error()', (): void => { - let message: string; - let coreErrorSpy: jest.SpyInstance; beforeEach((): void => { message = 'dummy-message'; + issue = new Issue( + DefaultProcessorOptions, + generateIIssue({ + number: 8 + }) + ); + issueLogger = new IssueLogger(issue); coreErrorSpy = jest.spyOn(core, 'error').mockImplementation(); }); @@ -75,4 +86,82 @@ describe('IssueLogger', (): void => { expect(coreErrorSpy).toHaveBeenCalledWith('[#8] dummy-message'); }); }); + + it('should prefix the message with the issue number', (): void => { + expect.assertions(2); + message = 'dummy-message'; + issue = new Issue( + DefaultProcessorOptions, + generateIIssue({ + number: 123 + }) + ); + issueLogger = new IssueLogger(issue); + coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation(); + + issueLogger.warning(message); + + expect(coreWarningSpy).toHaveBeenCalledTimes(1); + expect(coreWarningSpy).toHaveBeenCalledWith('[#123] dummy-message'); + }); + + it.each` + pull_request | replacement + ${{key: 'value'}} | ${'pull request'} + ${{}} | ${'pull request'} + ${null} | ${'issue'} + ${undefined} | ${'issue'} + `( + 'should replace the special tokens "$$type" with the corresponding type', + ({pull_request, replacement}): void => { + expect.assertions(2); + message = 'The $$type will stale! $$type will soon be closed!'; + issue = new Issue( + DefaultProcessorOptions, + generateIIssue({ + number: 8, + pull_request + }) + ); + issueLogger = new IssueLogger(issue); + coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation(); + + issueLogger.warning(message); + + expect(coreWarningSpy).toHaveBeenCalledTimes(1); + expect(coreWarningSpy).toHaveBeenCalledWith( + `[#8] The ${replacement} will stale! ${replacement} will soon be closed!` + ); + } + ); + + it.each` + pull_request | replacement + ${{key: 'value'}} | ${'Pull request'} + ${{}} | ${'Pull request'} + ${null} | ${'Issue'} + ${undefined} | ${'Issue'} + `( + 'should replace the special token "$$type" with the corresponding type with first letter as uppercase', + ({pull_request, replacement}): void => { + expect.assertions(2); + message = '$$type will stale'; + issue = new Issue( + DefaultProcessorOptions, + generateIIssue({ + number: 8, + pull_request + }) + ); + issueLogger = new IssueLogger(issue); + coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation(); + + issueLogger.warning(message); + + expect(coreWarningSpy).toHaveBeenCalledTimes(1); + expect(coreWarningSpy).toHaveBeenCalledWith( + `[#8] ${replacement} will stale` + ); + } + ); }); diff --git a/src/classes/loggers/issue-logger.ts b/src/classes/loggers/issue-logger.ts index 979c231fa..ef4cd1a98 100644 --- a/src/classes/loggers/issue-logger.ts +++ b/src/classes/loggers/issue-logger.ts @@ -2,6 +2,19 @@ import * as core from '@actions/core'; import {Issue} from '../issue'; import {Logger} from './logger'; +/** + * @description + * Each log will prefix the message with the issue number + * + * @example + * warning('No stale') => "[#123] No stale" + * + * Each log method can have special tokens: + * - $$type => will replace this by either "pull request" or "issue" depending of the type of issue + * + * @example + * warning('The $$type will stale') => "The pull request will stale" + */ export class IssueLogger implements Logger { private readonly _issue: Issue; @@ -10,15 +23,31 @@ export class IssueLogger implements Logger { } warning(message: Readonly): void { - core.warning(this._prefixWithIssueNumber(message)); + core.warning(this._format(message)); } info(message: Readonly): void { - core.info(this._prefixWithIssueNumber(message)); + core.info(this._format(message)); } error(message: Readonly): void { - core.error(this._prefixWithIssueNumber(message)); + core.error(this._format(message)); + } + + private _replaceTokens(message: Readonly): string { + return this._replaceTypeToken(message); + } + + private _replaceTypeToken(message: Readonly): string { + return message + .replace( + /^\$\$type/, + this._issue.isPullRequest ? 'Pull request' : 'Issue' + ) + .replace( + /\$\$type/g, + this._issue.isPullRequest ? 'pull request' : 'issue' + ); } private _prefixWithIssueNumber(message: Readonly): string { @@ -28,4 +57,8 @@ export class IssueLogger implements Logger { private _getIssueNumber(): number { return this._issue.number; } + + private _format(message: Readonly): string { + return this._prefixWithIssueNumber(this._replaceTokens(message)); + } } diff --git a/src/classes/milestones.spec.ts b/src/classes/milestones.spec.ts index d010fb765..91b2744dd 100644 --- a/src/classes/milestones.spec.ts +++ b/src/classes/milestones.spec.ts @@ -1,3 +1,5 @@ +import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options'; +import {generateIIssue} from '../../__tests__/functions/generate-iissue'; import {IIssue} from '../interfaces/issue'; import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options'; import {Issue} from './issue'; @@ -10,51 +12,8 @@ describe('Milestones', (): void => { let issueInterface: IIssue; beforeEach((): void => { - optionsInterface = { - ascending: false, - closeIssueLabel: '', - closeIssueMessage: '', - closePrLabel: '', - closePrMessage: '', - daysBeforeClose: 0, - daysBeforeIssueClose: 0, - daysBeforeIssueStale: 0, - daysBeforePrClose: 0, - daysBeforePrStale: 0, - daysBeforeStale: 0, - debugOnly: false, - deleteBranch: false, - exemptIssueLabels: '', - exemptPrLabels: '', - onlyLabels: '', - operationsPerRun: 0, - removeStaleWhenUpdated: false, - repoToken: '', - skipStaleIssueMessage: false, - skipStalePrMessage: false, - staleIssueLabel: '', - staleIssueMessage: '', - stalePrLabel: '', - stalePrMessage: '', - startDate: undefined, - exemptIssueMilestones: '', - exemptPrMilestones: '', - exemptMilestones: '', - exemptAllMilestones: false, - exemptAllIssueMilestones: undefined, - exemptAllPrMilestones: undefined - }; - issueInterface = { - created_at: '', - locked: false, - milestone: undefined, - number: 0, - pull_request: undefined, - state: '', - title: '', - updated_at: '', - labels: [] - }; + optionsInterface = {...DefaultProcessorOptions}; + issueInterface = generateIIssue(); }); describe('shouldExemptMilestones()', (): void => { diff --git a/src/classes/milestones.ts b/src/classes/milestones.ts index eeb91393f..a891f223a 100644 --- a/src/classes/milestones.ts +++ b/src/classes/milestones.ts @@ -6,8 +6,8 @@ import {Issue} from './issue'; type CleanMilestone = string; export class Milestones { - private static _cleanMilestone(label: Readonly): CleanMilestone { - return deburr(label.toLowerCase()); + private static _cleanMilestone(milestone: Readonly): CleanMilestone { + return deburr(milestone.toLowerCase()); } private readonly _options: IIssuesProcessorOptions; diff --git a/src/interfaces/assignee.ts b/src/interfaces/assignee.ts new file mode 100644 index 000000000..4d5293e9e --- /dev/null +++ b/src/interfaces/assignee.ts @@ -0,0 +1,3 @@ +export interface IAssignee { + login: string; +} diff --git a/src/interfaces/issue.ts b/src/interfaces/issue.ts index 0df42eb1c..5060eb95c 100644 --- a/src/interfaces/issue.ts +++ b/src/interfaces/issue.ts @@ -1,4 +1,5 @@ import {IsoDateString} from '../types/iso-date-string'; +import {IAssignee} from './assignee'; import {ILabel} from './label'; import {IMilestone} from './milestone'; @@ -12,4 +13,5 @@ export interface IIssue { state: string; locked: boolean; milestone: IMilestone | undefined; + assignees: IAssignee[]; } diff --git a/src/interfaces/issues-processor-options.ts b/src/interfaces/issues-processor-options.ts index 8a74cab0f..bb95ebac9 100644 --- a/src/interfaces/issues-processor-options.ts +++ b/src/interfaces/issues-processor-options.ts @@ -33,4 +33,10 @@ export interface IIssuesProcessorOptions { exemptAllMilestones: boolean; exemptAllIssueMilestones: boolean | undefined; exemptAllPrMilestones: boolean | undefined; + exemptAssignees: string; + exemptIssueAssignees: string; + exemptPrAssignees: string; + exemptAllAssignees: boolean; + exemptAllIssueAssignees: boolean | undefined; + exemptAllPrAssignees: boolean | undefined; } diff --git a/src/main.ts b/src/main.ts index 7620c05fd..14e949c23 100644 --- a/src/main.ts +++ b/src/main.ts @@ -59,7 +59,13 @@ function _getAndValidateArgs(): IIssuesProcessorOptions { exemptPrMilestones: core.getInput('exempt-pr-milestones'), exemptAllMilestones: core.getInput('exempt-all-milestones') === 'true', exemptAllIssueMilestones: _toOptionalBoolean('exempt-all-issue-milestones'), - exemptAllPrMilestones: _toOptionalBoolean('exempt-all-pr-milestones') + exemptAllPrMilestones: _toOptionalBoolean('exempt-all-pr-milestones'), + exemptAssignees: core.getInput('exempt-assignees'), + exemptIssueAssignees: core.getInput('exempt-issue-assignees'), + exemptPrAssignees: core.getInput('exempt-pr-assignees'), + exemptAllAssignees: core.getInput('exempt-all-assignees') === 'true', + exemptAllIssueAssignees: _toOptionalBoolean('exempt-all-issue-assignees'), + exemptAllPrAssignees: _toOptionalBoolean('exempt-all-pr-assignees') }; for (const numberInput of [