33 */
44
55/* eslint-disable @typescript-eslint/naming-convention */
6+
7+ // Mock fs
8+ jest . mock ( 'fs' ) ;
9+
10+ // Mock @actions /core for input handling and logging in tests
11+ jest . mock ( '@actions/core' , ( ) => ( {
12+ getInput : jest . fn ( ) ,
13+ info : jest . fn ( ) ,
14+ startGroup : jest . fn ( ) ,
15+ endGroup : jest . fn ( ) ,
16+ setFailed : jest . fn ( ) ,
17+ } ) ) ;
18+
619import * as fns from 'date-fns' ;
720import { vol } from 'memfs' ;
821import path from 'path' ;
@@ -12,23 +25,21 @@ import type {InternalOctokit} from '@github/libs/GithubUtils';
1225import GithubUtils from '@github/libs/GithubUtils' ;
1326import GitUtils from '@github/libs/GitUtils' ;
1427
28+ import * as core from '@actions/core' ;
29+
30+ const mockGetInput = core . getInput as jest . MockedFunction < typeof core . getInput > ;
31+
1532type Arguments = {
1633 issue_number ?: number ;
1734 labels ?: string ;
1835} ;
1936
2037const PATH_TO_PACKAGE_JSON = path . resolve ( __dirname , '../../package.json' ) ;
2138
22- jest . mock ( 'fs' ) ;
23- const mockGetInput = jest . fn ( ) ;
2439const mockListIssues = jest . fn ( ) ;
2540const mockGetPullRequestsDeployedBetween = jest . fn ( ) ;
2641
2742beforeAll ( ( ) => {
28- // Mock core module
29- jest . mock ( '@actions/core' , ( ) => ( {
30- getInput : mockGetInput ,
31- } ) ) ;
3243
3344 // Mock octokit module
3445 const mockOctokit = {
@@ -66,6 +77,7 @@ beforeAll(() => {
6677
6778 // Mock GitUtils
6879 GitUtils . getPullRequestsDeployedBetween = mockGetPullRequestsDeployedBetween ;
80+ mockGetInput . mockImplementation ( ( arg ) => ( arg === 'GITHUB_TOKEN' ? 'fake_token' : '' ) ) ;
6981
7082 vol . reset ( ) ;
7183 vol . fromJSON ( {
@@ -122,7 +134,7 @@ const basePRList = [
122134const baseIssueList = [ `https://github.com/${ process . env . GITHUB_REPOSITORY } /issues/11` , `https://github.com/${ process . env . GITHUB_REPOSITORY } /issues/12` ] ;
123135// eslint-disable-next-line max-len
124136const baseExpectedOutput = ( version = '1.0.2-1' ) =>
125- `**Release Version:** \`${ version } \`\r\n**Compare Changes:** https://github.com/${ process . env . GITHUB_REPOSITORY } /compare/production...staging\r\n\r\n**This release contains changes from the following pull requests:**\r\n` ;
137+ `**Release Version:** \`${ version } \`\r\n**Compare Changes:** https://github.com/${ process . env . GITHUB_REPOSITORY } /compare/production...staging\r\n\r\n> 💡 **Deployer FYI:** This checklist was generated using a new process. PR listfrom original method and detail logging can be found in the most recent [deploy workflow](https://github.com/Expensify/App/actions/workflows/deploy.yml) labeled \`staging\`, in the \`createChecklist\` action. Please tag @Julesssss with any issues.\r\n\r\n\r\n **This release contains changes from the following pull requests:**\r\n` ;
126138const openCheckbox = '- [ ] ' ;
127139const closedCheckbox = '- [x] ' ;
128140const deployerVerificationsHeader = '**Deployer verifications:**' ;
@@ -166,12 +178,6 @@ describe('createOrUpdateStagingDeployCash', () => {
166178 vol . fromJSON ( {
167179 [ PATH_TO_PACKAGE_JSON ] : JSON . stringify ( { version : '1.0.2-1' } ) ,
168180 } ) ;
169- mockGetInput . mockImplementation ( ( arg ) => {
170- if ( arg !== 'GITHUB_TOKEN' ) {
171- return ;
172- }
173- return 'fake_token' ;
174- } ) ;
175181
176182 mockGetPullRequestsDeployedBetween . mockImplementation ( ( fromRef , toRef ) => {
177183 if ( fromRef === '1.0.1-0-staging' && toRef === '1.0.2-1-staging' ) {
@@ -259,12 +265,6 @@ describe('createOrUpdateStagingDeployCash', () => {
259265 vol . fromJSON ( {
260266 [ PATH_TO_PACKAGE_JSON ] : JSON . stringify ( { version : '1.0.2-2' } ) ,
261267 } ) ;
262- mockGetInput . mockImplementation ( ( arg ) => {
263- if ( arg !== 'GITHUB_TOKEN' ) {
264- return ;
265- }
266- return 'fake_token' ;
267- } ) ;
268268
269269 // New pull requests to add to open StagingDeployCash
270270 const newPullRequests = [ 9 , 10 ] ;
@@ -338,12 +338,6 @@ describe('createOrUpdateStagingDeployCash', () => {
338338 vol . fromJSON ( {
339339 [ PATH_TO_PACKAGE_JSON ] : JSON . stringify ( { version : '1.0.2-1' } ) ,
340340 } ) ;
341- mockGetInput . mockImplementation ( ( arg ) => {
342- if ( arg !== 'GITHUB_TOKEN' ) {
343- return ;
344- }
345- return 'fake_token' ;
346- } ) ;
347341 mockGetPullRequestsDeployedBetween . mockImplementation ( ( fromRef , toRef ) => {
348342 if ( fromRef === '1.0.1-0-staging' && toRef === '1.0.2-1-staging' ) {
349343 return [ ...baseNewPullRequests ] ;
@@ -406,4 +400,65 @@ describe('createOrUpdateStagingDeployCash', () => {
406400 } ) ;
407401 } ) ;
408402 } ) ;
403+
404+ describe ( 'cherry-pick filtering' , ( ) => {
405+ test ( 'filters out PRs that were already included in previous checklist' , async ( ) => {
406+ vol . reset ( ) ;
407+ vol . fromJSON ( {
408+ [ PATH_TO_PACKAGE_JSON ] : JSON . stringify ( { version : '1.0.3-0' } ) ,
409+ } ) ;
410+
411+ mockGetInput . mockImplementation ( ( arg ) => arg === 'GITHUB_TOKEN' ? 'fake_token' : '' ) ;
412+ mockGetPullRequestsDeployedBetween . mockImplementation ( ( fromRef , toRef ) => {
413+ if ( fromRef === '1.0.2-1-staging' && toRef === '1.0.3-0-staging' ) {
414+ return [ 6 , 8 , 10 , 11 ] ;
415+ }
416+ return [ ] ;
417+ } ) ;
418+
419+ // Mock previous checklist containing PRs 6,8
420+ const mockGetStagingDeployCashData = jest . spyOn ( GithubUtils , 'getStagingDeployCashData' ) ;
421+ mockGetStagingDeployCashData . mockImplementation ( ( ) => ( {
422+ title : 'Previous Checklist' ,
423+ url : `https://github.com/${ process . env . GITHUB_REPOSITORY } /issues/29` ,
424+ number : 29 ,
425+ labels : [ LABELS . STAGING_DEPLOY_CASH ] ,
426+ PRList : [
427+ { url : `https://github.com/${ process . env . GITHUB_REPOSITORY } /pull/6` , number : 6 , isVerified : true } ,
428+ { url : `https://github.com/${ process . env . GITHUB_REPOSITORY } /pull/8` , number : 8 , isVerified : true } ,
429+ ] ,
430+ deployBlockers : [ ] ,
431+ internalQAPRList : [ ] ,
432+ isTimingDashboardChecked : true ,
433+ isFirebaseChecked : true ,
434+ isGHStatusChecked : true ,
435+ version : '1.0.2-1' ,
436+ tag : '1.0.2-1-staging' ,
437+ } ) ) ;
438+
439+ // Mock list of issues to return a closed previous checklist
440+ mockListIssues . mockImplementation ( ( args : Arguments ) => {
441+ if ( args . labels === CONST . LABELS . STAGING_DEPLOY ) {
442+ return Promise . resolve ( {
443+ data : [ {
444+ number : 29 ,
445+ state : 'closed' ,
446+ labels : [ LABELS . STAGING_DEPLOY_CASH ] ,
447+ } ]
448+ } ) ;
449+ }
450+ return Promise . resolve ( { data : [ ] } ) ;
451+ } ) ;
452+
453+ const result = await run ( ) ;
454+
455+ // Verify that only new PRs (10, 11) are included, not the previously included ones (6, 8)
456+ expect ( result ?. body ) . toContain ( 'https://github.com/Expensify/App/pull/10' ) ;
457+ expect ( result ?. body ) . toContain ( 'https://github.com/Expensify/App/pull/11' ) ;
458+ expect ( result ?. body ) . not . toContain ( 'https://github.com/Expensify/App/pull/6' ) ;
459+ expect ( result ?. body ) . not . toContain ( 'https://github.com/Expensify/App/pull/8' ) ;
460+
461+ mockGetStagingDeployCashData . mockRestore ( ) ;
462+ } ) ;
463+ } ) ;
409464} ) ;
0 commit comments