diff --git a/plugins/scaffolder-backend/src/scaffolder/__mocks__/octokit/index.ts b/plugins/scaffolder-backend/src/scaffolder/__mocks__/octokit/index.ts deleted file mode 100644 index de320ef498..0000000000 --- a/plugins/scaffolder-backend/src/scaffolder/__mocks__/octokit/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export const mockGithubClient = { - rest: { - actions: { - createWorkflowDispatch: jest.fn(), - }, - repos: { - createInOrg: jest.fn(), - createForAuthenticatedUser: jest.fn(), - createWebhook: jest.fn(), - addCollaborator: jest.fn(), - replaceAllTopics: jest.fn(), - }, - users: { - getByUsername: jest.fn(), - }, - teams: { - addOrUpdateRepoPermissionsInOrg: jest.fn(), - }, - }, -}; - -export class Octokit { - constructor() { - return mockGithubClient; - } -} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.test.ts index 7850163e9d..0782ea40cd 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.test.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -jest.mock('octokit'); import { TemplateAction } from '../../types'; import { createGithubActionsDispatchAction } from './githubActionsDispatch'; @@ -27,6 +26,21 @@ import { ConfigReader } from '@backstage/config'; import { getVoidLogger } from '@backstage/backend-common'; import { PassThrough } from 'stream'; +const mockOctokit = { + rest: { + actions: { + createWorkflowDispatch: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + describe('github:actions:dispatch', () => { const config = new ConfigReader({ integrations: { @@ -54,8 +68,6 @@ describe('github:actions:dispatch', () => { createTemporaryDirectory: jest.fn(), }; - const { mockGithubClient } = require('octokit'); - beforeEach(() => { jest.resetAllMocks(); githubCredentialsProvider = @@ -67,7 +79,7 @@ describe('github:actions:dispatch', () => { }); it('should call the githubApis for creating WorkflowDispatch without an input object', async () => { - mockGithubClient.rest.actions.createWorkflowDispatch.mockResolvedValue({ + mockOctokit.rest.actions.createWorkflowDispatch.mockResolvedValue({ data: { foo: 'bar', }, @@ -82,7 +94,7 @@ describe('github:actions:dispatch', () => { await action.handler(ctx); expect( - mockGithubClient.rest.actions.createWorkflowDispatch, + mockOctokit.rest.actions.createWorkflowDispatch, ).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', @@ -92,7 +104,7 @@ describe('github:actions:dispatch', () => { }); it('should call the githubApis for creating WorkflowDispatch with an input object', async () => { - mockGithubClient.rest.actions.createWorkflowDispatch.mockResolvedValue({ + mockOctokit.rest.actions.createWorkflowDispatch.mockResolvedValue({ data: { foo: 'bar', }, @@ -108,7 +120,7 @@ describe('github:actions:dispatch', () => { await action.handler(ctx); expect( - mockGithubClient.rest.actions.createWorkflowDispatch, + mockOctokit.rest.actions.createWorkflowDispatch, ).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubWebhook.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubWebhook.test.ts index b80691fdcc..0798b04a93 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubWebhook.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubWebhook.test.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -jest.mock('octokit'); - import { createGithubWebhookAction } from './githubWebhook'; import { ScmIntegrations, @@ -27,6 +25,21 @@ import { getVoidLogger } from '@backstage/backend-common'; import { PassThrough } from 'stream'; import { TemplateAction } from '../..'; +const mockOctokit = { + rest: { + repos: { + createWebhook: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + describe('github:repository:webhook:create', () => { const config = new ConfigReader({ integrations: { @@ -65,8 +78,6 @@ describe('github:repository:webhook:create', () => { createTemporaryDirectory: jest.fn(), }; - const { mockGithubClient } = require('octokit'); - it('should call the githubApi for creating repository Webhook', async () => { const repoUrl = 'github.com?repo=repo&owner=owner'; const webhookUrl = 'https://example.com/payload'; @@ -75,7 +86,7 @@ describe('github:repository:webhook:create', () => { }); await action.handler(ctx); - expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', events: ['push'], @@ -97,7 +108,7 @@ describe('github:repository:webhook:create', () => { }, }); - expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', events: ['push'], @@ -118,7 +129,7 @@ describe('github:repository:webhook:create', () => { }, }); - expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', events: ['push', 'pull_request'], @@ -139,7 +150,7 @@ describe('github:repository:webhook:create', () => { }, }); - expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', events: ['push'], @@ -160,7 +171,7 @@ describe('github:repository:webhook:create', () => { }, }); - expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', events: ['push'], @@ -181,7 +192,7 @@ describe('github:repository:webhook:create', () => { }, }); - expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', events: ['push'], @@ -202,7 +213,7 @@ describe('github:repository:webhook:create', () => { }, }); - expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', events: ['push'], diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts index ec564e93dc..4e835657a3 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts @@ -17,7 +17,6 @@ import { TemplateAction } from '../../types'; jest.mock('../helpers'); -jest.mock('octokit'); import { createPublishGithubAction } from './github'; import { @@ -34,6 +33,30 @@ import { } from '../helpers'; import { when } from 'jest-when'; +const mockOctokit = { + rest: { + users: { + getByUsername: jest.fn(), + }, + repos: { + addCollaborator: jest.fn(), + createInOrg: jest.fn(), + createForAuthenticatedUser: jest.fn(), + replaceAllTopics: jest.fn(), + }, + teams: { + addOrUpdateRepoPermissionsInOrg: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + describe('publish:github', () => { const config = new ConfigReader({ integrations: { @@ -62,8 +85,6 @@ describe('publish:github', () => { createTemporaryDirectory: jest.fn(), }; - const { mockGithubClient } = require('octokit'); - beforeEach(() => { jest.resetAllMocks(); githubCredentialsProvider = @@ -76,14 +97,14 @@ describe('publish:github', () => { }); it('should call the githubApis with the correct values for createInOrg', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'Organization' }, }); - mockGithubClient.rest.repos.createInOrg.mockResolvedValue({ data: {} }); + mockOctokit.rest.repos.createInOrg.mockResolvedValue({ data: {} }); await action.handler(mockContext); - expect(mockGithubClient.rest.repos.createInOrg).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({ description: 'description', name: 'repo', org: 'owner', @@ -102,7 +123,7 @@ describe('publish:github', () => { repoVisibility: 'public', }, }); - expect(mockGithubClient.rest.repos.createInOrg).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({ description: 'description', name: 'repo', org: 'owner', @@ -116,17 +137,17 @@ describe('publish:github', () => { }); it('should call the githubApis with the correct values for createForAuthenticatedUser', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: {}, }); await action.handler(mockContext); expect( - mockGithubClient.rest.repos.createForAuthenticatedUser, + mockOctokit.rest.repos.createForAuthenticatedUser, ).toHaveBeenCalledWith({ description: 'description', name: 'repo', @@ -145,7 +166,7 @@ describe('publish:github', () => { }, }); expect( - mockGithubClient.rest.repos.createForAuthenticatedUser, + mockOctokit.rest.repos.createForAuthenticatedUser, ).toHaveBeenCalledWith({ description: 'description', name: 'repo', @@ -158,11 +179,11 @@ describe('publish:github', () => { }); it('should call initRepoAndPush with the correct values', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', @@ -182,11 +203,11 @@ describe('publish:github', () => { }); it('should call initRepoAndPush with the correct defaultBranch main', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', @@ -235,11 +256,11 @@ describe('publish:github', () => { githubCredentialsProvider, }); - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', @@ -279,11 +300,11 @@ describe('publish:github', () => { githubCredentialsProvider, }); - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', @@ -304,11 +325,11 @@ describe('publish:github', () => { }); it('should add access for the team when it starts with the owner', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', @@ -318,7 +339,7 @@ describe('publish:github', () => { await action.handler(mockContext); expect( - mockGithubClient.rest.teams.addOrUpdateRepoPermissionsInOrg, + mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg, ).toHaveBeenCalledWith({ org: 'owner', team_slug: 'blam', @@ -329,11 +350,11 @@ describe('publish:github', () => { }); it('should add outside collaborators when provided', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', @@ -348,7 +369,7 @@ describe('publish:github', () => { }, }); - expect(mockGithubClient.rest.repos.addCollaborator).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.addCollaborator).toHaveBeenCalledWith({ username: 'outsidecollaborator', owner: 'owner', repo: 'repo', @@ -357,11 +378,11 @@ describe('publish:github', () => { }); it('should add multiple collaborators when provided', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', @@ -392,7 +413,7 @@ describe('publish:github', () => { }; expect( - mockGithubClient.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[1], + mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[1], ).toEqual([ { ...commonProperties, @@ -402,7 +423,7 @@ describe('publish:github', () => { ]); expect( - mockGithubClient.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2], + mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2], ).toEqual([ { ...commonProperties, @@ -413,18 +434,18 @@ describe('publish:github', () => { }); it('should ignore failures when adding multiple collaborators', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', }, }); - when(mockGithubClient.rest.teams.addOrUpdateRepoPermissionsInOrg) + when(mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg) .calledWith({ org: 'owner', owner: 'owner', @@ -452,7 +473,7 @@ describe('publish:github', () => { }); expect( - mockGithubClient.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2], + mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2], ).toEqual([ { org: 'owner', @@ -465,18 +486,18 @@ describe('publish:github', () => { }); it('should add topics when provided', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', }, }); - mockGithubClient.rest.repos.replaceAllTopics.mockResolvedValue({ + mockOctokit.rest.repos.replaceAllTopics.mockResolvedValue({ data: { names: ['node.js'], }, @@ -490,7 +511,7 @@ describe('publish:github', () => { }, }); - expect(mockGithubClient.rest.repos.replaceAllTopics).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.replaceAllTopics).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', names: ['node.js'], @@ -498,18 +519,18 @@ describe('publish:github', () => { }); it('should lowercase topics when provided', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', }, }); - mockGithubClient.rest.repos.replaceAllTopics.mockResolvedValue({ + mockOctokit.rest.repos.replaceAllTopics.mockResolvedValue({ data: { names: ['backstage'], }, @@ -523,7 +544,7 @@ describe('publish:github', () => { }, }); - expect(mockGithubClient.rest.repos.replaceAllTopics).toHaveBeenCalledWith({ + expect(mockOctokit.rest.repos.replaceAllTopics).toHaveBeenCalledWith({ owner: 'owner', repo: 'repo', names: ['backstage'], @@ -531,11 +552,11 @@ describe('publish:github', () => { }); it('should call output with the remoteUrl and the repoContentsUrl', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', @@ -555,11 +576,11 @@ describe('publish:github', () => { }); it('should use main as default branch', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { clone_url: 'https://github.com/clone/url.git', html_url: 'https://github.com/html/url', @@ -585,11 +606,11 @@ describe('publish:github', () => { }); it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of requireCodeOwnerReviews', async () => { - mockGithubClient.rest.users.getByUsername.mockResolvedValue({ + mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, }); - mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { name: 'repository', }, @@ -599,7 +620,7 @@ describe('publish:github', () => { expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ owner: 'owner', - client: mockGithubClient, + client: mockOctokit, repoName: 'repository', logger: mockContext.logger, defaultBranch: 'master', @@ -616,7 +637,7 @@ describe('publish:github', () => { expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ owner: 'owner', - client: mockGithubClient, + client: mockOctokit, repoName: 'repository', logger: mockContext.logger, defaultBranch: 'master', @@ -633,7 +654,7 @@ describe('publish:github', () => { expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ owner: 'owner', - client: mockGithubClient, + client: mockOctokit, repoName: 'repository', logger: mockContext.logger, defaultBranch: 'master',