Skip to content

Commit 5665ed1

Browse files
fix: account for multiple existing equivalent labels (#768)
## PR Checklist - [x] Addresses an existing open issue: fixes #767 - [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/create-typescript-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/create-typescript-app/blob/main/.github/CONTRIBUTING.md) were taken ## Overview Changes `getExistingEquivalentLabel` (singular) to `getExistingEquivalentLabels` (plural).
1 parent 235687f commit 5665ed1

File tree

6 files changed

+228
-109
lines changed

6 files changed

+228
-109
lines changed

src/steps/initializeGitHubRepository/labels/getExistingEquivalentLabel.test.ts

Lines changed: 0 additions & 60 deletions
This file was deleted.

src/steps/initializeGitHubRepository/labels/getExistingEquivalentLabel.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { describe, expect, it } from "vitest";
2+
3+
import { getExistingEquivalentLabels } from "./getExistingEquivalentLabels.js";
4+
5+
const createLabel = (name: string) => ({
6+
color: "#000000",
7+
description: "A good label.",
8+
name,
9+
});
10+
11+
describe("getExistingEquivalentLabels", () => {
12+
it("returns no labels when there are no existing labels", () => {
13+
const actual = getExistingEquivalentLabels([], "abc");
14+
15+
expect(actual).toEqual([]);
16+
});
17+
18+
it("returns no labels when no existing label matches", () => {
19+
const actual = getExistingEquivalentLabels([createLabel("abc")], "def");
20+
21+
expect(actual).toEqual([]);
22+
});
23+
24+
it("returns an existing un-prefixed label when it matches by name", () => {
25+
const abcLabel = createLabel("abc");
26+
const actual = getExistingEquivalentLabels(
27+
[createLabel("def"), abcLabel, createLabel("ghi")],
28+
"abc",
29+
);
30+
31+
expect(actual).toEqual([abcLabel]);
32+
});
33+
34+
it("returns an existing prefixed label when it matches by name", () => {
35+
const abcDefLabel = createLabel("abc: def");
36+
const actual = getExistingEquivalentLabels([abcDefLabel], "abc: def");
37+
38+
expect(actual).toEqual([abcDefLabel]);
39+
});
40+
41+
it("returns the existing label when it matches excluding prefix", () => {
42+
const abcLabel = createLabel("abc");
43+
const actual = getExistingEquivalentLabels(
44+
[createLabel("abc: def"), abcLabel, createLabel("ghi")],
45+
"type: abc",
46+
);
47+
48+
expect(actual).toEqual([abcLabel]);
49+
});
50+
51+
it("returns the existing label when it matches an alias", () => {
52+
const enhancementLabel = createLabel("enhancement");
53+
const actual = getExistingEquivalentLabels(
54+
[createLabel("abc: def"), enhancementLabel, createLabel("ghi")],
55+
"type: feature",
56+
);
57+
58+
expect(actual).toEqual([enhancementLabel]);
59+
});
60+
61+
it("returns both existing labels when one matches on name and another matches an alias", () => {
62+
const enhancementLabel = createLabel("enhancement");
63+
const typeFeatureLabel = createLabel("type: feature");
64+
const actual = getExistingEquivalentLabels(
65+
[
66+
createLabel("abc: def"),
67+
enhancementLabel,
68+
createLabel("ghi"),
69+
typeFeatureLabel,
70+
],
71+
"type: feature",
72+
);
73+
74+
expect(actual).toEqual([enhancementLabel, typeFeatureLabel]);
75+
});
76+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const aliases = new Map([
2+
["enhancement", "type: feature"],
3+
["help wanted", "status: accepting prs"],
4+
]);
5+
6+
export interface GhLabelData {
7+
color: string;
8+
description: string;
9+
name: string;
10+
}
11+
12+
export function getExistingEquivalentLabels(
13+
existingLabels: GhLabelData[],
14+
outcomeLabelName: string,
15+
) {
16+
const outcomeTrimmed = outcomeLabelName.replace(/\w+: (\w+)/, "$1");
17+
18+
return existingLabels.filter(({ name: existingName }) => {
19+
return (
20+
existingName === outcomeLabelName ||
21+
existingName === outcomeTrimmed ||
22+
aliases.get(existingName) === outcomeLabelName ||
23+
existingName.replace(/\w+: (\w+)/, "$1") === outcomeLabelName
24+
);
25+
});
26+
}

src/steps/initializeGitHubRepository/labels/initializeRepositoryLabels.test.ts

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ vi.mock("execa", () => ({
1313
const mockOutcomeLabel = {
1414
color: "000000",
1515
description: "def ghi",
16-
name: "abc",
16+
name: "area: abc",
1717
};
1818

1919
vi.mock("./outcomeLabels.js", () => ({
@@ -23,7 +23,7 @@ vi.mock("./outcomeLabels.js", () => ({
2323
}));
2424

2525
describe("migrateRepositoryLabels", () => {
26-
it("creates a outcome label when labels stdout is returned", async () => {
26+
it("creates an outcome label when labels stdout is empty", async () => {
2727
mock$.mockResolvedValue({
2828
stdout: "",
2929
});
@@ -44,15 +44,15 @@ describe("migrateRepositoryLabels", () => {
4444
" --description ",
4545
"",
4646
],
47-
"abc",
47+
"area: abc",
4848
"000000",
4949
"def ghi",
5050
],
5151
]
5252
`);
5353
});
5454

55-
it("creates a outcome label when it doesn't already exist", async () => {
55+
it("creates an outcome label when it doesn't already exist", async () => {
5656
mock$.mockResolvedValue({
5757
stdout: JSON.stringify([
5858
{
@@ -79,7 +79,7 @@ describe("migrateRepositoryLabels", () => {
7979
" --description ",
8080
"",
8181
],
82-
"abc",
82+
"area: abc",
8383
"000000",
8484
"def ghi",
8585
],
@@ -105,7 +105,7 @@ describe("migrateRepositoryLabels", () => {
105105
`);
106106
});
107107

108-
it("edits a outcome label when it already exists with different color", async () => {
108+
it("edits the outcome label when it already exists with different color", async () => {
109109
mock$.mockResolvedValue({
110110
stdout: JSON.stringify([
111111
{
@@ -130,18 +130,18 @@ describe("migrateRepositoryLabels", () => {
130130
" --color ",
131131
" --description ",
132132
" --name ",
133-
"",
133+
" --yes",
134134
],
135-
"abc",
135+
"area: abc",
136136
"000000",
137137
"def ghi",
138-
"abc",
138+
"area: abc",
139139
],
140140
]
141141
`);
142142
});
143143

144-
it("edits a outcome label when it already exists with different description", async () => {
144+
it("edits the outcome label when it already exists with a different description", async () => {
145145
mock$.mockResolvedValue({
146146
stdout: JSON.stringify([
147147
{
@@ -166,12 +166,12 @@ describe("migrateRepositoryLabels", () => {
166166
" --color ",
167167
" --description ",
168168
" --name ",
169-
"",
169+
" --yes",
170170
],
171-
"abc",
171+
"area: abc",
172172
"000000",
173173
"def ghi",
174-
"abc",
174+
"area: abc",
175175
],
176176
]
177177
`);
@@ -183,7 +183,7 @@ describe("migrateRepositoryLabels", () => {
183183
{
184184
color: "000000",
185185
description: "def ghi",
186-
name: "abc",
186+
name: "area: abc",
187187
},
188188
{
189189
color: "000000",
@@ -233,9 +233,94 @@ describe("migrateRepositoryLabels", () => {
233233
" --description ",
234234
"",
235235
],
236+
"area: abc",
237+
"000000",
238+
"def ghi",
239+
],
240+
]
241+
`);
242+
});
243+
244+
it("deletes the existing duplicate outcome label and edits the label with the outcome name and different color when both exist", async () => {
245+
mock$.mockResolvedValue({
246+
stdout: JSON.stringify([
247+
{
248+
color: "000000",
249+
description: "def ghi",
250+
name: "abc",
251+
},
252+
{
253+
color: "111111",
254+
description: "def ghi",
255+
name: "area: abc",
256+
},
257+
]),
258+
});
259+
260+
await initializeRepositoryLabels();
261+
262+
expect(mock$.mock.calls).toMatchInlineSnapshot(`
263+
[
264+
[
265+
[
266+
"gh label list --json color,description,name",
267+
],
268+
],
269+
[
270+
[
271+
"gh label delete ",
272+
" --yes",
273+
],
236274
"abc",
275+
],
276+
[
277+
[
278+
"gh label edit ",
279+
" --color ",
280+
" --description ",
281+
" --name ",
282+
" --yes",
283+
],
284+
"area: abc",
237285
"000000",
238286
"def ghi",
287+
"area: abc",
288+
],
289+
]
290+
`);
291+
});
292+
293+
it("deletes the existing duplicate outcome label and does not edit the label with the outcome name and same information when both exist", async () => {
294+
mock$.mockResolvedValue({
295+
stdout: JSON.stringify([
296+
{
297+
color: "000000",
298+
description: "def ghi",
299+
name: "abc",
300+
},
301+
{
302+
color: "000000",
303+
description: "def ghi",
304+
name: "area: abc",
305+
},
306+
]),
307+
});
308+
309+
await initializeRepositoryLabels();
310+
311+
expect(mock$.mock.calls).toMatchInlineSnapshot(`
312+
[
313+
[
314+
[
315+
"gh label list --json color,description,name",
316+
],
317+
],
318+
[
319+
[
320+
"gh label delete ",
321+
" --yes",
322+
],
323+
"abc",
239324
],
240325
]
241326
`);
@@ -268,7 +353,7 @@ describe("migrateRepositoryLabels", () => {
268353
" --description ",
269354
"",
270355
],
271-
"abc",
356+
"area: abc",
272357
"000000",
273358
"def ghi",
274359
],

0 commit comments

Comments
 (0)