From 169b215c7cb7665d19f4b55f784b14a418d55850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Schmitz=20von=20H=C3=BClst?= Date: Fri, 13 Nov 2020 11:09:36 +0100 Subject: [PATCH] more tests --- src/github.ts | 2 +- src/main.ts | 12 +- src/utils.ts | 4 +- tests/helper.test.ts | 17 +- tests/main.test.ts | 438 ++++++++++++++++++++++++++++++++++++------- tests/utils.test.ts | 86 ++++----- 6 files changed, 435 insertions(+), 124 deletions(-) diff --git a/src/github.ts b/src/github.ts index 55ad44c1..4425a3a6 100644 --- a/src/github.ts +++ b/src/github.ts @@ -4,7 +4,7 @@ import { Octokit } from "@octokit/rest"; let octokitSingleton; -function getOctokitSingleton() { +export function getOctokitSingleton() { if (octokitSingleton) { return octokitSingleton; } diff --git a/src/main.ts b/src/main.ts index 04eb5178..20010d45 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,7 +8,7 @@ import { getLatestPrereleaseTag, getLatestTag, getValidTags, - mapCustomReleaseTypes, + mapCustomReleaseRules, } from "./utils"; import { createTag } from "./github"; @@ -22,11 +22,11 @@ export default async () => { const appendToPreReleaseTag = core.getInput("append_to_pre_release_tag"); const createAnnotatedTag = !!core.getInput("create_annotated_tag"); const dryRun = core.getInput("dry_run"); - const customReleaseTypes = core.getInput("custom_release_types"); + const customReleaseRules = core.getInput("custom_release_rules"); - let mappedReleaseTypes; - if (customReleaseTypes) { - mappedReleaseTypes = mapCustomReleaseTypes(customReleaseTypes); + let mappedReleaseRules; + if (customReleaseRules) { + mappedReleaseRules = mapCustomReleaseRules(customReleaseRules); } const { GITHUB_REF, GITHUB_SHA } = process.env; @@ -85,7 +85,7 @@ export default async () => { core.setOutput("previous_tag", previousTag.version); const bump = await analyzeCommits( - { releaseTypes: mappedReleaseTypes }, + { releaseRules: mappedReleaseRules }, { commits, logger: { log: console.info.bind(console) } } ); diff --git a/src/utils.ts b/src/utils.ts index 767e1181..3d3bc5d2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -57,11 +57,11 @@ export function getLatestPrereleaseTag( .find((tag) => tag.name.match(identifier)); } -export function mapCustomReleaseTypes( +export function mapCustomReleaseRules( customReleaseTypes: string ) { return customReleaseTypes - .split(';') + .split(',') .map(part => { const custom = part.split(':'); if (custom.length !== 2) { diff --git a/tests/helper.test.ts b/tests/helper.test.ts index ecf8d050..6c785440 100644 --- a/tests/helper.test.ts +++ b/tests/helper.test.ts @@ -2,21 +2,30 @@ import yaml from 'js-yaml'; import fs from 'fs'; import path from 'path'; -export function setInput(key, value) { +export function setBranch(branch: string) { + process.env['GITHUB_REF'] = `refs/heads/${branch}`; +} + +export function setCommitSha(sha: string) { + process.env['GITHUB_SHA'] = sha; +} + +export function setInput(key: string, value: string) { process.env[`INPUT_${key.toUpperCase()}`] = value; } -export function setInputs(map) { +export function setInputs(map: Object) { Object.keys(map).forEach(key => setInput(key, map[key])); } -export function parseDefaultInputs() { +export function loadDefaultInputs() { const actionYaml = fs.readFileSync(path.join(process.cwd(), 'action.yml')); const actionJson = yaml.safeLoad(actionYaml); - return Object + const defaultInputs = Object .keys(actionJson.inputs) .filter(key => actionJson.inputs[key].default) .reduce((obj, key) => ({ ...obj, [key]: actionJson.inputs[key].default }), {}); + setInputs(defaultInputs); } // Don't know how to have this file only for test but not have 'tsc' complain. So I made it a test file... diff --git a/tests/main.test.ts b/tests/main.test.ts index f0f302dc..51c2fe04 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -2,91 +2,391 @@ import main from '../src/main'; import * as utils from '../src/utils'; import * as github from '../src/github'; import * as core from '@actions/core'; -import { setInputs, parseDefaultInputs, setInput } from "./helper.test"; +import { loadDefaultInputs, setBranch, setCommitSha, setInput } from './helper.test'; -const mockCreateTag = jest.spyOn(github, 'createTag').mockImplementation(async () => { -}); +jest.spyOn(core, 'debug').mockImplementation(() => {}); +jest.spyOn(core, 'info').mockImplementation(() => {}); +jest.spyOn(console, 'info').mockImplementation(() => {}); + +const mockCreateTag = jest.spyOn(github, 'createTag').mockResolvedValue(undefined); const mockSetFailed = jest.spyOn(core, 'setFailed'); +const mockSetOutput = jest.spyOn(core, 'setOutput').mockImplementation(() => {}); -describe('main tests', () => { - const mockGetCommits = (commits) => jest.spyOn(utils, 'getCommits').mockImplementation(async (sha) => { - return commits; - }); - const mockGetValidTags = (validTags) => jest.spyOn(utils, 'getValidTags').mockImplementation(async () => { - return validTags; - }); - +describe('github-tag-action', () => { beforeEach(() => { jest.clearAllMocks(); - process.env['GITHUB_REF'] = 'refs/heads/master'; - process.env['GITHUB_SHA'] = 'GITHUB_SHA'; - setInputs(parseDefaultInputs()); + setBranch('master'); + setCommitSha('79e0ea271c26aa152beef77c3275ff7b8f8d8274'); + loadDefaultInputs(); }); - it('does create initial tag', async () => { - /* - * Given - */ - const mockValidTags = mockGetValidTags([]); - const commits = [ - { message: 'fix: this is my first fix' }, - ] - const mockCommits = mockGetCommits(commits); + describe('special cases', () => { + it('does create initial tag', async () => { + /* + * Given + */ + const commits = [{ message: 'fix: this is my first fix' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); - /* - * When - */ - await main(); + const validTags = []; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); - /* - * Then - */ - expect(mockValidTags).toHaveBeenCalled(); - expect(mockCommits).toHaveBeenCalledWith('HEAD'); - expect(mockCreateTag).toHaveBeenCalledWith('v0.0.1', expect.any(Boolean), expect.any(String)); - expect(mockSetFailed).not.toBeCalled(); + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockCreateTag).toHaveBeenCalledWith('v0.0.1', expect.any(Boolean), expect.any(String)); + expect(mockSetFailed).not.toBeCalled(); + }); + + it('does create patch tag without commits', async () => { + /* + * Given + */ + const commits = []; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); + + const validTags = []; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockCreateTag).toHaveBeenCalledWith('v0.0.1', expect.any(Boolean), expect.any(String)); + expect(mockSetFailed).not.toBeCalled(); + }); + + it('does not create tag without commits and default_bump set to false', async () => { + /* + * Given + */ + setInput('default_bump', 'false'); + const commits = []; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); + + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockCreateTag).not.toBeCalled(); + expect(mockSetFailed).not.toBeCalled(); + }); + + it('does create tag using custom release types', async () => { + /* + * Given + */ + setInput('custom_release_rules', 'james:patch,bond:major'); + const commits = [{ message: 'james: is the new cool guy' }, { message: 'bond: is his last name' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); + + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockCreateTag).toHaveBeenCalledWith('v2.0.0', expect.any(Boolean), expect.any(String)); + expect(mockSetFailed).not.toBeCalled(); + }); }); - it('does create patch tag without commits', async () => { - /* - * Given - */ - const mockValidTags = mockGetValidTags([{ name: 'v1.2.3', commit: { sha: '012345' } }]); - const mockCommits = mockGetCommits([]); + describe('release branches', () => { + beforeEach(() => { + jest.clearAllMocks(); + setBranch('release'); + setInput('release_branches', 'release'); + }); - /* - * When - */ - await main(); + it('does create patch tag', async () => { + /* + * Given + */ + const commits = [{ message: 'fix: this is my first fix' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); - /* - * Then - */ - expect(mockValidTags).toHaveBeenCalled(); - expect(mockCommits).toHaveBeenCalledWith('012345'); - expect(mockCreateTag).toHaveBeenCalledWith('v1.2.4', expect.any(Boolean), expect.any(String)); - expect(mockSetFailed).not.toBeCalled(); + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockCreateTag).toHaveBeenCalledWith('v1.2.4', expect.any(Boolean), expect.any(String)); + expect(mockSetFailed).not.toBeCalled(); + }); + + it('does create minor tag', async () => { + /* + * Given + */ + const commits = [{ message: 'feat: this is my first feature' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); + + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockCreateTag).toHaveBeenCalledWith('v1.3.0', expect.any(Boolean), expect.any(String)); + expect(mockSetFailed).not.toBeCalled(); + }); + + it('does create major tag', async () => { + /* + * Given + */ + const commits = [{ message: 'my commit message\nBREAKING CHANGE:\nthis is a breaking change' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); + + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockCreateTag).toHaveBeenCalledWith('v2.0.0', expect.any(Boolean), expect.any(String)); + expect(mockSetFailed).not.toBeCalled(); + }); }); - it('does not create tag without commits and default_bump set to false', async () => { - /* - * Given - */ - setInput('default_bump', 'false'); - const mockValidTags = mockGetValidTags([{ name: 'v1.2.3', commit: { sha: '012345' } }]); - const mockCommits = mockGetCommits([]); + describe('pre-release branches', () => { + beforeEach(() => { + jest.clearAllMocks(); + setBranch('prerelease'); + setInput('pre_release_branches', 'prerelease'); + }); - /* - * When - */ - await main(); + it('does create prepatch tag', async () => { + /* + * Given + */ + const commits = [{ message: 'fix: this is my first fix' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); - /* - * Then - */ - expect(mockValidTags).toHaveBeenCalled(); - expect(mockCommits).toHaveBeenCalledWith('012345'); - expect(mockCreateTag).not.toBeCalled(); - expect(mockSetFailed).not.toBeCalled(); + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockCreateTag).toHaveBeenCalledWith('v1.2.4-prerelease.0', expect.any(Boolean), expect.any(String)); + expect(mockSetFailed).not.toBeCalled(); + }); + + it('does create preminor tag', async () => { + /* + * Given + */ + const commits = [{ message: 'feat: this is my first feature' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); + + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockCreateTag).toHaveBeenCalledWith('v1.3.0-prerelease.0', expect.any(Boolean), expect.any(String)); + expect(mockSetFailed).not.toBeCalled(); + }); + + it('does create premajor tag', async () => { + /* + * Given + */ + const commits = [{ message: 'my commit message\nBREAKING CHANGE:\nthis is a breaking change' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); + + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockCreateTag).toHaveBeenCalledWith('v2.0.0-prerelease.0', expect.any(Boolean), expect.any(String)); + expect(mockSetFailed).not.toBeCalled(); + }); + }); + + describe('other branches', () => { + beforeEach(() => { + jest.clearAllMocks(); + setBranch('development'); + setInput('pre_release_branches', 'prerelease'); + setInput('release_branches', 'release'); + }); + + it('does output patch tag', async () => { + /* + * Given + */ + const commits = [{ message: 'fix: this is my first fix' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); + + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockSetOutput).toHaveBeenCalledWith('new_version', '1.2.4'); + expect(mockCreateTag).not.toBeCalled(); + expect(mockSetFailed).not.toBeCalled(); + }); + + it('does output minor tag', async () => { + /* + * Given + */ + const commits = [{ message: 'feat: this is my first feature' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); + + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockSetOutput).toHaveBeenCalledWith('new_version', '1.3.0'); + expect(mockCreateTag).not.toBeCalled(); + expect(mockSetFailed).not.toBeCalled(); + }); + + it('does output major tag', async () => { + /* + * Given + */ + const commits = [{ message: 'my commit message\nBREAKING CHANGE:\nthis is a breaking change' }]; + jest + .spyOn(utils, 'getCommits') + .mockImplementation(async (sha) => commits); + + const validTags = [{ name: 'v1.2.3', commit: { sha: '012345' } }]; + jest + .spyOn(utils, 'getValidTags') + .mockImplementation(async () => validTags); + + /* + * When + */ + await main(); + + /* + * Then + */ + expect(mockSetOutput).toHaveBeenCalledWith('new_version', '2.0.0'); + expect(mockCreateTag).not.toBeCalled(); + expect(mockSetFailed).not.toBeCalled(); + }); }); }); diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 80f82f00..ac6f0e64 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -1,52 +1,11 @@ import * as utils from '../src/utils'; -import * as core from '@actions/core'; import { getValidTags } from '../src/utils'; +import * as core from '@actions/core'; import * as github from '../src/github'; jest.spyOn(core, 'debug').mockImplementation(() => {}); describe('utils', () => { - it('maps custom release types', () => { - /* - * Given - */ - const customReleasesString = 'james:preminor;bond:premajor'; - - /* - * When - */ - const mappedReleases = utils.mapCustomReleaseTypes(customReleasesString); - - /* - * Then - */ - expect(mappedReleases) - .toEqual([ - { type: 'james', release: 'preminor' }, - { type: 'bond', release: 'premajor' } - ]); - }); - - it('filters out invalid custom release types', () => { - /* - * Given - */ - const customReleasesString = 'james:pre-release;bond:premajor'; - - /* - * When - */ - const mappedReleases = utils.mapCustomReleaseTypes(customReleasesString); - - /* - * Then - */ - expect(mappedReleases) - .toEqual([ - { type: 'bond', release: 'premajor' } - ]); - }); - it('extracts branch from ref', () => { /* * Given @@ -109,4 +68,47 @@ describe('utils', () => { expect(mockListTags).toHaveBeenCalled(); expect(validTags[0]).toEqual({ name: '1.2.4-prerelease.2' }); }); + + describe('custom release types', () => { + it('maps custom release types', () => { + /* + * Given + */ + const customReleasesString = 'james:preminor,bond:premajor'; + + /* + * When + */ + const mappedReleases = utils.mapCustomReleaseRules(customReleasesString); + + /* + * Then + */ + expect(mappedReleases) + .toEqual([ + { type: 'james', release: 'preminor' }, + { type: 'bond', release: 'premajor' }, + ]); + }); + + it('filters out invalid custom release types', () => { + /* + * Given + */ + const customReleasesString = 'james:pre-release,bond:premajor'; + + /* + * When + */ + const mappedReleases = utils.mapCustomReleaseRules(customReleasesString); + + /* + * Then + */ + expect(mappedReleases) + .toEqual([ + { type: 'bond', release: 'premajor' }, + ]); + }); + }); });