Add examples for scaffolder action & improve related tests

Signed-off-by: parmar-abhinav <abhinavparmar147@gmail.com>
This commit is contained in:
parmar-abhinav
2024-04-06 15:47:02 +05:30
parent 51104134d6
commit aa514d12e0
4 changed files with 454 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-gitlab': patch
---
Add examples for `publish:gitlab:merge-request` scaffolder action & improve related tests
@@ -0,0 +1,305 @@
/*
* Copyright 2022 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.
*/
import { createRootLogger } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { ScmIntegrations } from '@backstage/integration';
import { TemplateAction } from '@backstage/plugin-scaffolder-node';
import { createPublishGitlabMergeRequestAction } from './gitlabMergeRequest';
import { createMockDirectory } from '@backstage/backend-test-utils';
import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils';
import { examples } from './gitlabMergeRequest.examples';
import yaml from 'yaml';
// Make sure root logger is initialized ahead of FS mock
createRootLogger();
const mockGitlabClient = {
Namespaces: {
show: jest.fn(),
},
Branches: {
create: jest.fn(),
},
Commits: {
create: jest.fn(),
},
MergeRequests: {
create: jest.fn(async (_: any) => {
return {
default_branch: 'main',
};
}),
},
Projects: {
create: jest.fn(),
show: jest.fn(async (_: any) => {
return {
default_branch: 'main',
};
}),
},
Users: {
current: jest.fn(),
username: jest.fn(async (user: string) => {
const users: string[] = ['John Smith', 'my-assignee'];
if (!users.includes(user)) throw new Error('user does not exist');
else
return [
{
id: 123,
},
];
}),
},
};
jest.mock('@gitbeaker/node', () => ({
Gitlab: class {
constructor() {
return mockGitlabClient;
}
},
}));
describe('createGitLabMergeRequest', () => {
let instance: TemplateAction<any>;
const mockDir = createMockDirectory();
const workspacePath = mockDir.resolve('workspace');
beforeEach(() => {
jest.clearAllMocks();
mockDir.clear();
const config = new ConfigReader({
integrations: {
gitlab: [
{
host: 'gitlab.com',
token: 'token',
apiBaseUrl: 'https://api.gitlab.com',
},
{
host: 'hosted.gitlab.com',
apiBaseUrl: 'https://api.hosted.gitlab.com',
},
],
},
});
const integrations = ScmIntegrations.fromConfig(config);
instance = createPublishGitlabMergeRequestAction({ integrations });
});
describe('createGitLabMergeRequestWithAssignee', () => {
it(`Should ${examples[0].description}`, async () => {
mockDir.setContent({
[workspacePath]: {
source: { 'foo.txt': 'Hello there!' },
irrelevant: { 'bar.txt': 'Nothing to see here' },
},
});
const input = yaml.parse(examples[0].example).steps[0].input;
const ctx = createMockActionContext({ input, workspacePath });
await instance.handler(ctx);
expect(mockGitlabClient.MergeRequests.create).toHaveBeenCalledWith(
'owner/repo',
'new-mr',
'main',
'Create my new MR',
{
description: 'This MR is really good',
removeSourceBranch: false,
assigneeId: 123,
},
);
});
it('assignee is not set when a valid assignee username is not passed in options', async () => {
const input = {
repoUrl: 'gitlab.com?repo=repo&owner=owner',
title: 'Create my new MR',
branchName: 'new-mr',
description: 'This is an important change',
removeSourceBranch: false,
targetPath: 'Subdirectory',
assingnee: 'John Doe',
};
mockDir.setContent({
[workspacePath]: {
source: { 'foo.txt': 'Hello there!' },
irrelevant: { 'bar.txt': 'Nothing to see here' },
},
});
const ctx = createMockActionContext({ input, workspacePath });
await instance.handler(ctx);
expect(mockGitlabClient.MergeRequests.create).toHaveBeenCalledWith(
'owner/repo',
'new-mr',
'main',
'Create my new MR',
{
description: 'This is an important change',
removeSourceBranch: false,
assigneeId: undefined,
},
);
});
});
describe('createGitLabMergeRequestWithRemoveBranch', () => {
it(`Should ${examples[1].description}`, async () => {
const input = yaml.parse(examples[1].example).steps[0].input;
mockDir.setContent({
[workspacePath]: {
source: { 'foo.txt': 'Hello there!' },
irrelevant: { 'bar.txt': 'Nothing to see here' },
},
});
const ctx = createMockActionContext({ input, workspacePath });
await instance.handler(ctx);
expect(mockGitlabClient.MergeRequests.create).toHaveBeenCalledWith(
'owner/repo',
'new-mr',
'main',
'Create my new MR',
{ description: 'This MR is really good', removeSourceBranch: true },
);
});
});
describe('createGitLabMergeRequestWithSpecifiedTargetBranch', () => {
it(`Should ${examples[2].description}`, async () => {
const input = yaml.parse(examples[2].example).steps[0].input;
mockDir.setContent({
[workspacePath]: {
source: { 'foo.txt': 'Hello there!' },
irrelevant: { 'bar.txt': 'Nothing to see here' },
},
});
const ctx = createMockActionContext({ input, workspacePath });
await instance.handler(ctx);
expect(mockGitlabClient.Projects.show).not.toHaveBeenCalled();
expect(mockGitlabClient.Branches.create).toHaveBeenCalledWith(
'owner/repo',
'new-mr',
'test',
);
expect(mockGitlabClient.MergeRequests.create).toHaveBeenCalledWith(
'owner/repo',
'new-mr',
'test',
'Create my new MR',
{ description: 'This MR is really good', removeSourceBranch: false },
);
expect(ctx.output).toHaveBeenCalledWith('targetBranchName', 'test');
});
});
describe('createGitLabMergeRequestWithCommitAction', () => {
it(`Should ${examples[3].description}`, async () => {
const input = yaml.parse(examples[3].example).steps[0].input;
mockDir.setContent({
[workspacePath]: {
source: { 'foo.txt': 'Hello there!' },
irrelevant: { 'bar.txt': 'Nothing to see here' },
},
});
const ctx = createMockActionContext({ input, workspacePath });
await instance.handler(ctx);
expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith(
'owner/repo',
'new-mr',
'Create my new MR',
[
{
action: 'create',
filePath: 'source/foo.txt',
content: 'SGVsbG8gdGhlcmUh',
encoding: 'base64',
execute_filemode: false,
},
],
);
});
it(`Should ${examples[4].description}`, async () => {
const input = yaml.parse(examples[4].example).steps[0].input;
mockDir.setContent({
[workspacePath]: {
source: { 'foo.txt': 'Hello there!' },
irrelevant: { 'bar.txt': 'Nothing to see here' },
},
});
const ctx = createMockActionContext({ input, workspacePath });
await instance.handler(ctx);
expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith(
'owner/repo',
'new-mr',
'Create my new MR',
[
{
action: 'delete',
filePath: 'source/foo.txt',
content: 'SGVsbG8gdGhlcmUh',
encoding: 'base64',
execute_filemode: false,
},
],
);
});
it(`Should ${examples[5].description}`, async () => {
const input = yaml.parse(examples[5].example).steps[0].input;
mockDir.setContent({
[workspacePath]: {
source: { 'foo.txt': 'Hello there!' },
irrelevant: { 'bar.txt': 'Nothing to see here' },
},
});
const ctx = createMockActionContext({ input, workspacePath });
await instance.handler(ctx);
expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith(
'owner/repo',
'new-mr',
'Create my new MR',
[
{
action: 'update',
filePath: 'source/foo.txt',
content: 'SGVsbG8gdGhlcmUh',
encoding: 'base64',
execute_filemode: false,
},
],
);
});
});
});
@@ -0,0 +1,142 @@
/*
* Copyright 2023 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.
*/
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
import yaml from 'yaml';
export const examples: TemplateExample[] = [
{
description: 'Create a merge request with a specific assignee',
example: yaml.stringify({
steps: [
{
id: 'createMergeRequest',
action: 'publish:gitlab:merge-request',
name: 'Create a Merge Request',
input: {
repoUrl: 'gitlab.com?repo=repo&owner=owner',
title: 'Create my new MR',
description: 'This MR is really good',
sourcePath: './path/to/my/changes',
branchName: 'new-mr',
assignee: 'my-assignee',
},
},
],
}),
},
{
description:
'Create a merge request with removal of source branch after merge',
example: yaml.stringify({
steps: [
{
id: 'createMergeRequest',
action: 'publish:gitlab:merge-request',
name: 'Create a Merge Request',
input: {
repoUrl: 'gitlab.com?repo=repo&owner=owner',
title: 'Create my new MR',
description: 'This MR is really good',
sourcePath: './path/to/my/changes',
branchName: 'new-mr',
removeSourceBranch: true,
},
},
],
}),
},
{
description: 'Create a merge request with a target branch',
example: yaml.stringify({
steps: [
{
id: 'createMergeRequest',
action: 'publish:gitlab:merge-request',
name: 'Create a Merge Request',
input: {
repoUrl: 'gitlab.com?repo=repo&owner=owner',
title: 'Create my new MR',
description: 'This MR is really good',
sourcePath: './path/to/my/changes',
branchName: 'new-mr',
targetBranchName: 'test',
targetPath: 'Subdirectory',
},
},
],
}),
},
{
description: 'Create a merge request with a commit action as create',
example: yaml.stringify({
steps: [
{
id: 'createMergeRequest',
action: 'publish:gitlab:merge-request',
name: 'Create a Merge Request',
input: {
repoUrl: 'gitlab.com?repo=repo&owner=owner',
title: 'Create my new MR',
branchName: 'new-mr',
description: 'MR description',
commitAction: 'create',
targetPath: 'source',
},
},
],
}),
},
{
description: 'Create a merge request with a commit action as delete',
example: yaml.stringify({
steps: [
{
id: 'createMergeRequest',
action: 'publish:gitlab:merge-request',
name: 'Create a Merge Request',
input: {
repoUrl: 'gitlab.com?repo=repo&owner=owner',
title: 'Create my new MR',
branchName: 'new-mr',
description: 'MR description',
commitAction: 'delete',
targetPath: 'source',
},
},
],
}),
},
{
description: 'Create a merge request with a commit action as update',
example: yaml.stringify({
steps: [
{
id: 'createMergeRequest',
action: 'publish:gitlab:merge-request',
name: 'Create a Merge Request',
input: {
repoUrl: 'gitlab.com?repo=repo&owner=owner',
title: 'Create my new MR',
branchName: 'new-mr',
description: 'MR description',
commitAction: 'update',
targetPath: 'source',
},
},
],
}),
},
];
@@ -25,6 +25,7 @@ import { ScmIntegrationRegistry } from '@backstage/integration';
import { InputError } from '@backstage/errors';
import { resolveSafeChildPath } from '@backstage/backend-common';
import { createGitlabApi } from './helpers';
import { examples } from './gitlabMergeRequest.examples';
/**
* Create a new action that creates a gitlab merge request.
@@ -52,6 +53,7 @@ export const createPublishGitlabMergeRequestAction = (options: {
assignee?: string;
}>({
id: 'publish:gitlab:merge-request',
examples,
schema: {
input: {
required: ['repoUrl', 'branchName'],