From b172b359b1d0bc31d97cda778c26fa8a9caf2f2d Mon Sep 17 00:00:00 2001 From: David Sanders Date: Thu, 3 Sep 2026 14:47:44 -0700 Subject: [PATCH 1/2] perf: make getSupportedBranches faster Assisted-by: GPT-5.6 Sol --- src/chromium-handler.ts | 16 +-- src/node-handler.ts | 11 +- src/types.ts | 13 +- src/utils/get-supported-branches.ts | 62 ++++++-- src/utils/get-target-branch-labels.ts | 10 +- src/utils/roll.ts | 4 +- tests/handlers.spec.ts | 140 ++----------------- tests/utils/get-supported-branches.spec.ts | 57 ++++++++ tests/utils/get-target-branch-labels.spec.ts | 37 +++-- 9 files changed, 152 insertions(+), 198 deletions(-) create mode 100644 tests/utils/get-supported-branches.spec.ts diff --git a/src/chromium-handler.ts b/src/chromium-handler.ts index 1db9489..06c3140 100644 --- a/src/chromium-handler.ts +++ b/src/chromium-handler.ts @@ -7,11 +7,9 @@ import { getSupportedBranches } from './utils/get-supported-branches.js'; import { getContent } from './utils/github-utils.js'; import { getOctokit } from './utils/octokit.js'; import { roll } from './utils/roll.js'; -import { ReposGetBranchResponseItem, ReposListBranchesResponseItem } from './types.js'; +import { Branch } from './types.js'; import { Octokit } from '@octokit/rest'; -type BranchItem = ReposGetBranchResponseItem | ReposListBranchesResponseItem; - // The outcome of a main branch roll: the Chromium version main is currently on // (its landed DEPS version, not the version the open roll PR targets) and the // release branches the roll PR covers with `target/N-x-y` labels. @@ -22,7 +20,7 @@ interface MainRollResult { async function rollReleaseBranch( github: Octokit, - branch: BranchItem, + branch: Branch, mainRoll?: MainRollResult | null, ) { const d = debug(`roller/chromium:rollReleaseBranch('${branch.name}')`); @@ -171,15 +169,7 @@ export async function handleChromiumCheck(target?: string): Promise { } } else { d('Fetching release branches for electron/electron'); - const branches: ReposListBranchesResponseItem[] = await github.paginate( - github.repos.listBranches.endpoint.merge({ - ...REPOS.electron, - protected: true, - }), - ); - - const supported = getSupportedBranches(branches); - const releaseBranches = branches.filter((branch) => supported.includes(branch.name)); + const releaseBranches = await getSupportedBranches(github); d(`Found ${releaseBranches.length} release branches`); // Roll main first, so that the release branches its roll PR covers with diff --git a/src/node-handler.ts b/src/node-handler.ts index f66a40d..b043404 100644 --- a/src/node-handler.ts +++ b/src/node-handler.ts @@ -5,7 +5,6 @@ import { MAIN_BRANCH, REPOS, ROLL_TARGETS } from './constants.js'; import { getContent } from './utils/github-utils.js'; import { getOctokit } from './utils/octokit.js'; import { roll } from './utils/roll.js'; -import { ReposListBranchesResponseItem } from './types.js'; import { getSupportedBranches } from './utils/get-supported-branches.js'; import { getLatestLTSVersion } from './utils/get-nodejs-lts.js'; @@ -15,15 +14,7 @@ export async function handleNodeCheck(target?: string): Promise { const github = await getOctokit(); d('Fetching release branches for electron/electron'); - const branches: ReposListBranchesResponseItem[] = await github.paginate( - github.repos.listBranches.endpoint.merge({ - ...REPOS.electron, - protected: true, - }), - ); - - const supported = getSupportedBranches(branches, 3); - const releaseBranches = branches.filter((branch) => supported.includes(branch.name)); + const releaseBranches = await getSupportedBranches(github, 3); d(`Found ${releaseBranches.length} release branches`); let failed = false; diff --git a/src/types.ts b/src/types.ts index 050c07f..21e036b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,8 +1,11 @@ -import { RestEndpointMethodTypes } from '@octokit/rest'; +import type { RestEndpointMethodTypes } from '@octokit/rest'; export type PullsGetResponseItem = RestEndpointMethodTypes['pulls']['get']['response']['data']; export type PullsListResponseItem = RestEndpointMethodTypes['pulls']['list']['response']['data'][0]; -export type ReposListBranchesResponseItem = - RestEndpointMethodTypes['repos']['listBranches']['response']['data'][0]; -export type ReposGetBranchResponseItem = - RestEndpointMethodTypes['repos']['getBranch']['response']['data']; + +export interface Branch { + name: string; + commit: { + sha: string; + }; +} diff --git a/src/utils/get-supported-branches.ts b/src/utils/get-supported-branches.ts index a19bbb2..26f4737 100644 --- a/src/utils/get-supported-branches.ts +++ b/src/utils/get-supported-branches.ts @@ -1,20 +1,58 @@ -// Get array of currently supported branches -export function getSupportedBranches( - branches: { name: string }[], +import type { Octokit } from '@octokit/rest'; +import type { Branch } from '../types.js'; + +/** + * Get array of currently supported branches + */ +export async function getSupportedBranches( + github: Octokit, numSupportedVersions = 4, -): string[] { - const releaseBranches = branches +): Promise { + if (numSupportedVersions < 1) { + throw new Error('numSupportedVersions must be greater than 0'); + } + + if (numSupportedVersions > 100) { + throw new Error('numSupportedVersions must be less than or equal to 100'); + } + + const { repository } = await github.graphql<{ + repository: { + refs: { nodes: { name: string; target: { oid: string } }[] }; + }; + }>( + `query ($owner: String!, $repo: String!, $branchQuery: String!) { + repository(owner: $owner, name: $repo) { + refs(refPrefix: "refs/heads/", query: $branchQuery, first: 100) { + nodes { + name + target { + ... on Commit { + oid + } + } + } + } + } + }`, + { owner: 'electron', repo: 'electron', branchQuery: '-x-y' }, + ); + + const releaseBranches = repository.refs.nodes .filter((branch) => { const releasePattern = /^(\d)+-(?:(?:[0-9]+-x$)|(?:x+-y$))$/; return releasePattern.test(branch.name); }) - .map((b) => b.name); + .map((branch) => ({ + name: branch.name, + commit: { sha: branch.target.oid }, + })); - const filtered: Record = {}; + const filtered: Record = {}; releaseBranches .sort((a, b) => { - const aParts = a.split('-'); - const bParts = b.split('-'); + const aParts = a.name.split('-'); + const bParts = b.name.split('-'); for (let i = 0; i < aParts.length; i += 1) { if (aParts[i] === bParts[i]) continue; return parseInt(aParts[i], 10) - parseInt(bParts[i], 10); @@ -22,9 +60,11 @@ export function getSupportedBranches( return 0; }) .forEach((branch) => { - return (filtered[branch.split('-')[0]] = branch); + return (filtered[branch.name.split('-')[0]] = branch); }); const values = Object.values(filtered); - return values.sort((a, b) => parseInt(a, 10) - parseInt(b, 10)).slice(-numSupportedVersions); + return values + .sort((a, b) => parseInt(a.name.split('-')[0], 10) - parseInt(b.name.split('-')[0], 10)) + .slice(-numSupportedVersions); } diff --git a/src/utils/get-target-branch-labels.ts b/src/utils/get-target-branch-labels.ts index 4e7c00a..131b79f 100644 --- a/src/utils/get-target-branch-labels.ts +++ b/src/utils/get-target-branch-labels.ts @@ -1,7 +1,5 @@ import { Octokit } from '@octokit/rest'; -import { REPOS } from '../constants.js'; -import { ReposListBranchesResponseItem } from '../types.js'; import { getSupportedBranches } from './get-supported-branches.js'; export const ELECTRON_RELEASE_SCHEDULE_URL = 'https://releases.electronjs.org/schedule.json'; @@ -29,13 +27,9 @@ export async function getBranchesTrackedByMain( } const schedule = (await response.json()) as ReleaseScheduleEntry[]; - const branches: ReposListBranchesResponseItem[] = await octokit.paginate( - octokit.repos.listBranches.endpoint.merge({ - ...REPOS.electron, - protected: true, - }), + const supported = await getSupportedBranches(octokit).then((branches) => + branches.map((branch) => branch.name), ); - const supported = getSupportedBranches(branches); return schedule .filter( diff --git a/src/utils/roll.ts b/src/utils/roll.ts index c9bbfde..bd28237 100644 --- a/src/utils/roll.ts +++ b/src/utils/roll.ts @@ -11,7 +11,7 @@ import { ROLLER_BOT_LOGIN, RollTarget, } from '../constants.js'; -import { ReposListBranchesResponseItem, PullsListResponseItem } from '../types.js'; +import { Branch, PullsListResponseItem } from '../types.js'; import { getOctokit } from './octokit.js'; import { getPRText } from './pr-text.js'; import { updateDepsFile } from './update-deps.js'; @@ -21,7 +21,7 @@ import { getBranchesTrackedByMain } from './get-target-branch-labels.js'; interface RollParams { rollTarget: RollTarget; - electronBranch: ReposListBranchesResponseItem; + electronBranch: Branch; targetVersion: string; prNumber?: number; previousVersion?: string; diff --git a/tests/handlers.spec.ts b/tests/handlers.spec.ts index 4eb080f..eb90f59 100644 --- a/tests/handlers.spec.ts +++ b/tests/handlers.spec.ts @@ -1,7 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { MAIN_BRANCH, REPOS, ROLL_TARGETS } from '../src/constants.js'; -import { getSupportedBranches } from '../src/utils/get-supported-branches.js'; import { handleNodeCheck } from '../src/node-handler.js'; import { handleChromiumCheck } from '../src/chromium-handler.js'; import { getChromiumReleases } from '../src/utils/get-chromium-tags.js'; @@ -9,25 +8,21 @@ import { getContent } from '../src/utils/github-utils.js'; import { getOctokit } from '../src/utils/octokit.js'; import { roll } from '../src/utils/roll.js'; import { getLatestLTSVersion } from '../src/utils/get-nodejs-lts.js'; +import { getSupportedBranches } from '../src/utils/get-supported-branches.js'; vi.mock('../src/utils/get-chromium-tags.js'); vi.mock('../src/utils/github-utils.js'); vi.mock('../src/utils/octokit.js'); vi.mock('../src/utils/roll.js'); vi.mock('../src/utils/get-nodejs-lts.js'); +vi.mock('../src/utils/get-supported-branches.js'); describe('handleChromiumCheck()', () => { let mockOctokit: any; beforeEach(() => { mockOctokit = { - paginate: vi.fn(), repos: { - listBranches: { - endpoint: { - merge: vi.fn(), - }, - }, getContent: vi.fn(), get: vi.fn(), getBranch: vi.fn().mockReturnValue({ @@ -41,97 +36,20 @@ describe('handleChromiumCheck()', () => { }, }; vi.mocked(getOctokit).mockReturnValue(mockOctokit); + vi.mocked(getSupportedBranches) + .mockReset() + .mockResolvedValue([{ name: '4-0-x', commit: { sha: '1234' } }]); vi.mocked(roll).mockReset().mockResolvedValue([]); }); describe('release branches', () => { beforeEach(() => { - mockOctokit.paginate.mockReturnValue([ - { - name: '4-0-x', - commit: { - sha: '1234', - }, - }, - ]); - vi.mocked(getContent).mockResolvedValue({ content: `${ROLL_TARGETS.chromium.depsKey}':\n '1.0.0.0',`, sha: '1234', }); }); - it('properly fetches supported versions of Electron to roll against', async () => { - vi.mocked(getChromiumReleases).mockResolvedValue(['1.1.0.0', '1.2.0.0', '2.1.0.0']); - - mockOctokit.paginate.mockReturnValue([ - { - name: '10-x-y', - commit: { - sha: '1234', - }, - }, - { - name: '9-x-y', - commit: { - sha: '1234', - }, - }, - { - name: '8-x-y', - commit: { - sha: '1234', - }, - }, - { - name: '7-1-x', - commit: { - sha: '1234', - }, - }, - { - name: '7-0-x', - commit: { - sha: '1234', - }, - }, - { - name: '6-1-x', - commit: { - sha: '1234', - }, - }, - { - name: '6-0-x', - commit: { - sha: '1234', - }, - }, - { - name: '5-0-x', - commit: { - sha: '1234', - }, - }, - { - name: MAIN_BRANCH, - commit: { - sha: '1234', - }, - }, - ]); - - const branches: { name: string }[] = await mockOctokit.paginate( - mockOctokit.repos.listBranches.endpoint.merge({ - ...REPOS.electron, - protected: true, - }), - ); - - const supported = getSupportedBranches(branches); - expect(supported).toEqual(['7-1-x', '8-x-y', '9-x-y', '10-x-y']); - }); - it('rolls with latest versions from release tags', async () => { vi.mocked(getChromiumReleases).mockResolvedValue(['1.1.0.0', '1.2.0.0']); @@ -298,14 +216,7 @@ describe('handleChromiumCheck()', () => { describe('main branch', () => { beforeEach(() => { - mockOctokit.paginate.mockReturnValue([ - { - name: MAIN_BRANCH, - commit: { - sha: '1234', - }, - }, - ]); + vi.mocked(getSupportedBranches).mockResolvedValue([]); vi.mocked(getContent).mockResolvedValue({ content: `${ROLL_TARGETS.chromium.depsKey}':\n '1.1.0.0',`, @@ -336,15 +247,6 @@ describe('handleChromiumCheck()', () => { }); it('throws error if roll() process failed', async () => { - mockOctokit.paginate.mockReturnValue([ - { - name: '4-0-x', - commit: { - sha: '1234', - }, - }, - ]); - vi.mocked(getContent).mockResolvedValue({ content: `${ROLL_TARGETS.chromium.depsKey}':\n '1.0.0.0',`, sha: '1234', @@ -366,7 +268,6 @@ describe('handleNodeCheck()', () => { beforeEach(() => { mockOctokit = { - paginate: vi.fn().mockReturnValue([]), repos: { getBranch: vi.fn().mockReturnValue({ data: { @@ -393,38 +294,19 @@ describe('handleNodeCheck()', () => { ], }), getContent: vi.fn(), - listBranches: { - endpoint: { - merge: vi.fn(), - }, - }, }, }; vi.mocked(getOctokit).mockReturnValue(mockOctokit); + vi.mocked(getSupportedBranches).mockReset().mockResolvedValue([]); }); it('rolls even major versions of Node.js with latest minor/patch update', async () => { vi.mocked(getLatestLTSVersion).mockResolvedValue('14.0.0'); - mockOctokit.paginate.mockReturnValue([ - { - name: '4-x-y', - commit: { - sha: '1234', - }, - }, - { - name: '5-x-y', - commit: { - sha: '2345', - }, - }, - { - name: '6-x-y', - commit: { - sha: '3456', - }, - }, + vi.mocked(getSupportedBranches).mockResolvedValue([ + { name: '4-x-y', commit: { sha: '1234' } }, + { name: '5-x-y', commit: { sha: '2345' } }, + { name: '6-x-y', commit: { sha: '3456' } }, ]); vi.mocked(getContent).mockResolvedValue({ diff --git a/tests/utils/get-supported-branches.spec.ts b/tests/utils/get-supported-branches.spec.ts new file mode 100644 index 0000000..d136786 --- /dev/null +++ b/tests/utils/get-supported-branches.spec.ts @@ -0,0 +1,57 @@ +import type { Octokit } from '@octokit/rest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { getSupportedBranches } from '../../src/utils/get-supported-branches.js'; + +describe('getSupportedBranches', () => { + let graphql: ReturnType; + let octokit: Octokit; + + beforeEach(() => { + graphql = vi.fn(); + octokit = { graphql } as unknown as Octokit; + }); + + it('returns the most recent release branches', async () => { + graphql.mockResolvedValueOnce({ + repository: { + refs: { + nodes: ['30-x-y', '31-x-y', '32-x-y', '33-x-y', '34-x-y'].map((name) => ({ + name, + target: { oid: `${name}-sha` }, + })), + }, + }, + }); + + await expect(getSupportedBranches(octokit)).resolves.toEqual( + ['31-x-y', '32-x-y', '33-x-y', '34-x-y'].map((name) => ({ + name, + commit: { sha: `${name}-sha` }, + })), + ); + expect(graphql).toHaveBeenCalledOnce(); + expect(graphql).toHaveBeenCalledWith(expect.any(String), { + owner: 'electron', + repo: 'electron', + branchQuery: '-x-y', + }); + }); + + it('filters invalid branches and honors the requested release count', async () => { + graphql.mockResolvedValueOnce({ + repository: { + refs: { + nodes: ['32-x-y', 'not-x-y-valid', '33-x-y', '34-xx-y', '34-x-y'].map((name) => ({ + name, + target: { oid: `${name}-sha` }, + })), + }, + }, + }); + + const branches = await getSupportedBranches(octokit, 2); + + expect(branches.map((branch) => branch.name)).toEqual(['33-x-y', '34-x-y']); + }); +}); diff --git a/tests/utils/get-target-branch-labels.spec.ts b/tests/utils/get-target-branch-labels.spec.ts index 824b928..e49e2e0 100644 --- a/tests/utils/get-target-branch-labels.spec.ts +++ b/tests/utils/get-target-branch-labels.spec.ts @@ -8,10 +8,11 @@ import { ELECTRON_RELEASE_SCHEDULE_URL, getBranchesTrackedByMain, } from '../../src/utils/get-target-branch-labels.js'; +import { getSupportedBranches } from '../../src/utils/get-supported-branches.js'; -describe('getBranchesTrackedByMain', () => { - let mockOctokit: any; +vi.mock('../../src/utils/get-supported-branches.js'); +describe('getBranchesTrackedByMain', () => { const fixture = fs.readFileSync( path.join(import.meta.dirname, '../fixtures/electron-release-schedule.json'), 'utf8', @@ -20,19 +21,15 @@ describe('getBranchesTrackedByMain', () => { beforeEach(() => { nock.cleanAll(); - mockOctokit = { - paginate: vi.fn().mockResolvedValue( + vi.mocked(getSupportedBranches) + .mockReset() + .mockResolvedValue( // 41-x-y is EOL and outside the supported window of 4. - ['41-x-y', '42-x-y', '43-x-y', '44-x-y', '45-x-y'].map((name) => ({ name })), - ), - repos: { - listBranches: { - endpoint: { - merge: vi.fn(), - }, - }, - }, - }; + ['42-x-y', '43-x-y', '44-x-y', '45-x-y'].map((name) => ({ + name, + commit: { sha: `${name}-sha` }, + })), + ); }); it('returns supported branches scheduled for >= the rolled major', async () => { @@ -40,19 +37,19 @@ describe('getBranchesTrackedByMain', () => { // 45-x-y ships Chromium 156, 44-x-y ships 152 - rolling to 154 only // targets 45-x-y. - await expect(getBranchesTrackedByMain(mockOctokit, 154)).resolves.toEqual(['45-x-y']); + await expect(getBranchesTrackedByMain({} as any, 154)).resolves.toEqual(['45-x-y']); }); it('includes a branch whose scheduled major equals the rolled major', async () => { nock(url.origin).get(url.pathname).reply(200, fixture); - await expect(getBranchesTrackedByMain(mockOctokit, 156)).resolves.toEqual(['45-x-y']); + await expect(getBranchesTrackedByMain({} as any, 156)).resolves.toEqual(['45-x-y']); }); it('returns every matching supported branch', async () => { nock(url.origin).get(url.pathname).reply(200, fixture); - await expect(getBranchesTrackedByMain(mockOctokit, 150)).resolves.toEqual([ + await expect(getBranchesTrackedByMain({} as any, 150)).resolves.toEqual([ '43-x-y', '44-x-y', '45-x-y', @@ -62,7 +59,7 @@ describe('getBranchesTrackedByMain', () => { it('returns no branches if the rolled major is newer than every scheduled version', async () => { nock(url.origin).get(url.pathname).reply(200, fixture); - await expect(getBranchesTrackedByMain(mockOctokit, 157)).resolves.toEqual([]); + await expect(getBranchesTrackedByMain({} as any, 157)).resolves.toEqual([]); }); it('ignores schedule entries for unsupported branches', async () => { @@ -70,7 +67,7 @@ describe('getBranchesTrackedByMain', () => { // 41-x-y ships Chromium 146 but is no longer supported, and main is not a // release branch - neither should be returned. - await expect(getBranchesTrackedByMain(mockOctokit, 140)).resolves.toEqual([ + await expect(getBranchesTrackedByMain({} as any, 140)).resolves.toEqual([ '42-x-y', '43-x-y', '44-x-y', @@ -81,7 +78,7 @@ describe('getBranchesTrackedByMain', () => { it('throws if the schedule fetch fails', async () => { nock(url.origin).get(url.pathname).reply(500); - await expect(getBranchesTrackedByMain(mockOctokit, 154)).rejects.toThrowError( + await expect(getBranchesTrackedByMain({} as any, 154)).rejects.toThrowError( 'Failed to fetch Electron release schedule: 500', ); }); From 6dba230dc38f4ec6677b4c22c96f287cca660aef Mon Sep 17 00:00:00 2001 From: David Sanders Date: Thu, 3 Sep 2026 15:35:32 -0700 Subject: [PATCH 2/2] chore: manually paginate GraphQL query Assisted-by: GPT-5.6 Sol --- src/utils/get-supported-branches.ts | 57 +++++++++++++++------- tests/utils/get-supported-branches.spec.ts | 37 ++++++++++++++ 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/utils/get-supported-branches.ts b/src/utils/get-supported-branches.ts index 26f4737..a1886e8 100644 --- a/src/utils/get-supported-branches.ts +++ b/src/utils/get-supported-branches.ts @@ -16,29 +16,50 @@ export async function getSupportedBranches( throw new Error('numSupportedVersions must be less than or equal to 100'); } - const { repository } = await github.graphql<{ - repository: { - refs: { nodes: { name: string; target: { oid: string } }[] }; - }; - }>( - `query ($owner: String!, $repo: String!, $branchQuery: String!) { - repository(owner: $owner, name: $repo) { - refs(refPrefix: "refs/heads/", query: $branchQuery, first: 100) { - nodes { - name - target { - ... on Commit { - oid + const branchRefs: { name: string; target: { oid: string } }[] = []; + let cursor: string | null = null; + + while (true) { + const { repository } = await github.graphql<{ + repository: { + refs: { + nodes: { name: string; target: { oid: string } }[]; + pageInfo: { hasNextPage: boolean; endCursor: string | null }; + }; + }; + }>( + `query ($owner: String!, $repo: String!, $branchQuery: String!, $cursor: String) { + repository(owner: $owner, name: $repo) { + refs(refPrefix: "refs/heads/", query: $branchQuery, first: 100, after: $cursor) { + nodes { + name + target { + ... on Commit { + oid + } } } + pageInfo { + hasNextPage + endCursor + } } } - } - }`, - { owner: 'electron', repo: 'electron', branchQuery: '-x-y' }, - ); + }`, + { owner: 'electron', repo: 'electron', branchQuery: '-x-y', cursor }, + ); + + branchRefs.push(...repository.refs.nodes); + + if (!repository.refs.pageInfo.hasNextPage) break; + + cursor = repository.refs.pageInfo.endCursor; + if (cursor === null) { + throw new Error('GitHub returned no cursor for the next page of branches'); + } + } - const releaseBranches = repository.refs.nodes + const releaseBranches = branchRefs .filter((branch) => { const releasePattern = /^(\d)+-(?:(?:[0-9]+-x$)|(?:x+-y$))$/; return releasePattern.test(branch.name); diff --git a/tests/utils/get-supported-branches.spec.ts b/tests/utils/get-supported-branches.spec.ts index d136786..9d6daee 100644 --- a/tests/utils/get-supported-branches.spec.ts +++ b/tests/utils/get-supported-branches.spec.ts @@ -20,6 +20,7 @@ describe('getSupportedBranches', () => { name, target: { oid: `${name}-sha` }, })), + pageInfo: { hasNextPage: false, endCursor: null }, }, }, }); @@ -35,6 +36,41 @@ describe('getSupportedBranches', () => { owner: 'electron', repo: 'electron', branchQuery: '-x-y', + cursor: null, + }); + }); + + it('fetches every page of release branches', async () => { + graphql + .mockResolvedValueOnce({ + repository: { + refs: { + nodes: ['30-x-y', '31-x-y', '32-x-y', '33-x-y'].map((name) => ({ + name, + target: { oid: `${name}-sha` }, + })), + pageInfo: { hasNextPage: true, endCursor: 'page-2' }, + }, + }, + }) + .mockResolvedValueOnce({ + repository: { + refs: { + nodes: [{ name: '34-x-y', target: { oid: '34-x-y-sha' } }], + pageInfo: { hasNextPage: false, endCursor: null }, + }, + }, + }); + + const branches = await getSupportedBranches(octokit); + + expect(branches.map((branch) => branch.name)).toEqual(['31-x-y', '32-x-y', '33-x-y', '34-x-y']); + expect(graphql).toHaveBeenCalledTimes(2); + expect(graphql).toHaveBeenLastCalledWith(expect.any(String), { + owner: 'electron', + repo: 'electron', + branchQuery: '-x-y', + cursor: 'page-2', }); }); @@ -46,6 +82,7 @@ describe('getSupportedBranches', () => { name, target: { oid: `${name}-sha` }, })), + pageInfo: { hasNextPage: false, endCursor: null }, }, }, });