Skip to content

Commit

Permalink
Merge pull request #119 from bogaertg/test
Browse files Browse the repository at this point in the history
feat: add new tests
  • Loading branch information
pmowrer authored May 23, 2022
2 parents 089e88a + 8c27b61 commit 1a79dae
Show file tree
Hide file tree
Showing 5 changed files with 1,615 additions and 1,193 deletions.
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,18 @@
"semantic-release-plugin-decorators": "^3.0.0"
},
"devDependencies": {
"get-stream": "^6.0.1",
"git-log-parser": "^1.2.0",
"file-url": "^3.0.0",
"p-each-series": "^2.1.0",
"fs-extra": "^10.0.1",
"husky": "^4.2.1",
"jest": "^25.1.0",
"lint-staged": "^10.0.7",
"prettier": "^1.19.1",
"semantic-release": "^17.0.2",
"semantic-release-github-pr": "^6.0.0"
"semantic-release-github-pr": "^6.0.0",
"tempy": "1.0.1"
},
"husky": {
"hooks": {
Expand Down
212 changes: 210 additions & 2 deletions src/git-utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
const { pipeP, split } = require('ramda');
const execa = require('execa');
const { pipeP, split } = require('ramda');
const fse = require('fs-extra');
const path = require('path');
const tempy = require('tempy');
const fileUrl = require('file-url');
const gitLogParser = require('git-log-parser');
const pEachSeries = require('p-each-series');
const getStream = require('get-stream');

const git = async (args, options = {}) => {
const { stdout } = await execa('git', args, options);
Expand All @@ -13,7 +20,8 @@ const git = async (args, options = {}) => {
* @return {Promise<Array>} List of modified files in a commit.
*/
const getCommitFiles = pipeP(
hash => git(['diff-tree', '--no-commit-id', '--name-only', '-r', hash]),
hash =>
git(['diff-tree', '--root', '--no-commit-id', '--name-only', '-r', hash]),
split('\n')
);

Expand All @@ -24,7 +32,207 @@ const getCommitFiles = pipeP(
*/
const getRoot = () => git(['rev-parse', '--show-toplevel']);

/**
* Create commits on the current git repository.
*
* @param {Array<string>} messages Commit messages.
* @param {Object} [execaOpts] Options to pass to `execa`.
*
* @returns {Array<Commit>} The created commits, in reverse order (to match `git log` order).
*/
const gitCommitsWithFiles = async commits => {
for (const commit of commits) {
for (const file of commit.files) {
let filePath = path.join(process.cwd(), file.name);
await fse.outputFile(
filePath,
(file.body = !'undefined' ? file.body : commit.message)
);
await execa('git', ['add', filePath]);
}
await execa('git', [
'commit',
'-m',
commit.message,
'--allow-empty',
'--no-gpg-sign',
]);
}
return (await gitGetCommits(undefined)).slice(0, commits.length);
};

/**
* Initialize git repository
* If `withRemote` is `true`, creates a bare repository and initialize it.
* If `withRemote` is `false`, creates a regular repository and initialize it.
*
* @param {Boolean} withRemote `true` to create a shallow clone of a bare repository.
* @return {{cwd: string, repositoryUrl: string}} The path of the repository
*/
const initGit = async withRemote => {
const cwd = tempy.directory();
const args = withRemote
? ['--bare', '--initial-branch=master']
: ['--initial-branch=master'];

await execa('git', ['init', ...args], { cwd }).catch(async () => {
const args = withRemote ? ['--bare'] : [];
return await execa('git', ['init', ...args], { cwd });
});
const repositoryUrl = fileUrl(cwd);
return { cwd, repositoryUrl };
};

/**
* Create commits on the current git repository.
*
* @param {Array<string>} messages Commit messages.
* @param {Object} [execaOpts] Options to pass to `execa`.
*
* @returns {Array<Commit>} The created commits, in reverse order (to match `git log` order).
*/
const gitCommits = async (messages, execaOptions) => {
await pEachSeries(
messages,
async message =>
(
await execa(
'git',
['commit', '-m', message, '--allow-empty', '--no-gpg-sign'],
execaOptions
)
).stdout
);
return (await gitGetCommits(undefined, execaOptions)).slice(
0,
messages.length
);
};

/**
* Get the list of parsed commits since a git reference.
*
* @param {String} [from] Git reference from which to seach commits.
* @param {Object} [execaOpts] Options to pass to `execa`.
*
* @return {Array<Commit>} The list of parsed commits.
*/
gitGetCommits = async from => {
Object.assign(gitLogParser.fields, {
hash: 'H',
message: 'B',
gitTags: 'd',
committerDate: { key: 'ci', type: Date },
});
return (
await getStream.array(
gitLogParser.parse(
{ _: `${from ? from + '..' : ''}HEAD` },
{ env: { ...process.env } }
)
)
).map(commit => {
commit.message = commit.message.trim();
commit.gitTags = commit.gitTags.trim();
return commit;
});
};

/**
* Initialize an existing bare repository:
* - Clone the repository
* - Change the current working directory to the clone root
* - Create a default branch
* - Create an initial commits
* - Push to origin
*
* @param {String} repositoryUrl The URL of the bare repository.
* @param {String} [branch='master'] the branch to initialize.
*/
const initBareRepo = async (repositoryUrl, branch = 'master') => {
const cwd = tempy.directory();
await execa('git', ['clone', '--no-hardlinks', repositoryUrl, cwd], { cwd });
await gitCheckout(branch, true, { cwd });
gitCommits(['Initial commit'], { cwd });
await execa('git', ['push', repositoryUrl, branch], { cwd });
};

/**
* Create a temporary git repository.
* If `withRemote` is `true`, creates a shallow clone. Change the current working directory to the clone root.
* If `withRemote` is `false`, just change the current working directory to the repository root.
*
*
* @param {Boolean} withRemote `true` to create a shallow clone of a bare repository.
* @param {String} [branch='master'] The branch to initialize.
* @return {String} The path of the clone if `withRemote` is `true`, the path of the repository otherwise.
*/
const initGitRepo = async (withRemote, branch = 'master') => {
let { cwd, repositoryUrl } = await initGit(withRemote);
if (withRemote) {
await initBareRepo(repositoryUrl, branch);
cwd = gitShallowClone(repositoryUrl, branch);
} else {
await gitCheckout(branch, true, { cwd });
}

await execa('git', ['config', 'commit.gpgsign', false], { cwd });

return { cwd, repositoryUrl };
};

/**
* Create a shallow clone of a git repository and change the current working directory to the cloned repository root.
* The shallow will contain a limited number of commit and no tags.
*
* @param {String} repositoryUrl The path of the repository to clone.
* @param {String} [branch='master'] the branch to clone.
* @param {Number} [depth=1] The number of commit to clone.
* @return {String} The path of the cloned repository.
*/
const gitShallowClone = (repositoryUrl, branch = 'master', depth = 1) => {
const cwd = tempy.directory();

execa(
'git',
[
'clone',
'--no-hardlinks',
'--no-tags',
'-b',
branch,
'--depth',
depth,
repositoryUrl,
cwd,
],
{
cwd,
}
);
return cwd;
};

/**
* Checkout a branch on the current git repository.
*
* @param {String} branch Branch name.
* @param {Boolean} create to create the branch, `false` to checkout an existing branch.
* @param {Object} [execaOptions] Options to pass to `execa`.
*/
const gitCheckout = async (branch, create, execaOptions) => {
await execa(
'git',
create ? ['checkout', '-b', branch] : ['checkout', branch],
execaOptions
);
};

module.exports = {
getCommitFiles,
getRoot,
gitCommitsWithFiles,
initGitRepo,
initGit,
initBareRepo,
};
6 changes: 5 additions & 1 deletion src/only-package-commits.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,8 @@ const withOnlyPackageCommits = plugin => async (pluginConfig, config) => {
);
};

module.exports = withOnlyPackageCommits;
module.exports = {
withOnlyPackageCommits,
onlyPackageCommits,
withFiles,
};
108 changes: 108 additions & 0 deletions src/only-package-commits.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const { gitCommitsWithFiles, initGitRepo } = require('./git-utils');
const { onlyPackageCommits, withFiles } = require('./only-package-commits');
const path = require('path');

async function getCommitWithFileFromMessage(commits, message) {
commitsWithFiles = await withFiles(
Array.of(commits.find(obj => obj.subject === message))
);
if (commitsWithFiles.length !== 0) {
return commitsWithFiles[0];
} else {
return null;
}
}

describe('filter commits', () => {
it('should filter 0 commits (no root folder support) ', async () => {
const gitRepo = await initGitRepo(false);
let commitsToCreate = [
{ message: 'init1', files: [{ name: 'package.json' }] },
{ message: 'message1', files: [{ name: 'readme.md' }] },
{ message: 'message2', files: [{ name: 'module1/readme.md' }] },
{
message: 'message3',
files: [{ name: 'readme1.md' }, { name: 'module1/readme2.md' }],
},
];
process.chdir(gitRepo.cwd);
commits = await gitCommitsWithFiles(commitsToCreate);
result = await onlyPackageCommits(commits);
expect(result).toHaveLength(0);
});

it('should filter 3 commits (folder module1) ', async () => {
const gitRepo = await initGitRepo(false);
let commitsToCreate = [
{
message: 'init1',
files: [{ name: 'package.json' }, { name: 'module1/package.json' }],
},
{ message: 'message1', files: [{ name: 'readme.md' }] },
{ message: 'message2', files: [{ name: 'module1/readme.md' }] },
{
message: 'message3',
files: [{ name: 'readme1.md' }, { name: 'module1/readme2.md' }],
},
];
process.chdir(gitRepo.cwd);
commits = await gitCommitsWithFiles(commitsToCreate);
process.chdir(path.join(gitRepo.cwd, 'module1'));
result = await onlyPackageCommits(commits);

expect(result).toHaveLength(3);
expect(result).toContainEqual(
await getCommitWithFileFromMessage(commits, 'init1')
);
expect(result).not.toContainEqual(
await getCommitWithFileFromMessage(commits, 'message1')
);
expect(result).toContainEqual(
await getCommitWithFileFromMessage(commits, 'message2')
);
expect(result).toContainEqual(
await getCommitWithFileFromMessage(commits, 'message3')
);
});

it('should filter 2 commits (folder module2) ', async () => {
const gitRepo = await initGitRepo(false);
let commitsToCreate = [
{
message: 'init1',
files: [{ name: 'package.json' }, { name: 'module1/package.json' }],
},
{
message: 'message1',
files: [{ name: 'readme.md' }, { name: 'module2/package.json' }],
},
{ message: 'message2', files: [{ name: 'module1/readme.md' }] },
{
message: 'message3',
files: [
{ name: 'readme1.md' },
{ name: 'module1/readme2.md' },
{ name: 'module2/readme.md' },
],
},
];
process.chdir(gitRepo.cwd);
commits = await gitCommitsWithFiles(commitsToCreate);
process.chdir(path.join(gitRepo.cwd, 'module2'));
result = await onlyPackageCommits(commits);

expect(result).toHaveLength(2);
expect(result).not.toContainEqual(
await getCommitWithFileFromMessage(commits, 'init1')
);
expect(result).toContainEqual(
await getCommitWithFileFromMessage(commits, 'message1')
);
expect(result).not.toContainEqual(
await getCommitWithFileFromMessage(commits, 'message2')
);
expect(result).toContainEqual(
await getCommitWithFileFromMessage(commits, 'message3')
);
});
});
Loading

0 comments on commit 1a79dae

Please sign in to comment.