import * as path from 'path'; import * as fs from 'fs'; import { describe, it } from 'mocha'; import fc from 'fast-check'; import * as chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; import { BranchDetails, BranchState, BranchType, getBranchDetails, getReleaseBranchName, versionFromReleaseBranch } from '../../../main/ts/core/BranchLogic'; import * as Git from '../../../main/ts/utils/Git'; import * as PackageJson from '../../../main/ts/core/PackageJson'; import * as Version from '../../../main/ts/core/Version'; import * as Files from '../../../main/ts/utils/Files'; import * as TestUtils from '../commands/TestUtils'; type PackageJson = PackageJson.PackageJson; const assert = chai.use(chaiAsPromised).assert; const newRepo = async (branchName: string) => { const hub = await Git.initInTempFolder(true); const gitUrl = hub.dir; const { dir, git } = await Git.cloneInTempFolder(gitUrl); await Git.checkoutNewBranch(git, branchName); return { gitUrl, dir, git }; }; describe('BranchLogic', () => { describe('releaseBranchName', () => { it('makes branch names (manual cases)', () => { assert.equal(getReleaseBranchName({ major: 1, minor: 2 }), 'release/1.2'); assert.equal(getReleaseBranchName({ major: 0, minor: 0 }), 'release/0.0'); }); it('makes branch names (property)', () => { fc.assert(fc.property(fc.nat(1000), fc.nat(1000), (major, minor) => { assert.equal(getReleaseBranchName({ major, minor }), `release/${major}.${minor}`); })); }); }); describe('versionFromReleaseBranch', () => { it('parses versions', async () => { await fc.assert(fc.asyncProperty(fc.nat(1000), fc.nat(1000), async (major, minor) => { await assert.becomes( versionFromReleaseBranch(`release/${major}.${minor}`), { major, minor } ); })); }); }); describe('getBranchDetails', () => { const setup = async (branchName: string, sVersion: string) => { const version = await Version.parseVersion(sVersion); const { gitUrl, dir, git } = await newRepo(branchName); const packageJsonFile = path.join(dir, 'package.json'); const packageJson: PackageJson = { name: '@beehive-test/dummypackage', version, other: {} }; await PackageJson.writePackageJsonFile(packageJsonFile, packageJson); await git.add(packageJsonFile); await git.commit('commit'); return { gitUrl, dir, packageJsonFile, version, packageJson, changelogFile: '', changelogFormat: 'none' as const }; }; it('fails if dir is not a git repo', async () => { const dir = await Files.tempFolder(); await assert.isRejected(getBranchDetails(dir)); }); it('fails if dir is not at the root of a git repo', async () => { const { dir } = await Git.initInTempFolder(); const subbie = path.join(dir, 'subbie'); fs.mkdirSync(subbie); await assert.isRejected(getBranchDetails(subbie)); }); const check = async (currentBranch: string, sVersion: string, branchType: BranchType, branchState: BranchState): Promise => { const { dir, packageJsonFile, version, packageJson, changelogFile, changelogFormat } = await setup(currentBranch, sVersion); const expected: BranchDetails = { rootModule: { packageJsonFile, packageJson, changelogFile, changelogFormat }, version, currentBranch, branchType, branchState, workspacesEnabled: false, modules: {} }; await assert.becomes(getBranchDetails(dir), expected); }; it('passes for main branch with rc version', () => check('main', '0.6.0-rc', BranchType.Main, BranchState.ReleaseCandidate) ); it('passes for timestamped version on main branch', () => check('main', '0.6.0-rc.20020202020202.293la9', BranchType.Main, BranchState.ReleaseCandidate) ); it('fails for main branch with wrong minor version', async () => { const { dir } = await setup('main', '0.6.1-alpha'); await assert.isRejected(getBranchDetails(dir)); }); it('fails for main branch with alpha version', async () => { const { dir } = await setup('main', '0.6.0-alpha'); await assert.isRejected(getBranchDetails(dir)); }); it('passes for main branch with release version', async () => check('main', '0.6.0', BranchType.Main, BranchState.ReleaseReady) ); it('fails for main branch with non-rc prerelease version', async () => { const { dir } = await setup('main', '0.6.0+9nesste123.frog'); await assert.isRejected(getBranchDetails(dir)); }); it('detects valid feature branch', () => check('feature/BLAH-1234', '0.6.0-alpha', BranchType.Feature, BranchState.Feature) ); it('detects valid dependabot branch', () => check('dependabot/npm_and_yarn/package-1.0.0', '0.6.0-alpha', BranchType.Dependabot, BranchState.Feature) ); it('detects valid spike branch', () => check('spike/BLAH-1234', '0.6.0-alpha', BranchType.Spike, BranchState.Spike) ); it('passes for timestamped version on feature branch', () => check('feature/98qenaoects', '0.6.0-alpha.20020202020202.293la9', BranchType.Feature, BranchState.Feature) ); it('detects valid rc state', () => check('release/8.1298', '8.1298.7-rc', BranchType.Release, BranchState.ReleaseCandidate) ); it('passes for timestamped version in rc state', () => check('release/9189382.88', '9189382.88.12-rc.20020202020202.293la9', BranchType.Release, BranchState.ReleaseCandidate) ); it('detects valid release state', () => check('release/8.1298', '8.1298.7', BranchType.Release, BranchState.ReleaseReady) ); context('workspaces', () => { it('reads modules', async () => { const { dir, git } = await newRepo('main'); const rootPj = await PackageJson.decode({ name: 'rootPackage', version: '1.2.3', private: true, workspaces: [ 'modules/*' ] }); const rootPjFile = path.join(dir, 'package.json'); await PackageJson.writePackageJsonFile(rootPjFile, rootPj); const makeModule = async (name: string) => { const module1Dir = path.join(dir, 'modules', name); fs.mkdirSync(module1Dir, { recursive: true }); const pj = await PackageJson.decode({ name, version: '2.71.1' }); const pjFile = path.join(module1Dir, 'package.json'); const changelogFile = path.join(module1Dir, 'CHANGELOG.md'); await PackageJson.writePackageJsonFile(pjFile, pj); await TestUtils.writeChangelog(module1Dir); return { pj, pjFile, changelogFile, changelogFormat: 'keepachangelog' as const }; }; const m1 = await makeModule('module1'); const m2 = await makeModule('module2'); await git.add([ rootPjFile, m1.pjFile, m2.pjFile ]); await git.commit('initial'); await Git.push(git); const actual = await getBranchDetails(dir); assert.deepEqual(actual, { currentBranch: 'main', branchState: BranchState.ReleaseReady, branchType: BranchType.Main, version: await Version.parseVersion('1.2.3'), workspacesEnabled: true, rootModule: { packageJson: rootPj, packageJsonFile: rootPjFile, changelogFile: '', changelogFormat: 'none' }, modules: { module1: { packageJson: m1.pj, packageJsonFile: m1.pjFile, changelogFile: m1.changelogFile, changelogFormat: m1.changelogFormat }, module2: { packageJson: m2.pj, packageJsonFile: m2.pjFile, changelogFile: m2.changelogFile, changelogFormat: m2.changelogFormat } } }); }); }); }); });