Skip to content

Commit

Permalink
fix(label): allow to use spaces inside the labels (actions#199)
Browse files Browse the repository at this point in the history
* chore(git): ignore .idea folder to avoid adding WebStorm files

* test(jest): find all spec files as well

* refactor(labels): create a dedicated function to parse the labels

at first I thought that the parseCommaSeparatedString method was causing the issue so I move it to a dedicated file to test it since it was private
also because I think that this repo could have more clean code and code splitting
anyway this was not the root of the actions#98 issue :/

* fix(label): allow to use spaces inside the labels

* docs(isLabeled): add JSDoc

* chore(npm): add lint:fix script
  • Loading branch information
C0ZEN authored Nov 20, 2020
1 parent 9b82e8c commit 324009e
Show file tree
Hide file tree
Showing 9 changed files with 407 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
node_modules/
lib/
__tests__/runner/*
.idea
4 changes: 2 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testMatch: ['**/*.test.ts', '**/*.spec.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
verbose: true
}
};
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"lint": "eslint src/**/*.ts",
"lint:fix": "eslint src/**/*.ts --fix",
"pack": "ncc build",
"test": "jest",
"all": "npm run build && npm run format && npm run lint && npm run pack && npm test"
Expand All @@ -28,12 +29,14 @@
"@actions/core": "^1.2.6",
"@actions/github": "^4.0.0",
"@octokit/rest": "^18.0.4",
"lodash.deburr": "^4.1.0",
"semver": "^7.3.2"
},
"devDependencies": {
"@types/semver": "^7.3.1",
"@types/jest": "^26.0.10",
"@types/lodash.deburr": "^4.1.6",
"@types/node": "^14.10.0",
"@types/semver": "^7.3.1",
"@typescript-eslint/parser": "^3.10.1",
"@vercel/ncc": "^0.24.0",
"eslint": "^7.7.0",
Expand Down
21 changes: 5 additions & 16 deletions src/IssueProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as core from '@actions/core';
import {context, getOctokit} from '@actions/github';
import {GetResponseTypeFromEndpointMethod} from '@octokit/types';
import {isLabeled} from './functions/is-labeled';
import {labelsToList} from './functions/labels-to-list';

export interface Issue {
title: string;
Expand Down Expand Up @@ -131,7 +133,7 @@ export class IssueProcessor {
const closeLabel: string = isPr
? this.options.closePrLabel
: this.options.closeIssueLabel;
const exemptLabels = IssueProcessor.parseCommaSeparatedString(
const exemptLabels: string[] = labelsToList(
isPr ? this.options.exemptPrLabels : this.options.exemptIssueLabels
);
const skipMessage = isPr
Expand All @@ -157,15 +159,15 @@ export class IssueProcessor {

if (
exemptLabels.some((exemptLabel: string) =>
IssueProcessor.isLabeled(issue, exemptLabel)
isLabeled(issue, exemptLabel)
)
) {
core.info(`Skipping ${issueType} because it has an exempt label`);
continue; // don't process exempt issues
}

// does this issue have a stale label?
let isStale = IssueProcessor.isLabeled(issue, staleLabel);
let isStale = isLabeled(issue, staleLabel);

// should this issue be marked stale?
const shouldBeStale = !IssueProcessor.updatedSince(
Expand Down Expand Up @@ -486,24 +488,11 @@ export class IssueProcessor {
return staleLabeledEvent.created_at;
}

private static isLabeled(issue: Issue, label: string): boolean {
const labelComparer: (l: Label) => boolean = l =>
label.localeCompare(l.name, undefined, {sensitivity: 'accent'}) === 0;
return issue.labels.filter(labelComparer).length > 0;
}

private static updatedSince(timestamp: string, num_days: number): boolean {
const daysInMillis = 1000 * 60 * 60 * 24 * num_days;
const millisSinceLastUpdated =
new Date().getTime() - new Date(timestamp).getTime();

return millisSinceLastUpdated <= daysInMillis;
}

private static parseCommaSeparatedString(s: string): string[] {
// String.prototype.split defaults to [''] when called on an empty string
// In this case, we'd prefer to just return an empty array indicating no labels
if (!s.length) return [];
return s.split(',').map(l => l.trim());
}
}
187 changes: 187 additions & 0 deletions src/functions/is-labeled.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import {Issue} from '../IssueProcessor';
import {isLabeled} from './is-labeled';

describe('isLabeled()', (): void => {
let issue: Issue;
let label: string;

describe('when the given issue contains no label', (): void => {
beforeEach((): void => {
issue = ({
labels: []
} as unknown) as Issue;
});

describe('when the given label is a simple label', (): void => {
beforeEach((): void => {
label = 'label';
});

it('should return false', (): void => {
expect.assertions(1);

const result = isLabeled(issue, label);

expect(result).toStrictEqual(false);
});
});
});

describe('when the given issue contains a simple label', (): void => {
beforeEach((): void => {
issue = {
labels: [
{
name: 'label'
}
]
} as Issue;
});

describe('when the given label is a simple label', (): void => {
beforeEach((): void => {
label = 'label';
});

it('should return true', (): void => {
expect.assertions(1);

const result = isLabeled(issue, label);

expect(result).toStrictEqual(true);
});
});
});

describe('when the given issue contains a kebab case label', (): void => {
beforeEach((): void => {
issue = {
labels: [
{
name: 'kebab-case-label'
}
]
} as Issue;
});

describe('when the given label is a kebab case label', (): void => {
beforeEach((): void => {
label = 'kebab-case-label';
});

it('should return true', (): void => {
expect.assertions(1);

const result = isLabeled(issue, label);

expect(result).toStrictEqual(true);
});
});
});

describe('when the given issue contains a multiple word label', (): void => {
beforeEach((): void => {
issue = {
labels: [
{
name: 'label like a sentence'
}
]
} as Issue;
});

describe('when the given label is a multiple word label', (): void => {
beforeEach((): void => {
label = 'label like a sentence';
});

it('should return true', (): void => {
expect.assertions(1);

const result = isLabeled(issue, label);

expect(result).toStrictEqual(true);
});
});
});

describe('when the given issue contains a multiple word label with %20 spaces', (): void => {
beforeEach((): void => {
issue = {
labels: [
{
name: 'label%20like%20a%20sentence'
}
]
} as Issue;
});

describe('when the given label is a multiple word label with %20 spaces', (): void => {
beforeEach((): void => {
label = 'label%20like%20a%20sentence';
});

it('should return true', (): void => {
expect.assertions(1);

const result = isLabeled(issue, label);

expect(result).toStrictEqual(true);
});
});
});

describe('when the given issue contains a label wih diacritical marks', (): void => {
beforeEach((): void => {
issue = {
labels: [
{
name: 'déjà vu'
}
]
} as Issue;
});

describe('when the given issue contains a label', (): void => {
beforeEach((): void => {
label = 'deja vu';
});

it('should return true', (): void => {
expect.assertions(1);

const result = isLabeled(issue, label);

expect(result).toStrictEqual(true);
});
});

describe('when the given issue contains an uppercase label', (): void => {
beforeEach((): void => {
label = 'DEJA VU';
});

it('should return true', (): void => {
expect.assertions(1);

const result = isLabeled(issue, label);

expect(result).toStrictEqual(true);
});
});

describe('when the given issue contains a label wih diacritical marks', (): void => {
beforeEach((): void => {
label = 'déjà vu';
});

it('should return true', (): void => {
expect.assertions(1);

const result = isLabeled(issue, label);

expect(result).toStrictEqual(true);
});
});
});
});
24 changes: 24 additions & 0 deletions src/functions/is-labeled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import deburr from 'lodash.deburr';
import {Issue, Label} from '../IssueProcessor';

/**
* @description
* Check if the label is listed as a label of the issue
*
* @param {Readonly<Issue>} issue A GitHub issue containing some labels
* @param {Readonly<string>} label The label to check the presence with
*
* @return {boolean} Return true when the given label is also in the issue labels
*/
export function isLabeled(
issue: Readonly<Issue>,
label: Readonly<string>
): boolean {
return !!issue.labels.find((issueLabel: Readonly<Label>): boolean => {
return cleanLabel(label) === cleanLabel(issueLabel.name);
});
}

function cleanLabel(label: Readonly<string>): string {
return deburr(label.toLowerCase());
}
Loading

0 comments on commit 324009e

Please sign in to comment.