Skip to content

Commit

Permalink
test: fix tests (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
ytanikin committed Sep 28, 2024
1 parent 8d258b5 commit cc6cc0d
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 76 deletions.
25 changes: 23 additions & 2 deletions .github/workflows/conventional_commits_in_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,26 @@ jobs:
- name: RR Conventional Commit Validation
uses: ./ # Uses an action in the root directory of the repository
with:
task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert"]'
custom_labels: '{"feat": "feature", "fix": "fix", "docs": "documentation", "test": "test", "ci": "CI/CD", "refactor": "refactor", "perf": "performance", "chore": "chore", "revert": "revert", "wip": "WIP"}'
task_types: '[
"feat",
"fix",
"docs",
"test",
"ci",
"refactor",
"perf",
"chore",
"revert"
]'
custom_labels: '{
"feat": "feature",
"fix": "fix",
"docs": "documentation",
"test": "test",
"ci": "CI/CD",
"refactor": "refactor",
"perf": "performance",
"chore": "chore",
"revert": "revert",
"wip": "WIP"
}'
29 changes: 29 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Test

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'

- name: Install dependencies
run: npm install

- name: Run tests
run: npm test

- name: Build project
run: npm run prepare
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,11 @@ For this configuration, the following PR title is valid: `feat: PROJECT-12345 ad

### Contributing

After updating the code, please run `npm prepare` to build the distribution files before committing.
After modifying the code, please run:
1. `npm test` to run test
2. `npm run prepare` to build the distribution files(`dist` folder with `index.js.map` file). Commit it together with the changes in `index.js` file.

You can verify your changes by editing the title of your PR. The PR uses an action located in the root directory of the repository or the branch associated with your PR.

## Author

Expand Down
12 changes: 2 additions & 10 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ async function checkConventionalCommits() {
}

const pr = context.payload.pull_request;
const titleAst = parser.sync(pr.title);
const titleAst = parser.sync(pr.title.trimStart());
const cc = {
type: titleAst.type ? titleAst.type : '',
scope: titleAst.scope ? titleAst.scope : '',
Expand Down Expand Up @@ -75,7 +75,6 @@ async function checkTicketNumber() {
async function applyLabel(pr, commitDetail) {
const addLabel = getInput('add_label');
if (addLabel !== undefined && addLabel.toLowerCase() === 'false') {
console.log('Skipping label addition as add_label is set to false.');
return;
}
const customLabelsInput = getInput('custom_labels');
Expand Down Expand Up @@ -107,26 +106,21 @@ async function updateLabels(pr, cc, customLabels) {
repo: context.repo.repo,
issue_number: pr.number
});
console.log('Current labels:', currentLabelsResult.data.map(label => label.name));
const currentLabels = currentLabelsResult.data.map(label => label.name);
let taskTypesInput = getInput('task_types');
console.log('Task types:', taskTypesInput);
let taskTypeList = JSON.parse(taskTypesInput);
const managedLabels = taskTypeList.concat(['breaking change']);
// Include customLabels keys in managedLabels, if any
console.log('Custom labels:', customLabels)
Object.values(customLabels).forEach(label => {
if (!managedLabels.includes(label)) {
managedLabels.push(label);
}
});
console.log('Managed labels:', managedLabels);
let newLabels = [customLabels[cc.type] ? customLabels[cc.type] : cc.type];
const breakingChangeLabel = 'breaking change';
if (cc.breaking && !newLabels.includes(breakingChangeLabel)) {
newLabels.push(breakingChangeLabel);
}
console.log('New labels:', newLabels);
// Determine labels to remove and remove them
const labelsToRemove = currentLabels.filter(label => managedLabels.includes(label) && !newLabels.includes(label));
for (let label of labelsToRemove) {
Expand All @@ -137,7 +131,6 @@ async function updateLabels(pr, cc, customLabels) {
name: label
});
}
console.log('Current labels:', currentLabels);
// Ensure new labels exist with the desired color and add them
for (let label of newLabels) {
if (!currentLabels.includes(label)) {
Expand Down Expand Up @@ -167,7 +160,6 @@ async function updateLabels(pr, cc, customLabels) {
});
}
}
console.log('New labels:', newLabels);
}

/**
Expand Down Expand Up @@ -31229,4 +31221,4 @@ module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"]
/******/
/******/ })()
;
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map
2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ async function checkConventionalCommits() {
}

const pr = context.payload.pull_request;
const titleAst = parser.sync(pr.title);
const titleAst = parser.sync(pr.title.trimStart());
const cc = {
type: titleAst.type ? titleAst.type : '',
scope: titleAst.scope ? titleAst.scope : '',
Expand Down
141 changes: 80 additions & 61 deletions index.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
const { getInput, setFailed } = require('@actions/core');
const { getOctokit, context } = require('@actions/github');
const toConventionalChangelogFormat = require('@conventional-commits/parser').toConventionalChangelogFormat;
const when = require('jest-when').when;
const toConventionalChangelogFormat = require('conventional-commits-parser');

jest.mock('@actions/core');
jest.mock('@actions/github');
jest.mock('@conventional-commits/parser');

const myModule = require('./index');

Expand All @@ -23,25 +21,38 @@ afterEach(() => {
});

describe('checkConventionalCommits', () => {
it('should pass with matching conventional commit', async () => {
when(getInput).calledWith('task_types').mockReturnValue('["feat"]');
when(toConventionalChangelogFormat).calledWith('feat(stuff)!:blah').mockReturnValue({
type: "feat",
scope: "stuff",
notes: [{
title: "BREAKING CHANGE",
text: 'blah'

}]

it('should succeed when a valid task type is provided', async () => {
getInput.mockReturnValue(JSON.stringify(['feat', 'fix']));
context.payload = {
pull_request: { title: 'feat(login): add new login feature' }
};

const commitDetail = await myModule.checkConventionalCommits();

expect(setFailed).not.toHaveBeenCalled();
expect(commitDetail).toEqual({
type: 'feat',
scope: 'login',
breaking: false
});
});

xit('should succeed when a valid task type is provided and breaking change', async () => {
getInput.mockReturnValue(JSON.stringify(['feat', 'fix']));
context.payload = {
"pull_request": {
"title": "feat(stuff)!:blah"
}
}
await myModule.checkConventionalCommits();
expect(setFailed).not.toHaveBeenCalled()
})
pull_request: { title: 'feat(login)!: add new login feature' }
};

const commitDetail = await myModule.checkConventionalCommits();

expect(setFailed).not.toHaveBeenCalled();
expect(commitDetail).toEqual({
type: 'feat',
scope: 'login',
breaking: true
});
});

it('should fail when task_types input is missing', async () => {
getInput.mockReturnValue('');
Expand All @@ -54,7 +65,6 @@ describe('checkConventionalCommits', () => {
await myModule.checkConventionalCommits();
expect(setFailed).toHaveBeenCalledWith('Invalid task_types input. Expecting a JSON array.');
});
// ... add more tests as required
});

describe('checkTicketNumber', () => {
Expand All @@ -66,77 +76,85 @@ describe('checkTicketNumber', () => {
await myModule.checkTicketNumber();
expect(setFailed).toHaveBeenCalledWith('Invalid or missing task number: \'\'. Must match: \\d+');
});
// ... add more tests as required
});

describe('applyLabel', () => {
beforeEach(() => {
// Mock the context.repo object to provide owner and repo values
context.repo = {
owner: 'mockOwner',
repo: 'mockRepo',
};
});

it('should skip label addition if add_label is set to false', async () => {
getInput.mockReturnValue('false');
await myModule.applyLabel({}, {});
expect(setFailed).not.toHaveBeenCalled();
expect(console.log).toHaveBeenCalledWith('Skipping label addition as add_label is set to false.');
});

it('should fail if custom_labels input is invalid JSON', async () => {
getInput.mockReturnValueOnce('true').mockReturnValueOnce('invalid JSON');
await myModule.applyLabel({}, {});
expect(setFailed).toHaveBeenCalledWith('Invalid custom_labels input. Unable to parse JSON.');
});
// ... add more tests as required
});
describe('updateLabels', () => {
let octokit;

beforeEach(() => {
octokit = {
it('should remove existing labels that are in the managed list but not in the new labels', async () => {
const mockOctokit = {
rest: {
issues: {
listLabelsOnIssue: jest.fn(),
removeLabel: jest.fn(),
getLabel: jest.fn(),
createLabel: jest.fn(),
addLabels: jest.fn()
}
}
listLabelsOnIssue: jest.fn().mockResolvedValue({
data: [
{ name: 'feat' },
{ name: 'fix' },
{ name: 'breaking change' },
],
}),
removeLabel: jest.fn().mockResolvedValue({}),
addLabels: jest.fn().mockResolvedValue({}),
},
},
};
getOctokit.mockReturnValue(octokit);
});

it('should remove all existing labels and add new labels', async () => {
getInput.mockReturnValue('myToken');
context.repo = { owner: 'myOwner', repo: 'myRepo' };
octokit.rest.issues.listLabelsOnIssue.mockResolvedValue({ data: [{ name: 'oldLabel' }] });
octokit.rest.issues.getLabel.mockRejectedValue(new Error());
getInput.mockImplementation((inputName) => {
if (inputName === 'task_types') {
return JSON.stringify(['feat', 'fix']);
}
if (inputName === 'token') {
return 'token';
}
return undefined;
});

const pr = { number: 123 };
const cc = { type: 'myType', breaking: false };
getOctokit.mockReturnValue(mockOctokit);
const pr = {
number: 123,
};
const commitDetail = {
type: 'fix',
breaking: false,
};
const customLabels = {};

await myModule.updateLabels(pr, cc, customLabels);
getInput.mockReturnValueOnce(JSON.stringify(['feat', 'fix'])); // task_types
getOctokit.mockReturnValue(mockOctokit);

expect(octokit.rest.issues.removeLabel).toHaveBeenCalledWith({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
name: 'oldLabel'
});
// Directly call the updateLabels function
await myModule.updateLabels(pr, commitDetail, customLabels);

expect(octokit.rest.issues.createLabel).toHaveBeenCalledWith({
// Assert removeLabel was called for 'feat' and 'breaking change'
expect(mockOctokit.rest.issues.removeLabel).toHaveBeenCalledWith({
owner: context.repo.owner,
repo: context.repo.repo,
name: 'myType',
color: expect.any(String)
issue_number: pr.number,
name: 'feat',
});

expect(octokit.rest.issues.addLabels).toHaveBeenCalledWith({
expect(mockOctokit.rest.issues.removeLabel).toHaveBeenCalledWith({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: ['myType']
name: 'breaking change',
});
});

// ... add more tests as required
});

describe('generateColor', () => {
Expand All @@ -149,4 +167,5 @@ describe('generateColor', () => {
const color2 = myModule.generateColor('test2');
expect(color1).not.toEqual(color2);
});

});

0 comments on commit cc6cc0d

Please sign in to comment.