refactor: update imports to use ES module syntax and improve test structure (#1350)

- Changed import statements across multiple files to use the .js extension for ES module compatibility.
- Refactored test files to utilize jest's unstable_mockModule for mocking core actions.
- Removed unnecessary spy instances in tests, replacing them with direct mocked function calls.
- Updated TypeScript configuration to target ES2022 and use NodeNext module resolution.
- Removed the tsconfig.spec.json file and adjusted the main tsconfig.json to exclude test files.
This commit is contained in:
Chiranjib Swain
2026-07-23 15:39:34 -05:00
committed by GitHub
parent 1e223db275
commit eaf9131fae
122 changed files with 67910 additions and 55239 deletions
+7 -6
View File
@@ -1,9 +1,10 @@
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 {Assignees} from './assignees';
import {Issue} from './issue';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options.js';
import {generateIIssue} from '../../__tests__/functions/generate-iissue.js';
import {IIssue} from '../interfaces/issue.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {Assignees} from './assignees.js';
import {Issue} from './issue.js';
describe('Assignees', (): void => {
let assignees: Assignees;
+7 -7
View File
@@ -1,11 +1,11 @@
import deburr from 'lodash.deburr';
import {Option} from '../enums/option';
import {wordsToList} from '../functions/words-to-list';
import {Assignee} from '../interfaces/assignee';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {Issue} from './issue';
import {IssueLogger} from './loggers/issue-logger';
import {LoggerService} from '../services/logger.service';
import {Option} from '../enums/option.js';
import {wordsToList} from '../functions/words-to-list.js';
import {Assignee} from '../interfaces/assignee.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {Issue} from './issue.js';
import {IssueLogger} from './loggers/issue-logger.js';
import {LoggerService} from '../services/logger.service.js';
type CleanAssignee = string;
+6 -6
View File
@@ -1,9 +1,9 @@
import {Option} from '../enums/option';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {IPullRequest} from '../interfaces/pull-request';
import {LoggerService} from '../services/logger.service';
import {Issue} from './issue';
import {IssueLogger} from './loggers/issue-logger';
import {Option} from '../enums/option.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {IPullRequest} from '../interfaces/pull-request.js';
import {LoggerService} from '../services/logger.service.js';
import {Issue} from './issue.js';
import {IssueLogger} from './loggers/issue-logger.js';
export class ExemptDraftPullRequest {
private readonly _options: IIssuesProcessorOptions;
+7 -6
View File
@@ -1,10 +1,11 @@
/* eslint jest/no-identical-title: "off" */
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 {IgnoreUpdates} from './ignore-updates';
import {Issue} from './issue';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options.js';
import {generateIIssue} from '../../__tests__/functions/generate-iissue.js';
import {IIssue} from '../interfaces/issue.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {IgnoreUpdates} from './ignore-updates.js';
import {Issue} from './issue.js';
describe('IgnoreUpdates', (): void => {
let ignoreUpdates: IgnoreUpdates;
+4 -4
View File
@@ -1,7 +1,7 @@
import {Option} from '../enums/option';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {Issue} from './issue';
import {IssueLogger} from './loggers/issue-logger';
import {Option} from '../enums/option.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {Issue} from './issue.js';
import {IssueLogger} from './loggers/issue-logger.js';
export class IgnoreUpdates {
private readonly _options: IIssuesProcessorOptions;
+7 -6
View File
@@ -1,9 +1,10 @@
import {IUserAssignee} from '../interfaces/assignee';
import {IIssue} from '../interfaces/issue';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {ILabel} from '../interfaces/label';
import {IMilestone} from '../interfaces/milestone';
import {Issue} from './issue';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {IUserAssignee} from '../interfaces/assignee.js';
import {IIssue} from '../interfaces/issue.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {ILabel} from '../interfaces/label.js';
import {IMilestone} from '../interfaces/milestone.js';
import {Issue} from './issue.js';
describe('Issue', (): void => {
let issue: Issue;
+9 -9
View File
@@ -1,12 +1,12 @@
import {isLabeled} from '../functions/is-labeled';
import {isPullRequest} from '../functions/is-pull-request';
import {Assignee} from '../interfaces/assignee';
import {IIssue, OctokitIssue} from '../interfaces/issue';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {ILabel} from '../interfaces/label';
import {IMilestone} from '../interfaces/milestone';
import {IsoDateString} from '../types/iso-date-string';
import {Operations} from './operations';
import {isLabeled} from '../functions/is-labeled.js';
import {isPullRequest} from '../functions/is-pull-request.js';
import {Assignee} from '../interfaces/assignee.js';
import {IIssue, OctokitIssue} from '../interfaces/issue.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {ILabel} from '../interfaces/label.js';
import {IMilestone} from '../interfaces/milestone.js';
import {IsoDateString} from '../types/iso-date-string.js';
import {Operations} from './operations.js';
export class Issue implements IIssue {
readonly title: string;
+35 -40
View File
@@ -1,35 +1,34 @@
import * as core from '@actions/core';
import {context, getOctokit} from '@actions/github';
import {GitHub} from '@actions/github/lib/utils';
import {Option} from '../enums/option';
import {getHumanizedDate} from '../functions/dates/get-humanized-date';
import {isDateMoreRecentThan} from '../functions/dates/is-date-more-recent-than';
import {isValidDate} from '../functions/dates/is-valid-date';
import {isBoolean} from '../functions/is-boolean';
import {isLabeled} from '../functions/is-labeled';
import {cleanLabel} from '../functions/clean-label';
import {shouldMarkWhenStale} from '../functions/should-mark-when-stale';
import {wordsToList} from '../functions/words-to-list';
import {IComment} from '../interfaces/comment';
import {IIssueEvent} from '../interfaces/issue-event';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {IPullRequest} from '../interfaces/pull-request';
import {Assignees} from './assignees';
import {IgnoreUpdates} from './ignore-updates';
import {ExemptDraftPullRequest} from './exempt-draft-pull-request';
import {Issue} from './issue';
import {IssueLogger} from './loggers/issue-logger';
import {Logger} from './loggers/logger';
import {Milestones} from './milestones';
import {StaleOperations} from './stale-operations';
import {Statistics} from './statistics';
import {LoggerService} from '../services/logger.service';
import {OctokitIssue} from '../interfaces/issue';
import {Option} from '../enums/option.js';
import {getHumanizedDate} from '../functions/dates/get-humanized-date.js';
import {isDateMoreRecentThan} from '../functions/dates/is-date-more-recent-than.js';
import {isValidDate} from '../functions/dates/is-valid-date.js';
import {isBoolean} from '../functions/is-boolean.js';
import {isLabeled} from '../functions/is-labeled.js';
import {cleanLabel} from '../functions/clean-label.js';
import {shouldMarkWhenStale} from '../functions/should-mark-when-stale.js';
import {wordsToList} from '../functions/words-to-list.js';
import {IComment} from '../interfaces/comment.js';
import {IIssueEvent} from '../interfaces/issue-event.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {IPullRequest} from '../interfaces/pull-request.js';
import {Assignees} from './assignees.js';
import {IgnoreUpdates} from './ignore-updates.js';
import {ExemptDraftPullRequest} from './exempt-draft-pull-request.js';
import {Issue} from './issue.js';
import {IssueLogger} from './loggers/issue-logger.js';
import {Logger} from './loggers/logger.js';
import {Milestones} from './milestones.js';
import {StaleOperations} from './stale-operations.js';
import {Statistics} from './statistics.js';
import {LoggerService} from '../services/logger.service.js';
import {OctokitIssue} from '../interfaces/issue.js';
import {retry} from '@octokit/plugin-retry';
import {IState} from '../interfaces/state/state';
import {IRateLimit} from '../interfaces/rate-limit';
import {RateLimit} from './rate-limit';
import {getSortField} from '../functions/get-sort-field';
import {IState} from '../interfaces/state/state.js';
import {IRateLimit} from '../interfaces/rate-limit.js';
import {RateLimit} from './rate-limit.js';
import {getSortField} from '../functions/get-sort-field.js';
/***
* Handle processing of issues for staleness/closure.
@@ -67,7 +66,7 @@ export class IssuesProcessor {
}
readonly operations: StaleOperations;
readonly client: InstanceType<typeof GitHub>;
readonly client: ReturnType<typeof getOctokit>;
readonly options: IIssuesProcessorOptions;
readonly staleIssues: Issue[] = [];
readonly closedIssues: Issue[] = [];
@@ -599,7 +598,9 @@ export class IssuesProcessor {
new Issue(this.options, issue as Readonly<OctokitIssue>)
);
} catch (error) {
throw Error(`Getting issues was blocked by the error: ${error.message}`);
throw Error(`Getting issues was blocked by the error: ${error.message}`, {
cause: error
});
}
}
@@ -1032,11 +1033,7 @@ export class IssuesProcessor {
issue_number: issue.number,
state: 'closed',
state_reason: (this.options.closeIssueReason || undefined) as
| 'completed'
| 'reopened'
| 'not_planned'
| null
| undefined
'completed' | 'reopened' | 'not_planned' | null | undefined
});
}
} catch (error) {
@@ -1352,16 +1349,14 @@ export class IssuesProcessor {
}
private _getDaysBeforeIssueStaleUsedOptionName():
| Option.DaysBeforeStale
| Option.DaysBeforeIssueStale {
Option.DaysBeforeStale | Option.DaysBeforeIssueStale {
return isNaN(this.options.daysBeforeIssueStale)
? Option.DaysBeforeStale
: Option.DaysBeforeIssueStale;
}
private _getDaysBeforePrStaleUsedOptionName():
| Option.DaysBeforeStale
| Option.DaysBeforePrStale {
Option.DaysBeforeStale | Option.DaysBeforePrStale {
return isNaN(this.options.daysBeforePrStale)
? Option.DaysBeforeStale
: Option.DaysBeforePrStale;
+39 -40
View File
@@ -1,16 +1,26 @@
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';
import {jest, beforeEach, describe, expect, it} from '@jest/globals';
import type {Issue as IssueType} from '../issue.js';
import type {IssueLogger as IssueLoggerType} from './issue-logger.js';
jest.unstable_mockModule('@actions/core', () => ({
warning: jest.fn(),
info: jest.fn(),
error: jest.fn()
}));
const {DefaultProcessorOptions} =
await import('../../../__tests__/constants/default-processor-options.js');
const {generateIIssue} =
await import('../../../__tests__/functions/generate-iissue.js');
const {Issue} = await import('../issue.js');
const {IssueLogger} = await import('./issue-logger.js');
const core = await import('@actions/core');
describe('IssueLogger', (): void => {
let issue: Issue;
let issueLogger: IssueLogger;
let issue: IssueType;
let issueLogger: IssueLoggerType;
let message: string;
let coreWarningSpy: jest.SpyInstance;
describe('warning()', (): void => {
beforeEach((): void => {
message = 'dummy-message';
@@ -21,8 +31,6 @@ describe('IssueLogger', (): void => {
})
);
issueLogger = new IssueLogger(issue);
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
});
it('should log a warning with the given message and with the issue number as prefix', (): void => {
@@ -30,19 +38,17 @@ describe('IssueLogger', (): void => {
issueLogger.warning(message);
expect(coreWarningSpy).toHaveBeenCalledTimes(1);
expect(coreWarningSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.warning)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.warning)).toHaveBeenCalledWith(
expect.stringContaining('[#8]')
);
expect(coreWarningSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.warning)).toHaveBeenCalledWith(
expect.stringContaining('dummy-message')
);
});
});
describe('info()', (): void => {
let coreInfoSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
issue = new Issue(
@@ -52,8 +58,6 @@ describe('IssueLogger', (): void => {
})
);
issueLogger = new IssueLogger(issue);
coreInfoSpy = jest.spyOn(core, 'info').mockImplementation();
});
it('should log an information with the given message and with the issue number as prefix', (): void => {
@@ -61,17 +65,17 @@ describe('IssueLogger', (): void => {
issueLogger.info(message);
expect(coreInfoSpy).toHaveBeenCalledTimes(1);
expect(coreInfoSpy).toHaveBeenCalledWith(expect.stringContaining('[#8]'));
expect(coreInfoSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.info)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.info)).toHaveBeenCalledWith(
expect.stringContaining('[#8]')
);
expect(jest.mocked(core.info)).toHaveBeenCalledWith(
expect.stringContaining('dummy-message')
);
});
});
describe('error()', (): void => {
let coreErrorSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
issue = new Issue(
@@ -81,8 +85,6 @@ describe('IssueLogger', (): void => {
})
);
issueLogger = new IssueLogger(issue);
coreErrorSpy = jest.spyOn(core, 'error').mockImplementation();
});
it('should log an error with the given message and with the issue number as prefix', (): void => {
@@ -90,11 +92,11 @@ describe('IssueLogger', (): void => {
issueLogger.error(message);
expect(coreErrorSpy).toHaveBeenCalledTimes(1);
expect(coreErrorSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.error)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.error)).toHaveBeenCalledWith(
expect.stringContaining('[#8]')
);
expect(coreErrorSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.error)).toHaveBeenCalledWith(
expect.stringContaining('dummy-message')
);
});
@@ -110,15 +112,14 @@ describe('IssueLogger', (): void => {
})
);
issueLogger = new IssueLogger(issue);
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
issueLogger.warning(message);
expect(coreWarningSpy).toHaveBeenCalledTimes(1);
expect(coreWarningSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.warning)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.warning)).toHaveBeenCalledWith(
expect.stringContaining('[#123]')
);
expect(coreWarningSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.warning)).toHaveBeenCalledWith(
expect.stringContaining('dummy-message')
);
});
@@ -142,15 +143,14 @@ describe('IssueLogger', (): void => {
})
);
issueLogger = new IssueLogger(issue);
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
issueLogger.warning(message);
expect(coreWarningSpy).toHaveBeenCalledTimes(1);
expect(coreWarningSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.warning)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.warning)).toHaveBeenCalledWith(
expect.stringContaining(`[#8]`)
);
expect(coreWarningSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.warning)).toHaveBeenCalledWith(
expect.stringContaining(
`The ${replacement} will stale! ${replacement} will soon be closed!`
)
@@ -177,15 +177,14 @@ describe('IssueLogger', (): void => {
})
);
issueLogger = new IssueLogger(issue);
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
issueLogger.warning(message);
expect(coreWarningSpy).toHaveBeenCalledTimes(1);
expect(coreWarningSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.warning)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.warning)).toHaveBeenCalledWith(
expect.stringContaining(`[#8]`)
);
expect(coreWarningSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.warning)).toHaveBeenCalledWith(
expect.stringContaining(`${replacement} will stale`)
);
}
+3 -3
View File
@@ -1,6 +1,6 @@
import {Issue} from '../issue';
import {Logger} from './logger';
import {LoggerService} from '../../services/logger.service';
import {Issue} from '../issue.js';
import {Logger} from './logger.js';
import {LoggerService} from '../../services/logger.service.js';
/**
* @description
+18 -21
View File
@@ -1,8 +1,17 @@
import {Logger} from './logger';
import * as core from '@actions/core';
import {jest, beforeEach, describe, expect, it} from '@jest/globals';
import type {Logger as LoggerType} from './logger.js';
jest.unstable_mockModule('@actions/core', () => ({
warning: jest.fn(),
info: jest.fn(),
error: jest.fn()
}));
const {Logger} = await import('./logger.js');
const core = await import('@actions/core');
describe('Logger', (): void => {
let logger: Logger;
let logger: LoggerType;
beforeEach((): void => {
logger = new Logger();
@@ -11,12 +20,8 @@ describe('Logger', (): void => {
describe('warning()', (): void => {
let message: string;
let coreWarningSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
});
it('should log a warning with the given message', (): void => {
@@ -24,8 +29,8 @@ describe('Logger', (): void => {
logger.warning(message);
expect(coreWarningSpy).toHaveBeenCalledTimes(1);
expect(coreWarningSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.warning)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.warning)).toHaveBeenCalledWith(
expect.stringContaining('dummy-message')
);
});
@@ -34,12 +39,8 @@ describe('Logger', (): void => {
describe('info()', (): void => {
let message: string;
let coreInfoSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
coreInfoSpy = jest.spyOn(core, 'info').mockImplementation();
});
it('should log an information with the given message', (): void => {
@@ -47,8 +48,8 @@ describe('Logger', (): void => {
logger.info(message);
expect(coreInfoSpy).toHaveBeenCalledTimes(1);
expect(coreInfoSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.info)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.info)).toHaveBeenCalledWith(
expect.stringContaining('dummy-message')
);
});
@@ -57,12 +58,8 @@ describe('Logger', (): void => {
describe('error()', (): void => {
let message: string;
let coreErrorSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
coreErrorSpy = jest.spyOn(core, 'error').mockImplementation();
});
it('should log an error with the given message', (): void => {
@@ -70,8 +67,8 @@ describe('Logger', (): void => {
logger.error(message);
expect(coreErrorSpy).toHaveBeenCalledTimes(1);
expect(coreErrorSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.error)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.error)).toHaveBeenCalledWith(
expect.stringContaining('dummy-message')
);
});
+2 -2
View File
@@ -1,7 +1,7 @@
import * as core from '@actions/core';
import terminalLink from 'terminal-link';
import {Option} from '../../enums/option';
import {LoggerService} from '../../services/logger.service';
import {Option} from '../../enums/option.js';
import {LoggerService} from '../../services/logger.service.js';
export class Logger {
warning(...message: string[]): void {
+7 -6
View File
@@ -1,9 +1,10 @@
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 {Milestones} from './milestones';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options.js';
import {generateIIssue} from '../../__tests__/functions/generate-iissue.js';
import {IIssue} from '../interfaces/issue.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {Issue} from './issue.js';
import {Milestones} from './milestones.js';
describe('Milestones', (): void => {
let milestones: Milestones;
+6 -6
View File
@@ -1,10 +1,10 @@
import deburr from 'lodash.deburr';
import {Option} from '../enums/option';
import {wordsToList} from '../functions/words-to-list';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {Issue} from './issue';
import {IssueLogger} from './loggers/issue-logger';
import {LoggerService} from '../services/logger.service';
import {Option} from '../enums/option.js';
import {wordsToList} from '../functions/words-to-list.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {Issue} from './issue.js';
import {IssueLogger} from './loggers/issue-logger.js';
import {LoggerService} from '../services/logger.service.js';
type CleanMilestone = string;
+2 -1
View File
@@ -1,4 +1,5 @@
import {Operations} from './operations';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {Operations} from './operations.js';
describe('Operations', (): void => {
let operations: Operations;
+1 -1
View File
@@ -1,4 +1,4 @@
import {IRateLimit, OctokitRateLimit} from '../interfaces/rate-limit';
import {IRateLimit, OctokitRateLimit} from '../interfaces/rate-limit.js';
export class RateLimit implements IRateLimit {
readonly limit: number;
+4 -3
View File
@@ -1,6 +1,7 @@
import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {StaleOperations} from './stale-operations';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {StaleOperations} from './stale-operations.js';
interface IHasRemainingOperationsMatrix {
operationsPerRun: number;
+2 -2
View File
@@ -1,5 +1,5 @@
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {Operations} from './operations';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {Operations} from './operations.js';
export class StaleOperations extends Operations {
private readonly _options: IIssuesProcessorOptions;
+2 -2
View File
@@ -1,4 +1,4 @@
import {IStateStorage} from '../../interfaces/state/state-storage';
import {IStateStorage} from '../../interfaces/state/state-storage.js';
import fs from 'fs';
import path from 'path';
import os from 'os';
@@ -20,7 +20,7 @@ const mkTempDir = (): string => {
const unlinkSafely = (filePath: string) => {
try {
fs.unlinkSync(filePath);
} catch (foo) {
} catch {
/* ignore */
}
};
+50 -44
View File
@@ -1,8 +1,18 @@
import {IssueID, State} from './state';
import {IIssuesProcessorOptions} from '../../interfaces/issues-processor-options';
import {IIssue} from '../../interfaces/issue';
import {IState} from '../../interfaces/state/state';
import * as core from '@actions/core';
import {jest, afterEach, describe, expect, it} from '@jest/globals';
import type {IIssue} from '../../interfaces/issue.js';
import type {IIssuesProcessorOptions} from '../../interfaces/issues-processor-options.js';
import type {IState} from '../../interfaces/state/state.js';
jest.unstable_mockModule('@actions/core', () => ({
debug: jest.fn(),
info: jest.fn(),
warning: jest.fn()
}));
const {State} = await import('./state.js');
const core = await import('@actions/core');
type IssueID = number;
const mockStorage = {
save: () => Promise.resolve(),
@@ -13,17 +23,7 @@ const getProcessedIssuesIDs = (state: IState): Set<IssueID> =>
(state as unknown as {processedIssuesIDs: Set<IssueID>}).processedIssuesIDs;
describe('State', () => {
let debugSpy: jest.SpyInstance;
let infoSpy: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
beforeEach(() => {
debugSpy = jest.spyOn(core, 'debug');
infoSpy = jest.spyOn(core, 'info');
warningSpy = jest.spyOn(core, 'warning');
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
@@ -34,7 +34,7 @@ describe('State', () => {
{} as unknown as IIssuesProcessorOptions
);
expect(getProcessedIssuesIDs(state)).toEqual(new Set());
expect(debugSpy).not.toHaveBeenCalled();
expect(jest.mocked(core.debug)).not.toHaveBeenCalled();
});
it('reset state should not contain any issues marked as proceeded', async () => {
const state = new State(
@@ -45,8 +45,8 @@ describe('State', () => {
expect(getProcessedIssuesIDs(state)).not.toEqual(new Set());
state.reset();
expect(getProcessedIssuesIDs(state)).toEqual(new Set());
expect(debugSpy).toHaveBeenCalledTimes(2);
expect(debugSpy).toHaveBeenCalledWith('state: reset');
expect(jest.mocked(core.debug)).toHaveBeenCalledTimes(2);
expect(jest.mocked(core.debug)).toHaveBeenCalledWith('state: reset');
});
});
describe('marking as proceeded', () => {
@@ -74,44 +74,50 @@ describe('State', () => {
expect(
state.isIssueProcessed({number: 4} as unknown as IIssue)
).toBeFalsy();
expect(debugSpy).toHaveBeenCalledTimes(3);
expect(debugSpy).toHaveBeenCalledWith('state: mark 1 as processed');
expect(debugSpy).toHaveBeenCalledWith('state: mark 2 as processed');
expect(debugSpy).toHaveBeenCalledWith('state: mark 3 as processed');
expect(jest.mocked(core.debug)).toHaveBeenCalledTimes(3);
expect(jest.mocked(core.debug)).toHaveBeenCalledWith(
'state: mark 1 as processed'
);
expect(jest.mocked(core.debug)).toHaveBeenCalledWith(
'state: mark 2 as processed'
);
expect(jest.mocked(core.debug)).toHaveBeenCalledWith(
'state: mark 3 as processed'
);
});
});
describe('persisting', () => {
it('[1,2,3] should be serialized and persisted as to "1|2|3|', async () => {
const mockStorage = {
save: jest.fn().mockReturnValue(Promise.resolve()),
it('[1,2,3] should be serialized and persisted as "1|2|3|', async () => {
const localStorage = {
save: jest.fn<() => Promise<void>>().mockResolvedValue(undefined),
async restore(): Promise<string> {
return '';
}
};
const state = new State(
mockStorage,
localStorage,
{} as unknown as IIssuesProcessorOptions
);
state.addIssueToProcessed({number: 1} as unknown as IIssue);
state.addIssueToProcessed({number: 2} as unknown as IIssue);
state.addIssueToProcessed({number: 3} as unknown as IIssue);
await state.persist();
expect(mockStorage.save).toHaveBeenCalledTimes(1);
expect(mockStorage.save).toHaveBeenCalledWith('1|2|3');
expect(infoSpy).toHaveBeenCalledTimes(1);
expect(infoSpy).toHaveBeenCalledWith(
expect(localStorage.save).toHaveBeenCalledTimes(1);
expect(localStorage.save).toHaveBeenCalledWith('1|2|3');
expect(jest.mocked(core.info)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.info)).toHaveBeenCalledWith(
'state: persisting info about 3 issue(s)'
);
});
});
describe('rehydrating', () => {
it('"1|2|3" should be rehydrate to the IState with issues 1,2,3 marked as proceeded', async () => {
const mockStorage = {
const localStorage = {
save: () => Promise.resolve(),
restore: () => Promise.resolve('1|2|3')
};
const state = new State(
mockStorage,
localStorage,
{} as unknown as IIssuesProcessorOptions
);
await state.restore();
@@ -119,41 +125,41 @@ describe('State', () => {
state as unknown as {processedIssuesIDs: Set<IssueID>}
).processedIssuesIDs;
expect(processedIssuesIDs).toEqual(new Set([1, 2, 3]));
expect(infoSpy).toHaveBeenCalledTimes(1);
expect(infoSpy).toHaveBeenCalledWith(
expect(jest.mocked(core.info)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.info)).toHaveBeenCalledWith(
'state: restored with info about 3 issue(s)'
);
});
});
describe('debugOnly', () => {
it('state should persisted if debugOnly not set', () => {
const mockStorage = {
save: jest.fn().mockReturnValue(Promise.resolve()),
const localStorage = {
save: jest.fn<() => Promise<void>>().mockResolvedValue(undefined),
async restore(): Promise<string> {
return '';
}
};
const state = new State(
mockStorage,
localStorage,
{} as unknown as IIssuesProcessorOptions
);
state.persist();
expect(mockStorage.save).toHaveBeenCalledTimes(1);
expect(localStorage.save).toHaveBeenCalledTimes(1);
});
it('state should not be persisted if debugOnly set true', () => {
const mockStorage = {
save: jest.fn().mockReturnValue(Promise.resolve()),
const localStorage = {
save: jest.fn<() => Promise<void>>().mockResolvedValue(undefined),
async restore(): Promise<string> {
return '';
}
};
const state = new State(mockStorage, {
const state = new State(localStorage, {
debugOnly: true
} as unknown as IIssuesProcessorOptions);
state.persist();
expect(mockStorage.save).not.toHaveBeenCalled();
expect(warningSpy).toHaveBeenCalledTimes(1);
expect(warningSpy).toHaveBeenCalledWith(
expect(localStorage.save).not.toHaveBeenCalled();
expect(jest.mocked(core.warning)).toHaveBeenCalledTimes(1);
expect(jest.mocked(core.warning)).toHaveBeenCalledWith(
'The state is not persisted in the debug mode'
);
});
+4 -4
View File
@@ -1,8 +1,8 @@
import {IState} from '../../interfaces/state/state';
import {IState} from '../../interfaces/state/state.js';
import * as core from '@actions/core';
import {IIssuesProcessorOptions} from '../../interfaces/issues-processor-options';
import {IStateStorage} from '../../interfaces/state/state-storage';
import {IIssue} from '../../interfaces/issue';
import {IIssuesProcessorOptions} from '../../interfaces/issues-processor-options.js';
import {IStateStorage} from '../../interfaces/state/state-storage.js';
import {IIssue} from '../../interfaces/issue.js';
export type IssueID = number;
+3 -3
View File
@@ -1,6 +1,6 @@
import {Issue} from './issue';
import {Logger} from './loggers/logger';
import {LoggerService} from '../services/logger.service';
import {Issue} from './issue.js';
import {Logger} from './loggers/logger.js';
import {LoggerService} from '../services/logger.service.js';
interface IGroupValue {
name: string;
+1 -1
View File
@@ -1,5 +1,5 @@
import deburr from 'lodash.deburr';
import {CleanLabel} from '../types/clean-label';
import {CleanLabel} from '../types/clean-label.js';
/**
* @description
@@ -1,4 +1,5 @@
import {getHumanizedDate} from './get-humanized-date';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {getHumanizedDate} from './get-humanized-date.js';
describe('getHumanizedDate()', (): void => {
let date: Date;
+1 -1
View File
@@ -1,4 +1,4 @@
import {HumanizedDate} from '../../types/humanized-date';
import {HumanizedDate} from '../../types/humanized-date.js';
export function getHumanizedDate(date: Readonly<Date>): HumanizedDate {
const year: number = date.getFullYear();
@@ -1,4 +1,8 @@
import {isDateEqualTo, isDateMoreRecentThan} from './is-date-more-recent-than';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {
isDateEqualTo,
isDateMoreRecentThan
} from './is-date-more-recent-than.js';
describe('isDateMoreRecentThan()', (): void => {
let date: Date;
+2 -1
View File
@@ -1,4 +1,5 @@
import {isValidDate} from './is-valid-date';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {isValidDate} from './is-valid-date.js';
describe('isValidDate()', (): void => {
let date: Date;
+2 -2
View File
@@ -3,6 +3,6 @@ export function getSortField(sortOption: sortOptions): sortOptions {
return sortOption === 'updated'
? 'updated'
: sortOption === 'comments'
? 'comments'
: 'created';
? 'comments'
: 'created';
}
+2 -1
View File
@@ -1,4 +1,5 @@
import {isBoolean} from './is-boolean';
import {describe, expect, it} from '@jest/globals';
import {isBoolean} from './is-boolean.js';
describe('isBoolean()', (): void => {
describe.each([0, 1, undefined, null, ''])(
+3 -2
View File
@@ -1,5 +1,6 @@
import {Issue} from '../classes/issue';
import {isLabeled} from './is-labeled';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {Issue} from '../classes/issue.js';
import {isLabeled} from './is-labeled.js';
describe('isLabeled()', (): void => {
let issue: Issue;
+3 -3
View File
@@ -1,6 +1,6 @@
import {Issue} from '../classes/issue';
import {ILabel} from '../interfaces/label';
import {cleanLabel} from './clean-label';
import {Issue} from '../classes/issue.js';
import {ILabel} from '../interfaces/label.js';
import {cleanLabel} from './clean-label.js';
/**
* @description
+3 -2
View File
@@ -1,5 +1,6 @@
import {Issue} from '../classes/issue';
import {isPullRequest} from './is-pull-request';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {Issue} from '../classes/issue.js';
import {isPullRequest} from './is-pull-request.js';
describe('isPullRequest()', (): void => {
let issue: Issue;
+1 -1
View File
@@ -1,4 +1,4 @@
import {Issue} from '../classes/issue';
import {Issue} from '../classes/issue.js';
export function isPullRequest(issue: Readonly<Issue>): boolean {
return !!issue.pull_request;
+2 -1
View File
@@ -1,4 +1,5 @@
import {shouldMarkWhenStale} from './should-mark-when-stale';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {shouldMarkWhenStale} from './should-mark-when-stale.js';
describe('shouldMarkWhenStale()', (): void => {
let daysBeforeStale: number;
+2 -1
View File
@@ -1,4 +1,5 @@
import {wordsToList} from './words-to-list';
import {beforeEach, describe, expect, it} from '@jest/globals';
import {wordsToList} from './words-to-list.js';
describe('wordsToList()', (): void => {
let words: string;
+1 -1
View File
@@ -1,4 +1,4 @@
import {IUser} from './user';
import {IUser} from './user.js';
export interface IComment {
user: IUser | null;
+1 -1
View File
@@ -1,4 +1,4 @@
import {ILabel} from './label';
import {ILabel} from './label.js';
export interface IIssueEvent {
created_at: string;
+4 -4
View File
@@ -1,7 +1,7 @@
import {IsoDateString} from '../types/iso-date-string';
import {Assignee} from './assignee';
import {ILabel} from './label';
import {IMilestone} from './milestone';
import {IsoDateString} from '../types/iso-date-string.js';
import {Assignee} from './assignee.js';
import {ILabel} from './label.js';
import {IMilestone} from './milestone.js';
import {components} from '@octokit/openapi-types';
export interface IIssue {
title: string;
+1 -1
View File
@@ -1,4 +1,4 @@
import {IsoOrRfcDateString} from '../types/iso-or-rfc-date-string';
import {IsoOrRfcDateString} from '../types/iso-or-rfc-date-string.js';
export interface IIssuesProcessorOptions {
repoToken: string;
+1 -1
View File
@@ -1,4 +1,4 @@
import {IIssue} from '../issue';
import {IIssue} from '../issue.js';
export interface IState {
isIssueProcessed(issue: IIssue): boolean;
+7 -7
View File
@@ -1,9 +1,9 @@
import * as core from '@actions/core';
import {IssuesProcessor} from './classes/issues-processor';
import {isValidDate} from './functions/dates/is-valid-date';
import {IIssuesProcessorOptions} from './interfaces/issues-processor-options';
import {Issue} from './classes/issue';
import {getStateInstance} from './services/state.service';
import {IssuesProcessor} from './classes/issues-processor.js';
import {isValidDate} from './functions/dates/is-valid-date.js';
import {IIssuesProcessorOptions} from './interfaces/issues-processor-options.js';
import {Issue} from './classes/issue.js';
import {getStateInstance} from './services/state.service.js';
async function _run(): Promise<void> {
try {
@@ -206,8 +206,8 @@ function _processParamtoString(
return sortByValueInput === 'updated'
? 'updated'
: sortByValueInput === 'comments'
? 'comments'
: 'created';
? 'comments'
: 'created';
}
void _run();
+4 -4
View File
@@ -1,7 +1,7 @@
import {IState} from '../interfaces/state/state';
import {State} from '../classes/state/state';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
import {StateCacheStorage} from '../classes/state/state-cache-storage';
import {IState} from '../interfaces/state/state.js';
import {State} from '../classes/state/state.js';
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options.js';
import {StateCacheStorage} from '../classes/state/state-cache-storage.js';
export const getStateInstance = (options: IIssuesProcessorOptions): IState => {
const storage = new StateCacheStorage();