feature: add dynamic messages support
This commit is contained in:
parent
65b52aff67
commit
c565207b1b
|
@ -14,7 +14,8 @@ export function generateIssue(
|
|||
isClosed = false,
|
||||
isLocked = false,
|
||||
milestone: string | undefined = undefined,
|
||||
assignees: string[] = []
|
||||
assignees: string[] = [],
|
||||
userLogin: string | undefined = undefined
|
||||
): Issue {
|
||||
return new Issue(options, {
|
||||
number: id,
|
||||
|
@ -37,6 +38,10 @@ export function generateIssue(
|
|||
login: assignee,
|
||||
type: 'User'
|
||||
};
|
||||
})
|
||||
}),
|
||||
user: {
|
||||
login: userLogin ? userLogin : 'dummy-test-user',
|
||||
type: 'User'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-option
|
|||
import {IssuesProcessorMock} from './classes/issues-processor-mock';
|
||||
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
||||
import {generateIssue} from './functions/generate-issue';
|
||||
import {isPullRequest} from '../src/functions/is-pull-request';
|
||||
|
||||
test('processing an issue with no label will make it stale and close it, if it is old enough only if days-before-close is set to 0', async () => {
|
||||
const opts: IIssuesProcessorOptions = {
|
||||
|
@ -2595,3 +2596,103 @@ test('processing an issue with the "includeOnlyAssigned" option set and no assig
|
|||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('interpolate stale message on prs when there is placeholder', async () => {
|
||||
const opts = {...DefaultProcessorOptions};
|
||||
opts.daysBeforeStale = 5; // stale after 5 days
|
||||
opts.daysBeforeClose = 20; // closes after 25 days
|
||||
opts.stalePrMessage = 'Hello {author}, Please take care of this pr!';
|
||||
const lastUpdate = new Date();
|
||||
lastUpdate.setDate(lastUpdate.getDate() - 10);
|
||||
const loginUser = 'dummy-user';
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
1,
|
||||
'An issue that should be marked stale but not closed.',
|
||||
lastUpdate.toString(),
|
||||
lastUpdate.toString(),
|
||||
true,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
[],
|
||||
loginUser
|
||||
)
|
||||
];
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
// for sake of testing, mocking private function
|
||||
const markSpy = jest.spyOn(processor as any, '_markStale');
|
||||
|
||||
await processor.processIssues(1);
|
||||
|
||||
// issue should be staled
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
|
||||
// comment should not be created
|
||||
expect(markSpy).toHaveBeenCalledWith(
|
||||
TestIssueList[0],
|
||||
'Hello @dummy-user, Please take care of this pr!',
|
||||
opts.stalePrLabel,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test('interpolate stale message on issues when there is placeholder', async () => {
|
||||
const opts = {...DefaultProcessorOptions};
|
||||
opts.daysBeforeStale = 5; // stale after 5 days
|
||||
opts.daysBeforeClose = 20; // closes after 25 days
|
||||
opts.staleIssueMessage = 'Hello {author}, Please take care of this issue!';
|
||||
const lastUpdate = new Date();
|
||||
lastUpdate.setDate(lastUpdate.getDate() - 10);
|
||||
const loginUser = 'dummy-user';
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
1,
|
||||
'An issue that should be marked stale but not closed',
|
||||
lastUpdate.toString(),
|
||||
lastUpdate.toString(),
|
||||
false,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
[],
|
||||
loginUser
|
||||
)
|
||||
];
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
// for sake of testing, mocking private function
|
||||
const markSpy = jest.spyOn(processor as any, '_markStale');
|
||||
|
||||
await processor.processIssues(1);
|
||||
|
||||
// issue should be staled
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
|
||||
// comment should be created
|
||||
expect(markSpy).toHaveBeenCalledWith(
|
||||
TestIssueList[0],
|
||||
'Hello @dummy-user, Please take care of this issue!',
|
||||
opts.staleIssueLabel,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@ import {ILabel} from '../interfaces/label';
|
|||
import {IMilestone} from '../interfaces/milestone';
|
||||
import {IsoDateString} from '../types/iso-date-string';
|
||||
import {Operations} from './operations';
|
||||
import {IUser} from '../interfaces/user';
|
||||
|
||||
export class Issue implements IIssue {
|
||||
readonly title: string;
|
||||
|
@ -23,6 +24,7 @@ export class Issue implements IIssue {
|
|||
markedStaleThisRun: boolean;
|
||||
operations = new Operations();
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
readonly user?: IUser | null;
|
||||
|
||||
constructor(
|
||||
options: Readonly<IIssuesProcessorOptions>,
|
||||
|
@ -41,6 +43,7 @@ export class Issue implements IIssue {
|
|||
this.assignees = issue.assignees || [];
|
||||
this.isStale = isLabeled(this, this.staleLabel);
|
||||
this.markedStaleThisRun = false;
|
||||
this.user = issue.user
|
||||
}
|
||||
|
||||
get isPullRequest(): boolean {
|
||||
|
|
|
@ -190,12 +190,18 @@ export class IssuesProcessor {
|
|||
);
|
||||
|
||||
// calculate string based messages for this issue
|
||||
const staleMessage: string = issue.isPullRequest
|
||||
? this.options.stalePrMessage
|
||||
: this.options.staleIssueMessage;
|
||||
const closeMessage: string = issue.isPullRequest
|
||||
? this.options.closePrMessage
|
||||
: this.options.closeIssueMessage;
|
||||
const staleMessage: string = this._interpolatePlaceholders(
|
||||
issue,
|
||||
issue.isPullRequest
|
||||
? this.options.stalePrMessage
|
||||
: this.options.staleIssueMessage
|
||||
);
|
||||
const closeMessage: string = this._interpolatePlaceholders(
|
||||
issue,
|
||||
issue.isPullRequest
|
||||
? this.options.closePrMessage
|
||||
: this.options.closeIssueMessage
|
||||
);
|
||||
const staleLabel: string = issue.isPullRequest
|
||||
? this.options.stalePrLabel
|
||||
: this.options.staleIssueLabel;
|
||||
|
@ -1227,4 +1233,10 @@ export class IssuesProcessor {
|
|||
|
||||
return Option.RemoveStaleWhenUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
private _interpolatePlaceholders(issue: Issue, message: string) {
|
||||
return issue.user
|
||||
? message.replace('{author}', `@${issue.user?.login}`)
|
||||
: message;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ import {Assignee} from './assignee';
|
|||
import {ILabel} from './label';
|
||||
import {IMilestone} from './milestone';
|
||||
import {components} from '@octokit/openapi-types';
|
||||
import {IUser} from './user';
|
||||
|
||||
export interface IIssue {
|
||||
title: string;
|
||||
number: number;
|
||||
|
@ -14,6 +16,7 @@ export interface IIssue {
|
|||
locked: boolean;
|
||||
milestone?: IMilestone | null;
|
||||
assignees?: Assignee[] | null;
|
||||
user?: IUser | null;
|
||||
}
|
||||
|
||||
export type OctokitIssue = components['schemas']['issue'];
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import {components} from '@octokit/openapi-types';
|
||||
|
||||
export interface IUser {
|
||||
type: string | 'User';
|
||||
login: string;
|
||||
}
|
||||
|
||||
export type OctokitUser = components['schemas']['nullable-simple-user'];
|
Loading…
Reference in New Issue