@@ -15,11 +15,11 @@ describe("extractLinearIssueIdentifiersForCommit", () => {
1515 expect ( result ) . toEqual ( [ "ENG-123" ] ) ;
1616 } ) ;
1717
18- it ( "extracts identifiers from commit message" , ( ) => {
18+ it ( "extracts identifiers from commit message with magic words " , ( ) => {
1919 const commit : CommitContext = {
2020 sha : "abc123" ,
2121 branchName : "feature/no-key-here" ,
22- message : "Implements PLAT-42 and ENG-7 in one go" ,
22+ message : "Fixes PLAT-42 and ENG-7 in one go" ,
2323 } ;
2424
2525 const result = extractLinearIssueIdentifiersForCommit ( commit ) ;
@@ -31,7 +31,7 @@ describe("extractLinearIssueIdentifiersForCommit", () => {
3131 const commit : CommitContext = {
3232 sha : "abc123" ,
3333 branchName : "feature/eng-123-awesome-change" ,
34- message : "ENG-123 fixed , see ENG-123" ,
34+ message : "Fixed ENG-123, see ENG-123" ,
3535 } ;
3636
3737 const result = extractLinearIssueIdentifiersForCommit ( commit ) ;
@@ -63,6 +63,18 @@ describe("extractLinearIssueIdentifiersForCommit", () => {
6363 expect ( result . sort ( ) ) . toEqual ( [ "A-1" , "ABCDEFG-999" , "X1Y2Z3A-100" ] . sort ( ) ) ;
6464 } ) ;
6565
66+ it ( "does not extract identifiers from commit message without magic words" , ( ) => {
67+ const commit : CommitContext = {
68+ sha : "abc123" ,
69+ branchName : "feature/no-key-here" ,
70+ message : "See LIN-123 for details" ,
71+ } ;
72+
73+ const result = extractLinearIssueIdentifiersForCommit ( commit ) ;
74+
75+ expect ( result ) . toEqual ( [ ] ) ;
76+ } ) ;
77+
6678 it ( "does not match team keys longer than 7 characters" , ( ) => {
6779 const commit : CommitContext = {
6880 sha : "abc123" ,
@@ -140,8 +152,8 @@ describe("underscore handling ", () => {
140152
141153describe ( "multiple identifiers " , ( ) => {
142154 it . each ( [
143- [ "LIN-123 LIN-321" , [ "LIN-123" , "LIN-321" ] ] ,
144- [ "Closes issues LIN-123 and LIN-321" , [ "LIN-123" , "LIN-321" ] ] ,
155+ [ "Fixes LIN-123 and LIN-321" , [ "LIN-123" , "LIN-321" ] ] ,
156+ [ "Closes LIN-123, LIN-321" , [ "LIN-123" , "LIN-321" ] ] ,
145157 ] ) ( "message %s should yield %j" , ( message , expected ) => {
146158 const result = extractLinearIssueIdentifiersForCommit ( {
147159 sha : "abc" ,
@@ -152,6 +164,226 @@ describe("multiple identifiers ", () => {
152164 } ) ;
153165} ) ;
154166
167+ describe ( "commit message magic word behavior" , ( ) => {
168+ it ( "extracts with closing keyword" , ( ) => {
169+ const result = extractLinearIssueIdentifiersForCommit ( {
170+ sha : "abc" ,
171+ branchName : null ,
172+ message : "Fixes LIN-123" ,
173+ } ) ;
174+ expect ( result ) . toEqual ( [ "LIN-123" ] ) ;
175+ } ) ;
176+
177+ it ( "extracts with contributing phrase" , ( ) => {
178+ const result = extractLinearIssueIdentifiersForCommit ( {
179+ sha : "abc" ,
180+ branchName : null ,
181+ message : "Related to LIN-123" ,
182+ } ) ;
183+ expect ( result ) . toEqual ( [ "LIN-123" ] ) ;
184+ } ) ;
185+
186+ it ( "does not extract without magic words" , ( ) => {
187+ const result = extractLinearIssueIdentifiersForCommit ( {
188+ sha : "abc" ,
189+ branchName : null ,
190+ message : "See LIN-123 for details" ,
191+ } ) ;
192+ expect ( result ) . toEqual ( [ ] ) ;
193+ } ) ;
194+
195+ it ( "extracts multiple keys after keyword" , ( ) => {
196+ const result = extractLinearIssueIdentifiersForCommit ( {
197+ sha : "abc" ,
198+ branchName : null ,
199+ message : "Fixes LIN-123, LIN-456 and ENG-789" ,
200+ } ) ;
201+ expect ( result . sort ( ) ) . toEqual ( [ "ENG-789" , "LIN-123" , "LIN-456" ] ) ;
202+ } ) ;
203+
204+ it ( "extracts magic word in title line" , ( ) => {
205+ const result = extractLinearIssueIdentifiersForCommit ( {
206+ sha : "abc" ,
207+ branchName : null ,
208+ message : "Fix LIN-123: something" ,
209+ } ) ;
210+ expect ( result ) . toEqual ( [ "LIN-123" ] ) ;
211+ } ) ;
212+
213+ it ( "does not extract key in title without keyword" , ( ) => {
214+ const result = extractLinearIssueIdentifiersForCommit ( {
215+ sha : "abc" ,
216+ branchName : null ,
217+ message : "LIN-123: Fix something" ,
218+ } ) ;
219+ expect ( result ) . toEqual ( [ ] ) ;
220+ } ) ;
221+
222+ it . each ( [
223+ "close" ,
224+ "closes" ,
225+ "closed" ,
226+ "closing" ,
227+ "fix" ,
228+ "fixes" ,
229+ "fixed" ,
230+ "fixing" ,
231+ "resolve" ,
232+ "resolves" ,
233+ "resolved" ,
234+ "resolving" ,
235+ "complete" ,
236+ "completes" ,
237+ "completed" ,
238+ "completing" ,
239+ ] ) ( "closing keyword '%s' extracts issue" , ( keyword ) => {
240+ const result = extractLinearIssueIdentifiersForCommit ( {
241+ sha : "abc" ,
242+ branchName : null ,
243+ message : `${ keyword } LIN-100` ,
244+ } ) ;
245+ expect ( result ) . toEqual ( [ "LIN-100" ] ) ;
246+ } ) ;
247+
248+ it . each ( [ "ref" , "refs" , "references" , "part of" , "related to" , "relates to" , "contributes to" , "towards" , "toward" ] ) (
249+ "contributing phrase '%s' extracts issue" ,
250+ ( phrase ) => {
251+ const result = extractLinearIssueIdentifiersForCommit ( {
252+ sha : "abc" ,
253+ branchName : null ,
254+ message : `${ phrase } LIN-200` ,
255+ } ) ;
256+ expect ( result ) . toEqual ( [ "LIN-200" ] ) ;
257+ } ,
258+ ) ;
259+
260+ it ( "supports keyword with colon separator" , ( ) => {
261+ const result = extractLinearIssueIdentifiersForCommit ( {
262+ sha : "abc" ,
263+ branchName : null ,
264+ message : "Closes: LIN-123" ,
265+ } ) ;
266+ expect ( result ) . toEqual ( [ "LIN-123" ] ) ;
267+ } ) ;
268+
269+ it ( "only extracts keys preceded by magic word on same line" , ( ) => {
270+ const result = extractLinearIssueIdentifiersForCommit ( {
271+ sha : "abc" ,
272+ branchName : null ,
273+ message : "See LIN-111, fixes LIN-222" ,
274+ } ) ;
275+ expect ( result ) . toEqual ( [ "LIN-222" ] ) ;
276+ } ) ;
277+
278+ it ( "does not extract from the original bug scenario" , ( ) => {
279+ const result = extractLinearIssueIdentifiersForCommit ( {
280+ sha : "abc" ,
281+ branchName : null ,
282+ message : "Title\nSeparate issue to follow up on that here LIN-60064" ,
283+ } ) ;
284+ expect ( result ) . toEqual ( [ ] ) ;
285+ } ) ;
286+
287+ it ( "branch provides keys independently of message magic words" , ( ) => {
288+ const result = extractLinearIssueIdentifiersForCommit ( {
289+ sha : "abc" ,
290+ branchName : "feature/LIN-100-something" ,
291+ message : "See LIN-200 for details" ,
292+ } ) ;
293+ expect ( result ) . toEqual ( [ "LIN-100" ] ) ;
294+ } ) ;
295+
296+ it ( "is case insensitive for magic words" , ( ) => {
297+ const result = extractLinearIssueIdentifiersForCommit ( {
298+ sha : "abc" ,
299+ branchName : null ,
300+ message : "fIXES LIN-123" ,
301+ } ) ;
302+ expect ( result ) . toEqual ( [ "LIN-123" ] ) ;
303+ } ) ;
304+
305+ it ( "extracts with multi-word phrase 'Part of'" , ( ) => {
306+ const result = extractLinearIssueIdentifiersForCommit ( {
307+ sha : "abc" ,
308+ branchName : null ,
309+ message : "Part of LIN-123" ,
310+ } ) ;
311+ expect ( result ) . toEqual ( [ "LIN-123" ] ) ;
312+ } ) ;
313+
314+ it ( "extracts with multi-word phrase 'Related to'" , ( ) => {
315+ const result = extractLinearIssueIdentifiersForCommit ( {
316+ sha : "abc" ,
317+ branchName : null ,
318+ message : "Related to LIN-456" ,
319+ } ) ;
320+ expect ( result ) . toEqual ( [ "LIN-456" ] ) ;
321+ } ) ;
322+
323+ it ( "extracts issue from Linear URL with slug after magic word" , ( ) => {
324+ const result = extractLinearIssueIdentifiersForCommit ( {
325+ sha : "abc" ,
326+ branchName : null ,
327+ message : "Fixes https://linear.app/myorg/issue/LIN-123/fix-auth" ,
328+ } ) ;
329+ expect ( result ) . toEqual ( [ "LIN-123" ] ) ;
330+ } ) ;
331+
332+ it ( "extracts issue from Linear URL without slug after magic word" , ( ) => {
333+ const result = extractLinearIssueIdentifiersForCommit ( {
334+ sha : "abc" ,
335+ branchName : null ,
336+ message : "Fixes https://linear.app/myorg/issue/LIN-123" ,
337+ } ) ;
338+ expect ( result ) . toEqual ( [ "LIN-123" ] ) ;
339+ } ) ;
340+
341+ it ( "does not extract Linear URL without magic word" , ( ) => {
342+ const result = extractLinearIssueIdentifiersForCommit ( {
343+ sha : "abc" ,
344+ branchName : null ,
345+ message : "See https://linear.app/myorg/issue/LIN-123/fix" ,
346+ } ) ;
347+ expect ( result ) . toEqual ( [ ] ) ;
348+ } ) ;
349+
350+ it ( "extracts mixed Linear URLs and raw IDs after magic word" , ( ) => {
351+ const result = extractLinearIssueIdentifiersForCommit ( {
352+ sha : "abc" ,
353+ branchName : null ,
354+ message : "Fixes https://linear.app/myorg/issue/LIN-123/slug, ENG-456 and LIN-789" ,
355+ } ) ;
356+ expect ( result . sort ( ) ) . toEqual ( [ "ENG-456" , "LIN-123" , "LIN-789" ] ) ;
357+ } ) ;
358+
359+ it ( "extracts issue from http Linear URL after magic word" , ( ) => {
360+ const result = extractLinearIssueIdentifiersForCommit ( {
361+ sha : "abc" ,
362+ branchName : null ,
363+ message : "Fixes http://linear.app/myorg/issue/LIN-123" ,
364+ } ) ;
365+ expect ( result ) . toEqual ( [ "LIN-123" ] ) ;
366+ } ) ;
367+
368+ it ( "extracts issue from Linear URL with trailing slash" , ( ) => {
369+ const result = extractLinearIssueIdentifiersForCommit ( {
370+ sha : "abc" ,
371+ branchName : null ,
372+ message : "Fixes https://linear.app/my-org/issue/LIN-213/" ,
373+ } ) ;
374+ expect ( result ) . toEqual ( [ "LIN-213" ] ) ;
375+ } ) ;
376+
377+ it ( "extracts issue from Linear URL with contributing phrase" , ( ) => {
378+ const result = extractLinearIssueIdentifiersForCommit ( {
379+ sha : "abc" ,
380+ branchName : null ,
381+ message : "Part of https://linear.app/myorg/issue/LIN-213/some-slug" ,
382+ } ) ;
383+ expect ( result ) . toEqual ( [ "LIN-213" ] ) ;
384+ } ) ;
385+ } ) ;
386+
155387describe ( "extractPullRequestNumbersForCommit" , ( ) => {
156388 // Messages that should extract PR numbers
157389 it . each ( [
0 commit comments