@tinymce/beehive-flow
Version:
A CLI tool implementing the beehive flow git branching process
230 lines (196 loc) • 8.09 kB
text/typescript
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<void> => {
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
}
}
});
});
});
});
});