From c63177cc2d35a22a331a3bf6d9c77688b2478589 Mon Sep 17 00:00:00 2001 From: kjarmicki Date: Tue, 28 Nov 2017 17:26:12 +0100 Subject: [PATCH 1/6] test(roc-plugin-repo): Add semver utilities tests --- .../__mocks__/conventional-changelog.js | 10 + .../src/semver/__mocks__/roc.js | 8 + .../roc-plugin-repo/src/semver/utils.test.js | 307 ++++++++++++++++++ 3 files changed, 325 insertions(+) create mode 100644 extensions/roc-plugin-repo/src/semver/__mocks__/conventional-changelog.js create mode 100644 extensions/roc-plugin-repo/src/semver/__mocks__/roc.js create mode 100644 extensions/roc-plugin-repo/src/semver/utils.test.js diff --git a/extensions/roc-plugin-repo/src/semver/__mocks__/conventional-changelog.js b/extensions/roc-plugin-repo/src/semver/__mocks__/conventional-changelog.js new file mode 100644 index 0000000..bf41692 --- /dev/null +++ b/extensions/roc-plugin-repo/src/semver/__mocks__/conventional-changelog.js @@ -0,0 +1,10 @@ +const conventionalChangelog = require.requireActual('conventional-changelog'); + +export default function(options, context, gitRawCommitsOpts) { + // lock down "to" commits to make results predictable + const to = '84fe778a213bea560ca30bb8f085d8d6652eb455'; + return conventionalChangelog(options, context, { + ...gitRawCommitsOpts, + to + }); +} diff --git a/extensions/roc-plugin-repo/src/semver/__mocks__/roc.js b/extensions/roc-plugin-repo/src/semver/__mocks__/roc.js new file mode 100644 index 0000000..40777c5 --- /dev/null +++ b/extensions/roc-plugin-repo/src/semver/__mocks__/roc.js @@ -0,0 +1,8 @@ +const executeSync = command => { + if(command.includes('commit-hash-some-directory')) { + return 'some-directory/some-folder/roc-plugin-hadron-collider'; + } + return ''; +}; + +export { executeSync }; diff --git a/extensions/roc-plugin-repo/src/semver/utils.test.js b/extensions/roc-plugin-repo/src/semver/utils.test.js new file mode 100644 index 0000000..601b2d6 --- /dev/null +++ b/extensions/roc-plugin-repo/src/semver/utils.test.js @@ -0,0 +1,307 @@ +jest.mock('conventional-changelog'); +jest.mock('roc'); + +import { + getLatestCommitsSinceRelease, + conventionalChangelogOptions, + getNextVersions, + createVersionsDoesNotMatch, + getMultiScopes, + getAutoScopes +} from './utils'; + +describe('Semver utilities', () => { + + describe('to get latest commits since release', () => { + it('should return information for current monorepo', async () => { + const projects = []; + const from = ''; + const isMonorepo = true; + + const result = await getLatestCommitsSinceRelease('angular', from, projects, isMonorepo); + + expect(Object.keys(result).sort()) + .toEqual(['roc-plugin-repo', 'roc-plugin-repo-react', 'roc-plugin-repo-roc']); + expect(result['roc-plugin-repo'].release.subject).toBe('0.0.27'); + expect(result['roc-plugin-repo-react'].release.subject).toBe('0.0.5'); + expect(result['roc-plugin-repo-roc'].release.subject).toBe('0.0.1'); + }); + }); + + describe('to generate Conventional Changelog options', () => { + it('should set correct scope when commit is a revert', done => { + const project = { + path: '/' + }; + const isMonorepo = false; + + const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const commit = { + type: 'revert', + subject: "feat(@whole/world): Hey, what's the worst that could happen?", + scope: 'this should be changed' + }; + + options.transform(commit, () => { + expect(commit.scope).toBe('@whole/world'); + done(); + }); + }); + + it('should not make changes to a commit if we are not using monorepo', done => { + const project = { + path: '/' + }; + const isMonorepo = false; + + const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const commit = { + type: 'release' + }; + + options.transform(commit, () => { + expect(commit).toEqual(commit); + done(); + }); + }); + + describe('should remove commit scope if we are using monorepo', () => { + it('when the scope is the same as the project name', done => { + const name = 'roc-plugin-hadron-collider'; + const project = { + name, + path: '/' + }; + const isMonorepo = true; + + const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const commit = { + type: 'release', + scope: name, + notes: [] + }; + + options.transform(commit, () => { + expect(commit.scope).toBe(null); + done(); + }); + }); + + it('when the scope is the same as the project name', done => { + const name = 'roc-plugin-hadron-collider'; + const project = { + name, + path: '/' + }; + const isMonorepo = true; + + const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const commit = { + type: 'release', + scope: name, + notes: [] + }; + + options.transform(commit, () => { + expect(commit.scope).toBe(null); + done(); + }); + }); + + it('when the scope affects all projects', done => { + const project = { + name: 'roc-plugin-hadron-collider', + path: '/' + }; + const isMonorepo = true; + + const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const commit = { + type: 'release', + scope: 'all', + notes: [] + }; + + options.transform(commit, () => { + expect(commit.scope).toBe(null); + done(); + }); + }); + + it('when project is included in a multi scope', done => { + const name = 'roc-plugin-hadron-collider'; + const project = { + name, + path: '/' + }; + const isMonorepo = true; + + const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const commit = { + type: 'multi', + scope: 'multi', + notes: [{ + title: 'scopes', + text: `roc-plugin-skynet, ${name}` + }] + }; + + options.transform(commit, () => { + expect(commit.scope).toBe(null); + done(); + }); + }); + }); + }); + + describe('to get next versions', () => { + it('should return correct next versions for projects', () => { + const hadron = 'roc-plugin-hadron-collider'; + const hadronNewVersion = '1.2.3'; + const skynet = 'roc-plugin-skynet'; + const skynetVersion = '0.1.4'; + const status = { + [hadron]: { + newVersion: hadronNewVersion + } + }; + const projects = [{ + name: hadron, + packageJSON: { + version: '0.0.0' + } + }, + { + name: skynet, + packageJSON: { + version: skynetVersion + } + }]; + + const projectsWithNextVersions = getNextVersions(status, projects); + + expect(projectsWithNextVersions[hadron].version).toBe(hadronNewVersion); + expect(projectsWithNextVersions[skynet].version).toBe(skynetVersion); + }); + }); + + describe('to match version numbers', () => { + it('should match when semver is ignored', () => { + const ignoreSemver = true; + const name = 'roc-plugin-hadron-collider'; + const projectsWithVersions = { + [name]: {} + }; + const dependencies = {}; + + const versionsDoesNotMatch = createVersionsDoesNotMatch(projectsWithVersions, dependencies, ignoreSemver); + expect(versionsDoesNotMatch(name)).toBe(false); + }); + + it('should match when dependency is declared as latest', () => { + const ignoreSemver = false; + const name = 'roc-plugin-hadron-collider'; + const projectsWithVersions = { + [name]: {} + }; + const dependencies = { + [name]: 'latest' + }; + + const versionsDoesNotMatch = createVersionsDoesNotMatch(projectsWithVersions, dependencies, ignoreSemver); + expect(versionsDoesNotMatch(name)).toBe(false); + }); + + it('should not match when dependency satisfies semver range', () => { + const ignoreSemver = false; + const name = 'roc-plugin-hadron-collider'; + const projectsWithVersions = { + [name]: { + version: '1.2.4' + } + }; + const dependencies = { + [name]: '^2.0.0' + }; + + const versionsDoesNotMatch = createVersionsDoesNotMatch(projectsWithVersions, dependencies, ignoreSemver); + expect(versionsDoesNotMatch(name)).toBe(true); + }); + }); + + describe('to get notes for multi scope commits', () => { + it('should ignore non-monorepo commits', () => { + const isMonorepo = false; + const commit = { + scope: 'multi', + notes: [ + { + title: 'scopes', + text: 'I should not see that' + } + ] + }; + + expect(getMultiScopes(commit, isMonorepo)).toEqual([]); + }); + + it('should extract text from monorepo commits', () => { + const isMonorepo = true; + const text = 'I should see that'; + const commit = { + scope: 'multi', + notes: [ + { + title: 'scopes', + text + } + ] + }; + + expect(getMultiScopes(commit, isMonorepo)).toEqual([text]); + }); + }); + + describe('to get notes from affected files', () => { + it('should ignore non-monorepo commits', () => { + const isMonorepo = false; + const commit = { + scope: '*' + }; + const projects = []; + + expect(getAutoScopes(commit, isMonorepo, projects)).toEqual([]); + }); + + it('should ignore commits that did not affect any files', () => { + const isMonorepo = true; + const commit = { + hash: 'commit-hash-nothing', + scope: '*' + }; + const projects = []; + + expect(getAutoScopes(commit, isMonorepo, projects)).toEqual([]); + }); + + it('should return scope of commits that did affect some files', () => { + const isMonorepo = true; + const hadron = 'roc-plugin-hadron-collider'; + const commit = { + hash: 'commit-hash-some-directory', + scope: '*' + }; + const projects = [{ + name: hadron, + directory: 'some-directory', + folder: 'some-folder' + }, { + name: 'roc-plugin-skynet', + directory: 'other-directory', + folder: 'other-folder' + }]; + + expect(getAutoScopes(commit, isMonorepo, projects)).toEqual([hadron]); + }); + }); +}); From d03b86ae5ea89a3117727ea4218606620101da0d Mon Sep 17 00:00:00 2001 From: kjarmicki Date: Tue, 28 Nov 2017 17:46:47 +0100 Subject: [PATCH 2/6] linting stuff --- .eslintrc | 3 + extensions/roc-plugin-repo/src/index.js | 1 + .../__mocks__/conventional-changelog.js | 2 +- .../src/semver/__mocks__/roc.js | 3 +- .../roc-plugin-repo/src/semver/utils.test.js | 164 +++++++++++------- 5 files changed, 109 insertions(+), 64 deletions(-) diff --git a/.eslintrc b/.eslintrc index dc1b7a7..4be8109 100644 --- a/.eslintrc +++ b/.eslintrc @@ -6,6 +6,9 @@ "plugins": [ "prettier" ], + "env": { + "jest": true + }, "rules": { "prettier/prettier": ["error", { "trailingComma": "all", "singleQuote": true }], "no-underscore-dangle": "off", diff --git a/extensions/roc-plugin-repo/src/index.js b/extensions/roc-plugin-repo/src/index.js index acf2cf9..fc6e36c 100644 --- a/extensions/roc-plugin-repo/src/index.js +++ b/extensions/roc-plugin-repo/src/index.js @@ -41,6 +41,7 @@ function fetchProjects(command) { return command(invokeHook('get-projects')); } +// eslint-disable-next-line import/no-unresolved const jestOptions = require('jest-cli/build/cli/args').options; Object.keys(jestOptions).forEach(key => { diff --git a/extensions/roc-plugin-repo/src/semver/__mocks__/conventional-changelog.js b/extensions/roc-plugin-repo/src/semver/__mocks__/conventional-changelog.js index bf41692..d2b9bfa 100644 --- a/extensions/roc-plugin-repo/src/semver/__mocks__/conventional-changelog.js +++ b/extensions/roc-plugin-repo/src/semver/__mocks__/conventional-changelog.js @@ -5,6 +5,6 @@ export default function(options, context, gitRawCommitsOpts) { const to = '84fe778a213bea560ca30bb8f085d8d6652eb455'; return conventionalChangelog(options, context, { ...gitRawCommitsOpts, - to + to, }); } diff --git a/extensions/roc-plugin-repo/src/semver/__mocks__/roc.js b/extensions/roc-plugin-repo/src/semver/__mocks__/roc.js index 40777c5..71a815a 100644 --- a/extensions/roc-plugin-repo/src/semver/__mocks__/roc.js +++ b/extensions/roc-plugin-repo/src/semver/__mocks__/roc.js @@ -1,8 +1,9 @@ const executeSync = command => { - if(command.includes('commit-hash-some-directory')) { + if (command.includes('commit-hash-some-directory')) { return 'some-directory/some-folder/roc-plugin-hadron-collider'; } return ''; }; +// eslint-disable-next-line import/prefer-default-export export { executeSync }; diff --git a/extensions/roc-plugin-repo/src/semver/utils.test.js b/extensions/roc-plugin-repo/src/semver/utils.test.js index 601b2d6..f258577 100644 --- a/extensions/roc-plugin-repo/src/semver/utils.test.js +++ b/extensions/roc-plugin-repo/src/semver/utils.test.js @@ -1,3 +1,4 @@ +/* eslint-disable import/first */ jest.mock('conventional-changelog'); jest.mock('roc'); @@ -7,21 +8,28 @@ import { getNextVersions, createVersionsDoesNotMatch, getMultiScopes, - getAutoScopes + getAutoScopes, } from './utils'; describe('Semver utilities', () => { - describe('to get latest commits since release', () => { it('should return information for current monorepo', async () => { const projects = []; const from = ''; const isMonorepo = true; - const result = await getLatestCommitsSinceRelease('angular', from, projects, isMonorepo); - - expect(Object.keys(result).sort()) - .toEqual(['roc-plugin-repo', 'roc-plugin-repo-react', 'roc-plugin-repo-roc']); + const result = await getLatestCommitsSinceRelease( + 'angular', + from, + projects, + isMonorepo, + ); + + expect(Object.keys(result).sort()).toEqual([ + 'roc-plugin-repo', + 'roc-plugin-repo-react', + 'roc-plugin-repo-roc', + ]); expect(result['roc-plugin-repo'].release.subject).toBe('0.0.27'); expect(result['roc-plugin-repo-react'].release.subject).toBe('0.0.5'); expect(result['roc-plugin-repo-roc'].release.subject).toBe('0.0.1'); @@ -31,15 +39,17 @@ describe('Semver utilities', () => { describe('to generate Conventional Changelog options', () => { it('should set correct scope when commit is a revert', done => { const project = { - path: '/' + path: '/', }; const isMonorepo = false; - const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const options = conventionalChangelogOptions('angular', isMonorepo, [])( + project, + ); const commit = { type: 'revert', subject: "feat(@whole/world): Hey, what's the worst that could happen?", - scope: 'this should be changed' + scope: 'this should be changed', }; options.transform(commit, () => { @@ -50,13 +60,15 @@ describe('Semver utilities', () => { it('should not make changes to a commit if we are not using monorepo', done => { const project = { - path: '/' + path: '/', }; const isMonorepo = false; - const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const options = conventionalChangelogOptions('angular', isMonorepo, [])( + project, + ); const commit = { - type: 'release' + type: 'release', }; options.transform(commit, () => { @@ -70,15 +82,17 @@ describe('Semver utilities', () => { const name = 'roc-plugin-hadron-collider'; const project = { name, - path: '/' + path: '/', }; const isMonorepo = true; - const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const options = conventionalChangelogOptions('angular', isMonorepo, [])( + project, + ); const commit = { type: 'release', scope: name, - notes: [] + notes: [], }; options.transform(commit, () => { @@ -91,15 +105,17 @@ describe('Semver utilities', () => { const name = 'roc-plugin-hadron-collider'; const project = { name, - path: '/' + path: '/', }; const isMonorepo = true; - const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const options = conventionalChangelogOptions('angular', isMonorepo, [])( + project, + ); const commit = { type: 'release', scope: name, - notes: [] + notes: [], }; options.transform(commit, () => { @@ -111,15 +127,17 @@ describe('Semver utilities', () => { it('when the scope affects all projects', done => { const project = { name: 'roc-plugin-hadron-collider', - path: '/' + path: '/', }; const isMonorepo = true; - const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const options = conventionalChangelogOptions('angular', isMonorepo, [])( + project, + ); const commit = { type: 'release', scope: 'all', - notes: [] + notes: [], }; options.transform(commit, () => { @@ -132,18 +150,22 @@ describe('Semver utilities', () => { const name = 'roc-plugin-hadron-collider'; const project = { name, - path: '/' + path: '/', }; const isMonorepo = true; - const options = conventionalChangelogOptions('angular', isMonorepo, [])(project); + const options = conventionalChangelogOptions('angular', isMonorepo, [])( + project, + ); const commit = { type: 'multi', scope: 'multi', - notes: [{ - title: 'scopes', - text: `roc-plugin-skynet, ${name}` - }] + notes: [ + { + title: 'scopes', + text: `roc-plugin-skynet, ${name}`, + }, + ], }; options.transform(commit, () => { @@ -162,21 +184,23 @@ describe('Semver utilities', () => { const skynetVersion = '0.1.4'; const status = { [hadron]: { - newVersion: hadronNewVersion - } + newVersion: hadronNewVersion, + }, }; - const projects = [{ + const projects = [ + { name: hadron, packageJSON: { - version: '0.0.0' - } + version: '0.0.0', + }, }, { name: skynet, packageJSON: { - version: skynetVersion - } - }]; + version: skynetVersion, + }, + }, + ]; const projectsWithNextVersions = getNextVersions(status, projects); @@ -190,11 +214,15 @@ describe('Semver utilities', () => { const ignoreSemver = true; const name = 'roc-plugin-hadron-collider'; const projectsWithVersions = { - [name]: {} + [name]: {}, }; const dependencies = {}; - const versionsDoesNotMatch = createVersionsDoesNotMatch(projectsWithVersions, dependencies, ignoreSemver); + const versionsDoesNotMatch = createVersionsDoesNotMatch( + projectsWithVersions, + dependencies, + ignoreSemver, + ); expect(versionsDoesNotMatch(name)).toBe(false); }); @@ -202,13 +230,17 @@ describe('Semver utilities', () => { const ignoreSemver = false; const name = 'roc-plugin-hadron-collider'; const projectsWithVersions = { - [name]: {} + [name]: {}, }; const dependencies = { - [name]: 'latest' + [name]: 'latest', }; - const versionsDoesNotMatch = createVersionsDoesNotMatch(projectsWithVersions, dependencies, ignoreSemver); + const versionsDoesNotMatch = createVersionsDoesNotMatch( + projectsWithVersions, + dependencies, + ignoreSemver, + ); expect(versionsDoesNotMatch(name)).toBe(false); }); @@ -217,14 +249,18 @@ describe('Semver utilities', () => { const name = 'roc-plugin-hadron-collider'; const projectsWithVersions = { [name]: { - version: '1.2.4' - } + version: '1.2.4', + }, }; const dependencies = { - [name]: '^2.0.0' + [name]: '^2.0.0', }; - const versionsDoesNotMatch = createVersionsDoesNotMatch(projectsWithVersions, dependencies, ignoreSemver); + const versionsDoesNotMatch = createVersionsDoesNotMatch( + projectsWithVersions, + dependencies, + ignoreSemver, + ); expect(versionsDoesNotMatch(name)).toBe(true); }); }); @@ -237,9 +273,9 @@ describe('Semver utilities', () => { notes: [ { title: 'scopes', - text: 'I should not see that' - } - ] + text: 'I should not see that', + }, + ], }; expect(getMultiScopes(commit, isMonorepo)).toEqual([]); @@ -253,9 +289,9 @@ describe('Semver utilities', () => { notes: [ { title: 'scopes', - text - } - ] + text, + }, + ], }; expect(getMultiScopes(commit, isMonorepo)).toEqual([text]); @@ -266,7 +302,7 @@ describe('Semver utilities', () => { it('should ignore non-monorepo commits', () => { const isMonorepo = false; const commit = { - scope: '*' + scope: '*', }; const projects = []; @@ -277,7 +313,7 @@ describe('Semver utilities', () => { const isMonorepo = true; const commit = { hash: 'commit-hash-nothing', - scope: '*' + scope: '*', }; const projects = []; @@ -289,19 +325,23 @@ describe('Semver utilities', () => { const hadron = 'roc-plugin-hadron-collider'; const commit = { hash: 'commit-hash-some-directory', - scope: '*' + scope: '*', }; - const projects = [{ - name: hadron, - directory: 'some-directory', - folder: 'some-folder' - }, { - name: 'roc-plugin-skynet', - directory: 'other-directory', - folder: 'other-folder' - }]; + const projects = [ + { + name: hadron, + directory: 'some-directory', + folder: 'some-folder', + }, + { + name: 'roc-plugin-skynet', + directory: 'other-directory', + folder: 'other-folder', + }, + ]; expect(getAutoScopes(commit, isMonorepo, projects)).toEqual([hadron]); }); }); }); +/* eslint-enable import/first */ From 78ff4297fd6f7fb62be45d20880a477ea415781b Mon Sep 17 00:00:00 2001 From: kjarmicki Date: Tue, 28 Nov 2017 17:55:19 +0100 Subject: [PATCH 3/6] we need complete history before testing --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 945df33..9e99311 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,5 @@ cache: node_js: - 6.9 before_script: + - git fetch --unshallow - npm start bootstrap From d9febeb2b2981799ce4cf33a96be449b8681ef79 Mon Sep 17 00:00:00 2001 From: kjarmicki Date: Mon, 11 Dec 2017 15:01:00 +0100 Subject: [PATCH 4/6] remove unnecessary travis command --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9e99311..945df33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,5 +5,4 @@ cache: node_js: - 6.9 before_script: - - git fetch --unshallow - npm start bootstrap From 344dc7946019ef0f6deb82a34a7b16261d38a5e4 Mon Sep 17 00:00:00 2001 From: kjarmicki Date: Tue, 12 Dec 2017 15:32:33 +0100 Subject: [PATCH 5/6] Revert "remove unnecessary travis command" This reverts commit d9febeb2b2981799ce4cf33a96be449b8681ef79. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 945df33..9e99311 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,5 @@ cache: node_js: - 6.9 before_script: + - git fetch --unshallow - npm start bootstrap From 7d516cb7a10af9ec9723eb071c23f648b5b06100 Mon Sep 17 00:00:00 2001 From: kjarmicki Date: Tue, 12 Dec 2017 15:33:38 +0100 Subject: [PATCH 6/6] describe need for unshallow fetch --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9e99311..3e70398 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,5 +5,5 @@ cache: node_js: - 6.9 before_script: - - git fetch --unshallow + - git fetch --unshallow # needed to perform tests correctly - npm start bootstrap