mirror of
https://github.com/fluxscape/fluxscape.git
synced 2026-01-12 07:12:54 +01:00
Initial commit
Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com> Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com> Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com> Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com> Co-Authored-By: Johan <4934465+joolsus@users.noreply.github.com> Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com> Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
// https://codehandbook.org/check-if-an-array-sorted-javascript/
|
||||
// Check if an array is sorted in order.
|
||||
function sorted(arr: number[]) {
|
||||
let second_index: number;
|
||||
for (let first_index = 0; first_index < arr.length; first_index++) {
|
||||
second_index = first_index + 1;
|
||||
if (arr[second_index] - arr[first_index] < 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
describe('Git clone progress tests', function () {
|
||||
let tempDir: string | undefined;
|
||||
|
||||
beforeEach(async function () {
|
||||
tempDir = app.getPath('temp') + '/noodlunittests-git-' + Utils.guid() + '/';
|
||||
FileSystem.instance.makeDirectorySync(tempDir);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
FileSystem.instance.removeDirectoryRecursive(tempDir, done);
|
||||
tempDir = undefined;
|
||||
});
|
||||
|
||||
it('clone from "master" with progress', async function () {
|
||||
const result = [];
|
||||
|
||||
// Clone the project
|
||||
const git = new Git(mergeProject);
|
||||
await git.clone({
|
||||
url: 'https://github.com/github/testrepo.git',
|
||||
directory: tempDir,
|
||||
onProgress: (progress) => {
|
||||
result.push(progress);
|
||||
}
|
||||
});
|
||||
|
||||
const values = result.map((x) => x.value);
|
||||
const isValuesSorted = sorted(values);
|
||||
|
||||
expect(result.length).toBeGreaterThan(10);
|
||||
expect(isValuesSorted).toBeTrue();
|
||||
});
|
||||
|
||||
it('clone and fetch with progress', async function () {
|
||||
const result = [];
|
||||
|
||||
// Create empty repo
|
||||
const git = new Git(mergeProject);
|
||||
await git.clone({
|
||||
url: 'https://github.com/github/testrepo.git',
|
||||
directory: tempDir,
|
||||
onProgress: (progress) => {
|
||||
result.push(progress);
|
||||
}
|
||||
});
|
||||
|
||||
await git.checkoutBranch('master', '6ff1be9c3819c93a2f41e0ddc09f252fcf154f34');
|
||||
|
||||
// Fetch from remote
|
||||
await git.fetch({
|
||||
onProgress: (progress) => {
|
||||
result.push(progress);
|
||||
}
|
||||
});
|
||||
|
||||
const values = result.map((x) => x.value);
|
||||
const isValuesSorted = sorted(values);
|
||||
|
||||
expect(result.length).toBeGreaterThan(10);
|
||||
expect(isValuesSorted).toBeTrue();
|
||||
});
|
||||
});
|
||||
59
packages/noodl-editor/tests/git/git-diff.spec.ts
Normal file
59
packages/noodl-editor/tests/git/git-diff.spec.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
describe('Git diff tests', function () {
|
||||
let git: Git;
|
||||
let tempDir: string;
|
||||
|
||||
beforeEach(async function () {
|
||||
// Logger.log(`[jest-before]: ${expect.getState().currentTestName}`)
|
||||
|
||||
tempDir = app.getPath('temp') + '/noodlunittests-git-' + Utils.guid() + '/';
|
||||
FileSystem.instance.makeDirectorySync(tempDir);
|
||||
|
||||
git = new Git(mergeProject);
|
||||
await git.initNewRepo(tempDir);
|
||||
// await git.commit("initial commit"); //commit .gitattributes so we have a clean repo
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
// Logger.log(`\r\n[jest-after]: ${expect.getState().currentTestName}`)
|
||||
FileSystem.instance.removeDirectoryRecursive(tempDir, done);
|
||||
tempDir = undefined;
|
||||
});
|
||||
|
||||
it('can diff files between commits', async function () {
|
||||
FileSystem.instance.writeFileSync(tempDir + 'initial.txt', 'hello world');
|
||||
await git.commit('initial commit');
|
||||
|
||||
const commit0 = await git.getHeadCommitId();
|
||||
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file.txt', 'text');
|
||||
await git.commit('added file');
|
||||
|
||||
const commit1 = await git.getHeadCommitId();
|
||||
let diff = await git.getFileDiff(commit0, commit1);
|
||||
expect(diff).toEqual([
|
||||
{
|
||||
status: 'new',
|
||||
path: 'file.txt'
|
||||
}
|
||||
]);
|
||||
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file.txt', 'text2');
|
||||
await git.commit('modified file');
|
||||
|
||||
const commit2 = await git.getHeadCommitId();
|
||||
diff = await git.getFileDiff(commit1, commit2);
|
||||
expect(diff).toEqual([
|
||||
{
|
||||
status: 'modified',
|
||||
path: 'file.txt'
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
160
packages/noodl-editor/tests/git/git-local-merge.spec.ts
Normal file
160
packages/noodl-editor/tests/git/git-local-merge.spec.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
// TODO: Stash untracked files ?
|
||||
|
||||
async function readTextFile(path) {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
FileSystem.instance.readTextFile(path, (text) => {
|
||||
resolve(text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('Git local tests', function () {
|
||||
let git: Git;
|
||||
let tempDir: string | undefined;
|
||||
|
||||
beforeEach(async function () {
|
||||
// Logger.log(`[jest-before]: ${expect.getState().currentTestName}`);
|
||||
|
||||
tempDir = app.getPath('temp') + '/noodlunittests-git-' + Utils.guid() + '/';
|
||||
FileSystem.instance.makeDirectorySync(tempDir);
|
||||
|
||||
git = new Git(mergeProject);
|
||||
await git.initNewRepo(tempDir);
|
||||
|
||||
// The new version doesnt make a first commit
|
||||
FileSystem.instance.writeFileSync(tempDir + 'initial.txt', 'Hello World');
|
||||
await git.commit('initial commit');
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
// Logger.log(`\r\n[jest-after]: ${expect.getState().currentTestName}`);
|
||||
|
||||
FileSystem.instance.removeDirectoryRecursive(tempDir, done);
|
||||
tempDir = undefined;
|
||||
});
|
||||
|
||||
it('can handle merge conflicts in files', async function () {
|
||||
//commit new file on main
|
||||
const path = tempDir + 'test.txt';
|
||||
|
||||
FileSystem.instance.writeFileSync(path, 'original');
|
||||
await git.commit('added new file');
|
||||
|
||||
//create new brach, change the file, and commit it
|
||||
await git.createAndCheckoutBranch('test-branch');
|
||||
FileSystem.instance.writeFileSync(path, 'test-branch');
|
||||
await git.commit('modified file');
|
||||
|
||||
//move back to main, and change the file
|
||||
await git.checkoutBranch('main');
|
||||
FileSystem.instance.writeFileSync(path, 'main');
|
||||
|
||||
//and now merge in the test brach. Should automatically resolve the conflict to "ours"
|
||||
await git.mergeToCurrentBranch('test-branch');
|
||||
const file = await readTextFile(path);
|
||||
expect(file).toBe('main');
|
||||
|
||||
// Status should show test.txt as modified
|
||||
const status = await git.status();
|
||||
expect(status).toEqual([{ status: 'modified', path: 'test.txt' }]);
|
||||
});
|
||||
|
||||
it('can handle merge without conflicts in project.json', async function () {
|
||||
//commit project on main
|
||||
const path = tempDir + 'project.json';
|
||||
|
||||
FileSystem.instance.writeFileSync(path, JSON.stringify(simpleProject()));
|
||||
await git.commit('added original project');
|
||||
|
||||
//create new brach, change the project, and commit it
|
||||
await git.createAndCheckoutBranch('test-branch');
|
||||
await git.checkoutBranch('test-branch');
|
||||
|
||||
const modifiedProjectTestBranch = simpleProject();
|
||||
modifiedProjectTestBranch.components[0].graph.roots[0].parameters.text = 'test-branch';
|
||||
FileSystem.instance.writeFileSync(path, JSON.stringify(modifiedProjectTestBranch));
|
||||
await git.commit('modified project');
|
||||
|
||||
//move back to main, and merge in the test branch
|
||||
await git.checkoutBranch('main');
|
||||
await git.mergeToCurrentBranch('test-branch');
|
||||
const proj = JSON.parse(await readTextFile(path));
|
||||
|
||||
const conflicts = proj.components[0].graph.roots[0].conflicts;
|
||||
expect(conflicts).toBe(undefined);
|
||||
expect(proj.components[0].graph.roots[0].parameters.text).toBe('test-branch');
|
||||
|
||||
//status should be empty
|
||||
expect(await git.status()).toEqual([]);
|
||||
});
|
||||
|
||||
it('can handle merge with conflicts in project.json', async function () {
|
||||
//commit project on main
|
||||
const path = tempDir + 'project.json';
|
||||
|
||||
FileSystem.instance.writeFileSync(path, JSON.stringify(simpleProject()));
|
||||
await git.commit('added original project');
|
||||
|
||||
//create new brach, change the project, and commit it
|
||||
await git.createAndCheckoutBranch('test-branch');
|
||||
await git.checkoutBranch('test-branch');
|
||||
|
||||
const modifiedProjectTestBranch = simpleProject();
|
||||
modifiedProjectTestBranch.components[0].graph.roots[0].parameters.text = 'test-branch';
|
||||
FileSystem.instance.writeFileSync(path, JSON.stringify(modifiedProjectTestBranch));
|
||||
await git.commit('modified project');
|
||||
|
||||
//move back to main, and change the project again
|
||||
await git.checkoutBranch('main');
|
||||
const modifiedProjectMaster = simpleProject();
|
||||
modifiedProjectMaster.components[0].graph.roots[0].parameters.text = 'main';
|
||||
|
||||
FileSystem.instance.writeFileSync(path, JSON.stringify(modifiedProjectMaster));
|
||||
|
||||
//and now merge in the test brach. Should automatically result in conflicts in project
|
||||
await git.mergeToCurrentBranch('test-branch');
|
||||
const proj = JSON.parse(await readTextFile(path));
|
||||
|
||||
const conflicts = proj.components[0].graph.roots[0].conflicts;
|
||||
expect(conflicts.length).toBe(1);
|
||||
expect(conflicts[0].name).toBe('text');
|
||||
expect(conflicts[0].ours).toBe('main');
|
||||
expect(conflicts[0].theirs).toBe('test-branch');
|
||||
|
||||
//check that status is contains a modified project.json
|
||||
expect(await git.status()).toEqual([{ status: 'modified', path: 'project.json' }]);
|
||||
});
|
||||
|
||||
function simpleProject() {
|
||||
return {
|
||||
name: 'proj',
|
||||
components: [
|
||||
{
|
||||
name: '/comp1',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: 'c8451024-fe91-0cbe-b3ad-85d77dd01432',
|
||||
type: 'Text',
|
||||
x: 237,
|
||||
y: 170,
|
||||
parameters: {
|
||||
text: 'original'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
rootNodeId: 'c8451024-fe91-0cbe-b3ad-85d77dd01432',
|
||||
version: '1'
|
||||
};
|
||||
}
|
||||
});
|
||||
44
packages/noodl-editor/tests/git/git-local-misc.spec.ts
Normal file
44
packages/noodl-editor/tests/git/git-local-misc.spec.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import Process from 'process';
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
describe('git local misc', () => {
|
||||
//Read project.json from the filesystem using using node, and then read the same file from the HEAD commit using git. They should be equal.
|
||||
//This will test the character encoding, and the test project contains a few special characters
|
||||
it('reads files with the correct encoding', async () => {
|
||||
const testFilePath = Process.cwd() + '/tests/testfs/git-repo-utf8/';
|
||||
|
||||
//create a temp folder with the test project.json file
|
||||
const tempDir = app.getPath('temp') + '/noodlunittests-git-' + Utils.guid() + '/';
|
||||
FileSystem.instance.makeDirectorySync(tempDir);
|
||||
FileSystem.instance.copyRecursiveSync(testFilePath, tempDir);
|
||||
|
||||
//create a new git repo and commit the file
|
||||
const git = new Git(mergeProject);
|
||||
await git.initNewRepo(tempDir);
|
||||
await git.commit('test');
|
||||
|
||||
//read project.json from the filesystem
|
||||
const projectFromFS = JSON.parse(FileSystem.instance.readFileSync(tempDir + 'project.json'));
|
||||
|
||||
//and read the same file from the HEAD commit
|
||||
const headCommitId = await git.getHeadCommitId();
|
||||
const headCommit = await git.getCommitFromId(headCommitId);
|
||||
expect(headCommit).toBeTruthy();
|
||||
const projectJson = await headCommit.getFileAsString('project.json');
|
||||
const project = JSON.parse(projectJson);
|
||||
expect(project).toBeTruthy();
|
||||
|
||||
//and compare
|
||||
expect(projectFromFS).toEqual(project);
|
||||
|
||||
//clean up
|
||||
await new Promise((resolve) => {
|
||||
FileSystem.instance.removeDirectoryRecursive(tempDir, resolve);
|
||||
});
|
||||
});
|
||||
});
|
||||
403
packages/noodl-editor/tests/git/git-local.spec.ts
Normal file
403
packages/noodl-editor/tests/git/git-local.spec.ts
Normal file
@@ -0,0 +1,403 @@
|
||||
import path from 'path';
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
// TODO: Stash untracked files ?
|
||||
|
||||
async function readTextFile(path) {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
FileSystem.instance.readTextFile(path, (text) => {
|
||||
resolve(text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('Git local tests', function () {
|
||||
let git: Git;
|
||||
let tempDir: string | undefined;
|
||||
|
||||
beforeEach(async function () {
|
||||
// Logger.log(`[jest-before]: ${expect.getState().currentTestName}`);
|
||||
|
||||
tempDir = app.getPath('temp') + '/noodlunittests-git-' + Utils.guid() + '/';
|
||||
FileSystem.instance.makeDirectorySync(tempDir);
|
||||
|
||||
git = new Git(mergeProject);
|
||||
await git.initNewRepo(tempDir);
|
||||
|
||||
// The new version doesnt make a first commit
|
||||
FileSystem.instance.writeFileSync(tempDir + 'initial.txt', 'Hello World');
|
||||
await git.commit('initial commit');
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
// Logger.log(`\r\n[jest-after]: ${expect.getState().currentTestName}`);
|
||||
|
||||
FileSystem.instance.removeDirectoryRecursive(tempDir, done);
|
||||
tempDir = undefined;
|
||||
});
|
||||
|
||||
it('new repo has correct config and no changes', async function () {
|
||||
const text = await readTextFile(tempDir + '.git/config');
|
||||
|
||||
expect(text.includes('precomposeUnicode = true')).toBe(true);
|
||||
|
||||
expect(text.includes('[merge "noodl"]')).toBe(true);
|
||||
|
||||
expect(await git.status()).toEqual([]);
|
||||
});
|
||||
|
||||
it('can get head commit', async function () {
|
||||
const head = await git.getHeadCommitId();
|
||||
expect(head).toHaveSize(40); // git commit hash length
|
||||
});
|
||||
|
||||
it('can get remote commit', async function () {
|
||||
//we have no remote, so it should be null
|
||||
const head = await git.getRemoteHeadCommitId();
|
||||
expect(head).toBe(null);
|
||||
});
|
||||
|
||||
it('can commit a new file', async function () {
|
||||
//add a new file
|
||||
FileSystem.instance.writeFileSync(tempDir + 'test.txt', 'hej');
|
||||
|
||||
//status should reflect the new file
|
||||
let status = await git.status();
|
||||
expect(status.length).toBe(1);
|
||||
expect(status[0].path).toBe('test.txt');
|
||||
expect(status[0].status).toBe('new');
|
||||
|
||||
//commit the file
|
||||
await git.commit('added new file');
|
||||
|
||||
//now git status should be empty
|
||||
status = await git.status();
|
||||
expect(status.length).toBe(0);
|
||||
|
||||
//and read it's content to make sure it wasn't modified
|
||||
const text = await readTextFile(tempDir + 'test.txt');
|
||||
expect(text).toBe('hej');
|
||||
});
|
||||
|
||||
it('can reset untracked files', async function () {
|
||||
//add a new file
|
||||
const path = tempDir + 'test.txt';
|
||||
|
||||
FileSystem.instance.writeFileSync(path, 'hej');
|
||||
expect(FileSystem.instance.fileExistsSync(path)).toBe(true);
|
||||
|
||||
await git.resetToHead();
|
||||
|
||||
expect(FileSystem.instance.fileExistsSync(path)).toBe(false);
|
||||
const status = await git.status();
|
||||
expect(status.length).toBe(0);
|
||||
});
|
||||
|
||||
it('can reset changed files', async function () {
|
||||
//add a new file
|
||||
const path = tempDir + 'test.txt';
|
||||
|
||||
//add file and commit it
|
||||
FileSystem.instance.writeFileSync(path, 'hej');
|
||||
await git.commit('added new file');
|
||||
|
||||
//change the file content
|
||||
FileSystem.instance.writeFileSync(path, 'hej2');
|
||||
expect(await readTextFile(path)).toBe('hej2');
|
||||
|
||||
await git.resetToHead();
|
||||
|
||||
expect(await readTextFile(path)).toBe('hej');
|
||||
|
||||
//check that status is empty
|
||||
const status = await git.status();
|
||||
expect(status.length).toBe(0);
|
||||
});
|
||||
|
||||
it('can create and checkout a new branch', async function () {
|
||||
expect(await git.getCurrentBranchName()).toBe('main');
|
||||
|
||||
await git.createAndCheckoutBranch('test-branch');
|
||||
await git.checkoutBranch('test-branch');
|
||||
expect(await git.getCurrentBranchName()).toBe('test-branch');
|
||||
|
||||
//check that status is empty
|
||||
const status = await git.status();
|
||||
expect(status.length).toBe(0);
|
||||
});
|
||||
|
||||
it('can list all branches', async function () {
|
||||
expect(await git.getBranches()).toEqual([{ name: 'main', local: true, remote: false }]);
|
||||
|
||||
await git.createAndCheckoutBranch('test-branch-1');
|
||||
await git.createAndCheckoutBranch('test-branch-2');
|
||||
|
||||
const branches = await git.getBranches();
|
||||
|
||||
expect(branches[0]).toEqual({ name: 'main', local: true, remote: false });
|
||||
expect(branches[1]).toEqual({
|
||||
name: 'test-branch-1',
|
||||
local: true,
|
||||
remote: false
|
||||
});
|
||||
expect(branches[2]).toEqual({
|
||||
name: 'test-branch-2',
|
||||
local: true,
|
||||
remote: false
|
||||
});
|
||||
});
|
||||
|
||||
it('can list commits from different branches', async function () {
|
||||
const path = tempDir + 'test.txt';
|
||||
|
||||
await git.createAndCheckoutBranch('test-branch-1');
|
||||
await git.checkoutBranch('test-branch-1');
|
||||
|
||||
//commit new file on test-branch
|
||||
FileSystem.instance.writeFileSync(path, 'text file');
|
||||
await git.commit('added new file');
|
||||
|
||||
await git.checkoutBranch('main');
|
||||
let commits = await git.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(1);
|
||||
expect(commits[0].message).toBe('initial commit');
|
||||
|
||||
await git.checkoutBranch('test-branch-1');
|
||||
commits = await git.getCommitsCurrentBranch();
|
||||
|
||||
//latest first
|
||||
expect(commits.length).toBe(2);
|
||||
expect(commits[0].message).toBe('added new file');
|
||||
expect(commits[1].message).toBe('initial commit');
|
||||
|
||||
//back to main, shouldn't see the commit on the other branch
|
||||
await git.checkoutBranch('main');
|
||||
commits = await git.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(1);
|
||||
expect(commits[0].message).toBe('initial commit');
|
||||
});
|
||||
|
||||
it('can list commits', async function () {
|
||||
//do a new commit
|
||||
FileSystem.instance.writeFileSync(tempDir + 'test.txt', 'text file');
|
||||
await git.commit('added new file');
|
||||
|
||||
const commits = await git.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(2);
|
||||
|
||||
//latest first
|
||||
expect(commits[0].message).toBe('added new file');
|
||||
expect(commits[0].parentCount).toBe(1);
|
||||
expect(commits[1].message).toBe('initial commit');
|
||||
expect(commits[1].parentCount).toBe(0);
|
||||
});
|
||||
|
||||
it('throws nice error message when creating invalid branches', async function () {
|
||||
await expectAsync(git.createAndCheckoutBranch('main')).toBeRejectedWithError('Branch already exists');
|
||||
await expectAsync(git.createAndCheckoutBranch(')(*&^%$')).toBeRejectedWithError(
|
||||
'Branch name contains invalid characters.'
|
||||
);
|
||||
});
|
||||
|
||||
it('can delete a local branch', async function () {
|
||||
await git.createBranchFromHead('test');
|
||||
await git.deleteBranch('test');
|
||||
|
||||
expect(await git.getBranches()).toEqual([{ name: 'main', local: true, remote: false }]);
|
||||
});
|
||||
|
||||
it('fails when deleting a non-existing branch', async function () {
|
||||
await expectAsync(git.deleteBranch('test')).toBeRejectedWithError("Branch doesn't exist");
|
||||
});
|
||||
|
||||
it('remembers local changes per branch using the stash', async function () {
|
||||
const path = tempDir + 'test.txt';
|
||||
|
||||
//commit a file
|
||||
FileSystem.instance.writeFileSync(path, 'original');
|
||||
await git.commit('added the file');
|
||||
|
||||
//make a local change
|
||||
FileSystem.instance.writeFileSync(path, 'main');
|
||||
|
||||
//create a few branches and make changes on each, without committing
|
||||
await git.createBranchFromHead('test-1');
|
||||
await git.checkoutBranch('test-1');
|
||||
FileSystem.instance.writeFileSync(path, 'test-1');
|
||||
|
||||
await git.createBranchFromHead('test-2');
|
||||
await git.checkoutBranch('test-2');
|
||||
FileSystem.instance.writeFileSync(path, 'test-2');
|
||||
|
||||
//checkout all branches and make sure the local changes are re-applied
|
||||
await git.checkoutBranch('main');
|
||||
expect(await readTextFile(path)).toBe('main');
|
||||
|
||||
await git.checkoutBranch('test-1');
|
||||
expect(await readTextFile(path)).toBe('test-1');
|
||||
|
||||
await git.checkoutBranch('test-2');
|
||||
expect(await readTextFile(path)).toBe('test-2');
|
||||
|
||||
//and back again, just to make sure :)
|
||||
await git.checkoutBranch('test-1');
|
||||
expect(await readTextFile(path)).toBe('test-1');
|
||||
|
||||
await git.checkoutBranch('main');
|
||||
expect(await readTextFile(path)).toBe('main');
|
||||
});
|
||||
|
||||
it('brings the local changes to a newly created branch', async function () {
|
||||
const path = tempDir + 'test.txt';
|
||||
|
||||
//commit a file
|
||||
FileSystem.instance.writeFileSync(path, 'original');
|
||||
await git.commit('added the file');
|
||||
|
||||
//make a local change
|
||||
FileSystem.instance.writeFileSync(path, 'change');
|
||||
|
||||
//create a new branch, it should get the changes
|
||||
await git.createAndCheckoutBranch('test-1');
|
||||
expect(await readTextFile(path)).toBe('change');
|
||||
|
||||
//and move it over to yet another new branch
|
||||
await git.createAndCheckoutBranch('test-2');
|
||||
expect(await readTextFile(path)).toBe('change');
|
||||
|
||||
//checkout all other branches and make sure they don't have any changes
|
||||
await git.checkoutBranch('main');
|
||||
FileSystem.instance.writeFileSync(path, 'original');
|
||||
|
||||
await git.checkoutBranch('test-1');
|
||||
FileSystem.instance.writeFileSync(path, 'original');
|
||||
|
||||
//and that test-2 still have them
|
||||
await git.checkoutBranch('test-2');
|
||||
expect(await readTextFile(path)).toBe('change');
|
||||
});
|
||||
|
||||
it('brings the local changes to a newly created branch - with project.json', async function () {
|
||||
//commit project on main
|
||||
const path = tempDir + 'project.json';
|
||||
const projectJson = simpleProject();
|
||||
FileSystem.instance.writeFileSync(path, JSON.stringify(projectJson));
|
||||
await git.commit('added original project');
|
||||
|
||||
//modify project without committing
|
||||
projectJson.components[0].graph.roots[0].parameters.text = 'modified';
|
||||
FileSystem.instance.writeFileSync(path, JSON.stringify(projectJson));
|
||||
|
||||
//create a new brach and do a checkout
|
||||
await git.createAndCheckoutBranch('test-branch');
|
||||
|
||||
//check that the previous uncommitted change is on the new branch
|
||||
expect(await git.status()).toEqual([{ status: 'modified', path: 'project.json' }]);
|
||||
const p = JSON.parse(await readTextFile(path));
|
||||
expect(p).toEqual(projectJson);
|
||||
|
||||
//move back to main, and the local changes shouldn't be here anymore
|
||||
await git.checkoutBranch('main');
|
||||
expect(await git.status()).toEqual([]);
|
||||
});
|
||||
|
||||
it('can create and checkout new branches when there are no local changes', async function () {
|
||||
//commit a file
|
||||
FileSystem.instance.writeFileSync(tempDir + 'my-file.txt', 'original');
|
||||
await git.commit('added the file');
|
||||
|
||||
//create a new brach and do a checkout
|
||||
await git.createAndCheckoutBranch('test-branch');
|
||||
|
||||
expect(await git.status()).toEqual([]);
|
||||
expect(await git.getCurrentBranchName()).toEqual('test-branch');
|
||||
|
||||
const commits = await git.getCommitsCurrentBranch();
|
||||
expect(commits.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should leave local changes intact even if a checkout fails', async function () {
|
||||
const path = tempDir + 'test.txt';
|
||||
FileSystem.instance.writeFileSync(path, 'local change');
|
||||
|
||||
await expectAsync(git.checkoutBranch('non-existing-branch')).toBeRejected();
|
||||
|
||||
expect(await readTextFile(path)).toBe('local change');
|
||||
});
|
||||
|
||||
it('should leave local changes intact even if a merge fails', async function () {
|
||||
const path = tempDir + 'test.txt';
|
||||
FileSystem.instance.writeFileSync(path, 'local change');
|
||||
|
||||
await expectAsync(git.mergeToCurrentBranch('non-existing-branch')).toBeRejected();
|
||||
|
||||
expect(await readTextFile(path)).toBe('local change');
|
||||
});
|
||||
|
||||
it('should leave local changes intact even if creating a branch fails', async function () {
|
||||
const path = tempDir + 'test.txt';
|
||||
FileSystem.instance.writeFileSync(path, 'local change');
|
||||
|
||||
await expectAsync(git.createAndCheckoutBranch('invalid &*^%')).toBeRejected();
|
||||
|
||||
expect(await readTextFile(path)).toBe('local change');
|
||||
});
|
||||
|
||||
it("ignores files that shouldn't be added to the repo", async function () {
|
||||
FileSystem.instance.writeFileSync(tempDir + 'project-tmp.json', 'test');
|
||||
FileSystem.instance.writeFileSync(tempDir + '.DS_Store', 'test');
|
||||
|
||||
expect(await git.status()).toEqual([]);
|
||||
});
|
||||
|
||||
it('ahead and behind', async function () {
|
||||
FileSystem.instance.writeFileSync(path.join(tempDir, 'a1.txt'), 'Hello World');
|
||||
await git.commit('A1 commit');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(tempDir, 'a2.txt'), 'Hello World');
|
||||
await git.commit('A2 commit');
|
||||
const a2 = await git.getHeadCommitId();
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(tempDir, 'a3.txt'), 'Hello World');
|
||||
await git.commit('A3 commit');
|
||||
const a3 = await git.getHeadCommitId();
|
||||
|
||||
const commits = await git.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(4); // 3 + initial
|
||||
|
||||
const { ahead, behind } = await git.aheadBehind(a2, a3);
|
||||
expect(ahead).toEqual(0);
|
||||
expect(behind).toEqual(1);
|
||||
});
|
||||
|
||||
function simpleProject() {
|
||||
return {
|
||||
name: 'proj',
|
||||
components: [
|
||||
{
|
||||
name: '/comp1',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: 'c8451024-fe91-0cbe-b3ad-85d77dd01432',
|
||||
type: 'Text',
|
||||
x: 237,
|
||||
y: 170,
|
||||
parameters: {
|
||||
text: 'original'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
rootNodeId: 'c8451024-fe91-0cbe-b3ad-85d77dd01432',
|
||||
version: '1'
|
||||
};
|
||||
}
|
||||
});
|
||||
105
packages/noodl-editor/tests/git/git-log.spec.ts
Normal file
105
packages/noodl-editor/tests/git/git-log.spec.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import path from 'path';
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
describe('Git log tests', function () {
|
||||
let git: Git;
|
||||
let tempDir: string | undefined;
|
||||
|
||||
beforeEach(async function () {
|
||||
// Logger.log(`[jest-before]: ${expect.getState().currentTestName}`);
|
||||
|
||||
tempDir = app.getPath('temp') + '/noodlunittests-git-' + Utils.guid() + '/';
|
||||
FileSystem.instance.makeDirectorySync(tempDir);
|
||||
|
||||
git = new Git(mergeProject);
|
||||
await git.initNewRepo(tempDir);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
// Logger.log(`\r\n[jest-after]: ${expect.getState().currentTestName}`);
|
||||
|
||||
FileSystem.instance.removeDirectoryRecursive(tempDir, done);
|
||||
tempDir = undefined;
|
||||
});
|
||||
|
||||
it('make a few commits and check logs', async function () {
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file1.txt', 'text');
|
||||
await git.setConfigValue('user.name', 'test');
|
||||
await git.setConfigValue('user.email', 'test@test.test');
|
||||
|
||||
await git.commit('add file1.txt');
|
||||
const commit0 = await git.getHeadCommitId();
|
||||
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file2.txt', 'text');
|
||||
await git.commit('add file2.txt');
|
||||
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file3.txt', 'text');
|
||||
await git.commit('add file3.txt');
|
||||
const commit2 = await git.getHeadCommitId();
|
||||
|
||||
const commits = await git.getCommitsBetween(commit0, commit2);
|
||||
expect(commits.length).toBe(2);
|
||||
expect(commits[0].message).toEqual('add file3.txt');
|
||||
expect(commits[0].author.name).toEqual('test');
|
||||
expect(commits[0].author.email).toEqual('test@test.test');
|
||||
expect(commits[1].message).toEqual('add file2.txt');
|
||||
});
|
||||
|
||||
it('can get the commits included in a merge', async function () {
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file1.txt', 'text');
|
||||
await git.commit('add file1.txt');
|
||||
|
||||
//create a new brach and do a checkout
|
||||
await git.createAndCheckoutBranch('test-branch');
|
||||
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file2.txt', 'text');
|
||||
await git.commit('a commit');
|
||||
const commit1Sha = await git.getHeadCommitId();
|
||||
expect(commit1Sha).toBeTruthy();
|
||||
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file3.txt', 'text');
|
||||
await git.commit('another commit');
|
||||
const commit2Sha = await git.getHeadCommitId();
|
||||
expect(commit1Sha).toBeTruthy();
|
||||
|
||||
await git.checkoutBranch('main');
|
||||
|
||||
const mainHead = await git.getHeadCommitId();
|
||||
const testBranchHead = await git.getHeadCommitOnBranch('test-branch');
|
||||
|
||||
expect(mainHead).toBeTruthy();
|
||||
expect(testBranchHead).toBeTruthy();
|
||||
|
||||
// TODO: The order matters a lot, this will fail in the real test
|
||||
const commits = await git.getCommitsBetween(mainHead, testBranchHead);
|
||||
|
||||
expect(commits.length).toEqual(2);
|
||||
expect(commits[0].message).toEqual('another commit');
|
||||
expect(commits[1].message).toEqual('a commit');
|
||||
});
|
||||
|
||||
it('support .gitignore', async function () {
|
||||
FileSystem.instance.writeFileSync(path.join(tempDir, 'test.txt'), 'hello');
|
||||
FileSystem.instance.writeFileSync(path.join(tempDir, '.DS_Store'), 'asdasd');
|
||||
|
||||
const status1 = await git.status();
|
||||
expect(status1.length).toBe(3);
|
||||
expect(status1).toEqual([
|
||||
{ status: 'new', path: '.gitattributes' },
|
||||
{ status: 'new', path: '.gitignore' },
|
||||
{ status: 'new', path: 'test.txt' }
|
||||
]);
|
||||
|
||||
await git.commit('A added file');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(tempDir, '.DS_Store'), 'asd');
|
||||
|
||||
const status2 = await git.status();
|
||||
expect(status2.length).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
import fs from 'fs';
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
async function unzipFileToFolder(zipPath: string, tempDir: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.readFile(zipPath, (err, data) => {
|
||||
if (err) throw err;
|
||||
|
||||
FileSystem.instance.unzipToFolder(tempDir, data, (r) => {
|
||||
if (r.result === 'success') {
|
||||
resolve();
|
||||
} else {
|
||||
reject(r);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('Git tests - misc', () => {
|
||||
let tempDir: string;
|
||||
let remoteDir: string;
|
||||
|
||||
beforeEach(async function () {
|
||||
tempDir = app.getPath('temp') + '/noodlunittests-git-' + Utils.guid() + '/';
|
||||
remoteDir = app.getPath('temp') + '/noodlunittests-git-' + Utils.guid() + '/';
|
||||
|
||||
FileSystem.instance.makeDirectorySync(tempDir);
|
||||
FileSystem.instance.makeDirectorySync(remoteDir);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
FileSystem.instance.removeDirectoryRecursive(tempDir, () => {
|
||||
FileSystem.instance.removeDirectoryRecursive(remoteDir, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can merge text example with two branches', async function () {
|
||||
const zipPath = process.cwd() + '/tests/testfs/git-merge-test-1.zip';
|
||||
|
||||
//unzip test project
|
||||
await unzipFileToFolder(zipPath, tempDir);
|
||||
|
||||
const remoteGit = new Git(mergeProject);
|
||||
await remoteGit.initNewRepo(remoteDir, { bare: true });
|
||||
|
||||
const git = new Git(mergeProject);
|
||||
await git.openRepository(tempDir + 'git-merge-test-1');
|
||||
|
||||
await git.addRemote(remoteDir);
|
||||
await git.push();
|
||||
|
||||
await git.checkoutBranch('branch-test');
|
||||
await git.push();
|
||||
|
||||
const branchCommitId = await git.getHeadCommitOnBranch('main');
|
||||
|
||||
const c = await git.getCommitFromId(branchCommitId);
|
||||
expect(c).toBeTruthy();
|
||||
});
|
||||
});
|
||||
363
packages/noodl-editor/tests/git/git-remote-merge.spec.ts
Normal file
363
packages/noodl-editor/tests/git/git-remote-merge.spec.ts
Normal file
@@ -0,0 +1,363 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
async function readTextFile(path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
FileSystem.instance.readTextFile(path, (text) => {
|
||||
resolve(text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('Git remote tests', function () {
|
||||
let localGitA: Git, localGitB: Git;
|
||||
let remoteGit: Git;
|
||||
|
||||
let localDirA: string;
|
||||
let localDirB: string;
|
||||
let remoteDir: string;
|
||||
|
||||
beforeEach(async function () {
|
||||
// console.log(`[jest-before]: ${jasmine.currentTest.fullName}`);
|
||||
|
||||
remoteDir = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
localDirA = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
localDirB = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
|
||||
// Logger.log("remoteDir: " + remoteDir);
|
||||
// Logger.log("localDirA: " + localDirA);
|
||||
// Logger.log("localDirB: " + localDirB);
|
||||
|
||||
FileSystem.instance.makeDirectorySync(localDirA);
|
||||
FileSystem.instance.makeDirectorySync(localDirB);
|
||||
FileSystem.instance.makeDirectorySync(remoteDir);
|
||||
|
||||
localGitA = new Git(mergeProject);
|
||||
localGitB = new Git(mergeProject);
|
||||
remoteGit = new Git(mergeProject);
|
||||
|
||||
//init a bare repository as remote
|
||||
await remoteGit.initNewRepo(remoteDir, { bare: true });
|
||||
|
||||
//init a new local repo and push it to A (mimics how a new project is created)
|
||||
await localGitA.initNewRepo(localDirA);
|
||||
|
||||
// The new version doesnt make a first commit
|
||||
FileSystem.instance.writeFileSync(localDirA + 'initial.txt', 'Hello World');
|
||||
await localGitA.commit('initial commit');
|
||||
|
||||
await localGitA.addRemote(remoteDir);
|
||||
await localGitA.push();
|
||||
|
||||
//and clone the project as B to another directory
|
||||
await localGitB.clone({
|
||||
url: remoteDir,
|
||||
directory: localDirB,
|
||||
onProgress: undefined
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
// Logger.log(`\r\n[jest-after]: ${expect.getState().currentTestName}`);
|
||||
|
||||
FileSystem.instance.removeDirectoryRecursive(remoteDir, () => {
|
||||
FileSystem.instance.removeDirectoryRecursive(localDirA, () => {
|
||||
FileSystem.instance.removeDirectoryRecursive(localDirB, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can pull and merge remote commits', async function () {
|
||||
//create new file on localA
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'test.txt'), 'localA file');
|
||||
await localGitA.commit('A added file');
|
||||
await localGitA.push();
|
||||
|
||||
//pull it down on localB
|
||||
await localGitB.pull({});
|
||||
expect(await readTextFile(path.join(localDirB, 'test.txt'))).toBe('localA file');
|
||||
|
||||
//modify it on localA
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'test.txt'), 'localA mod');
|
||||
await localGitA.commit('A modified file');
|
||||
await localGitA.push();
|
||||
|
||||
//modify it on localB
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'test.txt'), 'localB mod');
|
||||
await localGitB.commit('B modified file');
|
||||
|
||||
//pull it down
|
||||
await localGitB.pull({});
|
||||
|
||||
//should have been resolved to latest localB modification
|
||||
expect(await readTextFile(path.join(localDirB, 'test.txt'))).toBe('localB mod');
|
||||
|
||||
const commits = await localGitB.getCommitsCurrentBranch();
|
||||
|
||||
expect(commits.length).toEqual(5);
|
||||
|
||||
expect(commits[0].message).toEqual('Merge origin/main into main');
|
||||
expect(commits[1].message).toEqual('B modified file');
|
||||
expect(commits[2].message).toEqual('A modified file');
|
||||
expect(commits[3].message).toEqual('A added file');
|
||||
expect(commits[4].message).toEqual('initial commit');
|
||||
});
|
||||
|
||||
it('can pull and merge remote commits when a file is added both on remote and locally', async function () {
|
||||
//create new file on localA and push
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'test.txt'), 'A file in localA');
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, '.DS_Store'), 'asdasd');
|
||||
await localGitA.commit('A added file');
|
||||
await localGitA.push();
|
||||
|
||||
//add it in localB, and then pull
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'test.txt'), 'A file in localB');
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, '.DS_Store'), '453456');
|
||||
await localGitB.pull({});
|
||||
expect(await readTextFile(path.join(localDirB, 'test.txt'))).toBe('A file in localB');
|
||||
|
||||
// Check all the commits
|
||||
const commits = await localGitB.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(2);
|
||||
expect(commits[0].message).toBe('A added file');
|
||||
expect(commits[1].message).toBe('initial commit');
|
||||
|
||||
const status = await localGitB.status();
|
||||
expect(status.length).toBe(1);
|
||||
expect(status[0]).toEqual({ status: 'modified', path: 'test.txt' });
|
||||
});
|
||||
|
||||
it('can pull and merge remote commits when a commits and a file is added both on remote and locally', async function () {
|
||||
await localGitB.pull({});
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'b.txt'), 'asdasd');
|
||||
await localGitB.commit('B commit');
|
||||
await localGitB.push();
|
||||
|
||||
//create new file on localA and push
|
||||
await localGitA.pull({});
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'test.txt'), 'A file in localA');
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, '.DS_Store'), 'asdasd');
|
||||
await localGitA.commit('A added file');
|
||||
await localGitA.push();
|
||||
|
||||
//add it in localB, and then pull
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'test.txt'), 'A file in localB');
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, '.DS_Store'), '453456');
|
||||
await localGitB.pull({});
|
||||
expect(await readTextFile(path.join(localDirB, 'test.txt'))).toBe('A file in localB');
|
||||
|
||||
// Check all the commits
|
||||
const commits = await localGitB.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(3);
|
||||
expect(commits[0].message).toBe('A added file');
|
||||
expect(commits[1].message).toBe('B commit');
|
||||
expect(commits[2].message).toBe('initial commit');
|
||||
|
||||
const status = await localGitB.status();
|
||||
expect(status.length).toBe(1);
|
||||
expect(status[0]).toEqual({ status: 'modified', path: 'test.txt' });
|
||||
});
|
||||
|
||||
it('can reset to merge base when remote is not ahead', async function () {
|
||||
await localGitA.createAndCheckoutBranch('A');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'test.txt'), 'file');
|
||||
await localGitA.commit('A commit');
|
||||
await localGitA.push();
|
||||
|
||||
await localGitB.fetch({});
|
||||
await localGitB.checkoutRemoteBranch('A');
|
||||
|
||||
//B should now have the file
|
||||
expect(await readTextFile(path.join(localDirB, 'test.txt'))).toBe('file');
|
||||
|
||||
//do a local uncommitted change
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'test.txt'), 'modified');
|
||||
|
||||
//reset, and verify that it's back to original file
|
||||
await localGitB.resetToMergeBase();
|
||||
expect(await readTextFile(path.join(localDirB, 'test.txt'))).toBe('file');
|
||||
|
||||
let commits = await localGitB.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(2);
|
||||
|
||||
//do a local committed change
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'test.txt'), 'modified');
|
||||
await localGitB.commit('modified file');
|
||||
|
||||
commits = await localGitB.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(3);
|
||||
|
||||
//reset, and verify that it's back to original file, and commit is removed
|
||||
await localGitB.resetToMergeBase();
|
||||
expect(await readTextFile(path.join(localDirB, 'test.txt'))).toBe('file');
|
||||
|
||||
commits = await localGitB.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(2);
|
||||
});
|
||||
|
||||
it('can reset to merge base when remote is ahead', async function () {
|
||||
await localGitA.createAndCheckoutBranch('A');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'test.txt'), 'file');
|
||||
await localGitA.commit('A commit');
|
||||
await localGitA.push();
|
||||
|
||||
await localGitB.fetch({});
|
||||
await localGitB.checkoutRemoteBranch('A');
|
||||
|
||||
//do a local committed change
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'test.txt'), 'modified');
|
||||
await localGitB.commit('modified file');
|
||||
|
||||
// TODO: Make sure the history is correct?
|
||||
|
||||
//... plus a local modification
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'test.txt'), 'modified2');
|
||||
|
||||
//localA pushed new commit to remote
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'test.txt'), 'file2');
|
||||
await localGitA.commit('Another commit');
|
||||
await localGitA.push();
|
||||
|
||||
//localB fetches, but doesn't merge in (doesn't pull)
|
||||
await localGitB.fetch({});
|
||||
|
||||
//B resets, verify that it's back to original file, and commit is removed, and remote didn't get merged in
|
||||
await localGitB.resetToMergeBase();
|
||||
expect(await readTextFile(path.join(localDirB, 'test.txt'))).toBe('file');
|
||||
|
||||
const commits = await localGitB.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(3);
|
||||
expect(commits[0].isRemoteAhead).toBe(true);
|
||||
|
||||
// Lets just make sure the commits are in the right order
|
||||
expect(commits[0].message).toBe('Another commit');
|
||||
expect(commits[1].message).toBe('A commit');
|
||||
expect(commits[2].message).toBe('initial commit');
|
||||
});
|
||||
|
||||
it('can handle merge with conflicts in project.json', async function () {
|
||||
const localDirA_projectPath = path.join(localDirA, 'project.json');
|
||||
const localDirB_projectPath = path.join(localDirB, 'project.json');
|
||||
|
||||
// Git A
|
||||
// Write a simple project file to localDirA
|
||||
fs.writeFileSync(localDirA_projectPath, JSON.stringify(simpleProject()));
|
||||
|
||||
// We now have 1 file, project.json
|
||||
const statusA1 = await localGitA.status();
|
||||
expect(statusA1.length).toEqual(1);
|
||||
|
||||
// Commit and add remote
|
||||
await localGitA.commit('add project.json');
|
||||
|
||||
// Git B
|
||||
// Do all the changes on Git B without remote
|
||||
await localGitB.clone({ url: remoteDir, directory: localDirB });
|
||||
|
||||
// Write a simple project file with changes to localDirB
|
||||
const modifiedProjectTestBranch = simpleProject();
|
||||
modifiedProjectTestBranch.components[0].graph.roots[0].parameters.text = 'changed';
|
||||
fs.writeFileSync(localDirB_projectPath, JSON.stringify(modifiedProjectTestBranch));
|
||||
|
||||
// We now have 1 file, project.json
|
||||
const statusB1 = await localGitB.status();
|
||||
expect(statusB1.length).toEqual(1);
|
||||
|
||||
// Commit and push to remote
|
||||
await localGitB.commit('commit');
|
||||
|
||||
// Merge
|
||||
await localGitA.push();
|
||||
await localGitB.pull({});
|
||||
await localGitB.push();
|
||||
|
||||
const proj = JSON.parse(await fs.promises.readFile(localDirB_projectPath, 'utf8'));
|
||||
|
||||
const conflicts = proj.components[0].graph.roots[0].conflicts;
|
||||
expect(conflicts.length).toBe(1);
|
||||
expect(conflicts[0].name).toBe('text');
|
||||
expect(conflicts[0].ours).toBe('changed');
|
||||
expect(conflicts[0].theirs).toBe('original');
|
||||
});
|
||||
|
||||
it('Merge with conflicts everywhere, even in stash', async function () {
|
||||
const localDirA_projectPath = path.join(localDirA, 'project.json');
|
||||
const localDirB_projectPath = path.join(localDirB, 'project.json');
|
||||
|
||||
// Git A
|
||||
// Write a simple project file to localDirA
|
||||
fs.writeFileSync(localDirA_projectPath, JSON.stringify(simpleProject()));
|
||||
|
||||
// We now have 1 file, project.json
|
||||
const statusA1 = await localGitA.status();
|
||||
expect(statusA1.length).toEqual(1);
|
||||
|
||||
// Commit and add remote
|
||||
await localGitA.commit('add project.json');
|
||||
|
||||
// Git B
|
||||
// Do all the changes on Git B without remote
|
||||
await localGitB.clone({ url: remoteDir, directory: localDirB });
|
||||
|
||||
// Write a simple project file with changes to localDirB
|
||||
const modifiedProjectTestBranch = simpleProject();
|
||||
modifiedProjectTestBranch.components[0].graph.roots[0].parameters.text = 'changed';
|
||||
fs.writeFileSync(localDirB_projectPath, JSON.stringify(modifiedProjectTestBranch));
|
||||
|
||||
// We now have 1 file, project.json
|
||||
const statusB1 = await localGitB.status();
|
||||
expect(statusB1.length).toEqual(1);
|
||||
|
||||
// Commit and push to remote
|
||||
await localGitB.commit('commit');
|
||||
|
||||
// Write a simple project file with changes to localDirB
|
||||
const modifiedProjectTestBranch2 = simpleProject();
|
||||
modifiedProjectTestBranch2.components[0].graph.roots[0].parameters.text = 'stashed';
|
||||
fs.writeFileSync(localDirB_projectPath, JSON.stringify(modifiedProjectTestBranch2));
|
||||
|
||||
// Merge
|
||||
await localGitA.push();
|
||||
await localGitB.pull({});
|
||||
await localGitB.push();
|
||||
|
||||
const proj = JSON.parse(await fs.promises.readFile(localDirB_projectPath, 'utf8'));
|
||||
|
||||
const entry = proj.components[0].graph.roots[0];
|
||||
expect(entry.parameters.text).toBe('stashed');
|
||||
});
|
||||
});
|
||||
|
||||
function simpleProject() {
|
||||
return {
|
||||
name: 'proj',
|
||||
components: [
|
||||
{
|
||||
name: '/comp1',
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: 'c8451024-fe91-0cbe-b3ad-85d77dd01432',
|
||||
type: 'Text',
|
||||
x: 237,
|
||||
y: 170,
|
||||
parameters: {
|
||||
text: 'original'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
rootNodeId: 'c8451024-fe91-0cbe-b3ad-85d77dd01432',
|
||||
version: '1'
|
||||
};
|
||||
}
|
||||
122
packages/noodl-editor/tests/git/git-remote-squash.spec.ts
Normal file
122
packages/noodl-editor/tests/git/git-remote-squash.spec.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import path from 'path';
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
// jest.setTimeout(10_000);
|
||||
|
||||
async function readTextFile(path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
FileSystem.instance.readTextFile(path, (text) => {
|
||||
resolve(text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('Git remote squash tests', function () {
|
||||
let localGitA: Git, localGitB: Git;
|
||||
let remoteGit: Git;
|
||||
|
||||
let localDirA: string;
|
||||
let localDirB: string;
|
||||
let remoteDir: string;
|
||||
|
||||
beforeEach(async function () {
|
||||
// console.log(`[jest-before]: ${jasmine.currentTest.fullName}`);
|
||||
|
||||
remoteDir = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
localDirA = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
localDirB = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
|
||||
// Logger.log("remoteDir: " + remoteDir);
|
||||
// Logger.log("localDirA: " + localDirA);
|
||||
// Logger.log("localDirB: " + localDirB);
|
||||
|
||||
FileSystem.instance.makeDirectorySync(localDirA);
|
||||
FileSystem.instance.makeDirectorySync(localDirB);
|
||||
FileSystem.instance.makeDirectorySync(remoteDir);
|
||||
|
||||
localGitA = new Git(mergeProject);
|
||||
localGitB = new Git(mergeProject);
|
||||
remoteGit = new Git(mergeProject);
|
||||
|
||||
//init a bare repository as remote
|
||||
await remoteGit.initNewRepo(remoteDir, { bare: true });
|
||||
|
||||
//init a new local repo and push it to A (mimics how a new project is created)
|
||||
await localGitA.initNewRepo(localDirA);
|
||||
|
||||
// The new version doesnt make a first commit
|
||||
FileSystem.instance.writeFileSync(localDirA + 'initial.txt', 'Hello World');
|
||||
await localGitA.commit('initial commit');
|
||||
|
||||
await localGitA.addRemote(remoteDir);
|
||||
await localGitA.push();
|
||||
|
||||
//and clone the project as B to another directory
|
||||
await localGitB.clone({
|
||||
url: remoteDir,
|
||||
directory: localDirB,
|
||||
onProgress: undefined
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
// Logger.log(`\r\n[jest-after]: ${expect.getState().currentTestName}`);
|
||||
|
||||
FileSystem.instance.removeDirectoryRecursive(remoteDir, () => {
|
||||
FileSystem.instance.removeDirectoryRecursive(localDirA, () => {
|
||||
FileSystem.instance.removeDirectoryRecursive(localDirB, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Push when there are remote changes (squash merge)', async function () {
|
||||
await localGitB.createAndCheckoutBranch('squash-test');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'a.txt'), 'Hello World');
|
||||
await localGitB.commit('commit 1');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'b.txt'), 'Hello World');
|
||||
await localGitB.commit('commit 2');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'c.txt'), 'Hello World');
|
||||
await localGitB.commit('commit 3');
|
||||
|
||||
await localGitB.push();
|
||||
|
||||
await localGitA.pull({});
|
||||
await localGitA.mergeToCurrentBranch('origin/squash-test');
|
||||
|
||||
const commits = await localGitA.getCommitsCurrentBranch();
|
||||
expect(commits[0].message).toBe("Squashed commit from branch 'origin/squash-test'");
|
||||
expect(commits[1].message).toBe('initial commit');
|
||||
});
|
||||
|
||||
it('Push when there are remote changes (merge)', async function () {
|
||||
await localGitB.createAndCheckoutBranch('squash-test');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'a.txt'), 'Hello World');
|
||||
await localGitB.commit('commit 1');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'b.txt'), 'Hello World');
|
||||
await localGitB.commit('commit 2');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'c.txt'), 'Hello World');
|
||||
await localGitB.commit('commit 3');
|
||||
|
||||
await localGitB.push();
|
||||
|
||||
await localGitA.pull({});
|
||||
await localGitA.mergeToCurrentBranch('origin/squash-test', false);
|
||||
|
||||
const commits = await localGitA.getCommitsCurrentBranch();
|
||||
expect(commits[0].message).toBe('commit 3');
|
||||
expect(commits[1].message).toBe('commit 2');
|
||||
expect(commits[2].message).toBe('commit 1');
|
||||
expect(commits[3].message).toBe('initial commit');
|
||||
});
|
||||
});
|
||||
325
packages/noodl-editor/tests/git/git-remote.spec.ts
Normal file
325
packages/noodl-editor/tests/git/git-remote.spec.ts
Normal file
@@ -0,0 +1,325 @@
|
||||
import path from 'path';
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
// jest.setTimeout(10_000);
|
||||
|
||||
async function readTextFile(path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
FileSystem.instance.readTextFile(path, (text) => {
|
||||
resolve(text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('Git remote tests', function () {
|
||||
let localGitA: Git, localGitB: Git;
|
||||
let remoteGit: Git;
|
||||
|
||||
let localDirA: string;
|
||||
let localDirB: string;
|
||||
let remoteDir: string;
|
||||
|
||||
beforeEach(async function () {
|
||||
// console.log(`[jest-before]: ${jasmine.currentTest.fullName}`);
|
||||
|
||||
remoteDir = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
localDirA = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
localDirB = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
|
||||
// Logger.log("remoteDir: " + remoteDir);
|
||||
// Logger.log("localDirA: " + localDirA);
|
||||
// Logger.log("localDirB: " + localDirB);
|
||||
|
||||
FileSystem.instance.makeDirectorySync(localDirA);
|
||||
FileSystem.instance.makeDirectorySync(localDirB);
|
||||
FileSystem.instance.makeDirectorySync(remoteDir);
|
||||
|
||||
localGitA = new Git(mergeProject);
|
||||
localGitB = new Git(mergeProject);
|
||||
remoteGit = new Git(mergeProject);
|
||||
|
||||
//init a bare repository as remote
|
||||
await remoteGit.initNewRepo(remoteDir, { bare: true });
|
||||
|
||||
//init a new local repo and push it to A (mimics how a new project is created)
|
||||
await localGitA.initNewRepo(localDirA);
|
||||
|
||||
// The new version doesnt make a first commit
|
||||
FileSystem.instance.writeFileSync(localDirA + 'initial.txt', 'Hello World');
|
||||
await localGitA.commit('initial commit');
|
||||
|
||||
await localGitA.addRemote(remoteDir);
|
||||
await localGitA.push();
|
||||
|
||||
//and clone the project as B to another directory
|
||||
await localGitB.clone({
|
||||
url: remoteDir,
|
||||
directory: localDirB,
|
||||
onProgress: undefined
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
// Logger.log(`\r\n[jest-after]: ${expect.getState().currentTestName}`);
|
||||
|
||||
FileSystem.instance.removeDirectoryRecursive(remoteDir, () => {
|
||||
FileSystem.instance.removeDirectoryRecursive(localDirA, () => {
|
||||
FileSystem.instance.removeDirectoryRecursive(localDirB, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can get remote head', async function () {
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'a.txt'), 'Hello World');
|
||||
await localGitA.commit('local commit');
|
||||
|
||||
const remoteHeadId1 = await localGitA.getRemoteHeadCommitId();
|
||||
const localHeadId1 = await localGitA.getHeadCommitId();
|
||||
expect(remoteHeadId1).not.toEqual(localHeadId1);
|
||||
|
||||
await localGitA.push();
|
||||
|
||||
const remoteHeadId2 = await localGitA.getRemoteHeadCommitId();
|
||||
const localHeadId2 = await localGitA.getHeadCommitId();
|
||||
expect(remoteHeadId2).toEqual(localHeadId2);
|
||||
});
|
||||
|
||||
it('can push and fetch remote commits', async function () {
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'a.txt'), 'Hello World');
|
||||
await localGitA.commit('localA commit');
|
||||
await localGitA.push();
|
||||
|
||||
await localGitB.fetch({});
|
||||
|
||||
const commits = await localGitB.getCommitsCurrentBranch();
|
||||
|
||||
expect(commits[0].isLocalAhead).toBe(false);
|
||||
expect(commits[0].isRemoteAhead).toBe(true);
|
||||
expect(commits[0].message).toEqual('localA commit');
|
||||
|
||||
expect(commits[0].isLocalAhead).toBe(false);
|
||||
expect(commits[1].isRemoteAhead).toBe(false);
|
||||
expect(commits[1].message).toEqual('initial commit');
|
||||
|
||||
expect(commits.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('can list local and remote branches', async function () {
|
||||
await localGitA.createAndCheckoutBranch('A');
|
||||
await localGitB.createAndCheckoutBranch('B');
|
||||
|
||||
await localGitA.push();
|
||||
await localGitB.fetch({});
|
||||
|
||||
const branches = await localGitB.getBranches();
|
||||
|
||||
//alphabetical order
|
||||
expect(branches[0]).toEqual({ name: 'A', remote: true, local: false });
|
||||
expect(branches[1]).toEqual({ name: 'B', remote: false, local: true });
|
||||
expect(branches[2]).toEqual({ name: 'main', remote: true, local: true });
|
||||
|
||||
expect(branches.length).toBe(3);
|
||||
});
|
||||
|
||||
it('can checkout remote branch', async function () {
|
||||
await localGitA.createAndCheckoutBranch('A');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'test.txt'), 'remote file');
|
||||
await localGitA.commit('A commit');
|
||||
await localGitA.push();
|
||||
|
||||
await localGitB.fetch({});
|
||||
await localGitB.checkoutRemoteBranch('A');
|
||||
|
||||
expect(await readTextFile(path.join(localDirB, 'test.txt'))).toBe('remote file');
|
||||
|
||||
const commits = await localGitB.getCommitsCurrentBranch();
|
||||
|
||||
expect(commits[0].message).toEqual('A commit');
|
||||
expect(commits.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('can list remote branch correctly when they have slashes in the name', async function () {
|
||||
await localGitA.createAndCheckoutBranch('test/A');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'test.txt'), 'remote file');
|
||||
await localGitA.commit('A commit');
|
||||
await localGitA.push();
|
||||
|
||||
await localGitB.fetch({});
|
||||
const branches = await localGitB.getBranches();
|
||||
|
||||
expect(branches[1]).toEqual({ name: 'test/A', remote: true, local: false });
|
||||
});
|
||||
|
||||
it('can delete remote branch', async function () {
|
||||
//A creates a new branch
|
||||
await localGitA.createAndCheckoutBranch('A');
|
||||
await localGitA.push();
|
||||
|
||||
//B fetches the new branch
|
||||
await localGitB.fetch({});
|
||||
let branches = await localGitB.getBranches();
|
||||
expect(branches.length).toEqual(2);
|
||||
|
||||
//B deletes the branch
|
||||
await localGitB.deleteRemoteBranch('A');
|
||||
|
||||
//.. and B now doesn't have it anymore
|
||||
branches = await localGitB.getBranches();
|
||||
expect(branches.length).toEqual(1);
|
||||
|
||||
//A should think it still exists on the remote
|
||||
branches = await localGitA.getBranches();
|
||||
expect(branches.find((b) => b.name === 'A').remote).toEqual(true);
|
||||
|
||||
//but after a fetch it should be local only
|
||||
await localGitA.fetch({});
|
||||
branches = await localGitA.getBranches();
|
||||
const branchA = branches.find((b) => b.name === 'A');
|
||||
expect(branchA.remote).toEqual(false);
|
||||
expect(branchA.local).toEqual(true);
|
||||
});
|
||||
|
||||
it('can delete local branch but leave remote intact', async function () {
|
||||
await localGitA.createAndCheckoutBranch('A');
|
||||
await localGitA.push();
|
||||
|
||||
await localGitB.fetch({});
|
||||
await localGitB.checkoutRemoteBranch('A');
|
||||
|
||||
let branches = await localGitB.getBranches();
|
||||
expect(branches.length).toEqual(2);
|
||||
expect(branches[0]).toEqual({ name: 'A', local: true, remote: true });
|
||||
|
||||
await localGitB.checkoutBranch('main');
|
||||
await localGitB.deleteBranch('A');
|
||||
|
||||
branches = await localGitB.getBranches();
|
||||
expect(branches.length).toEqual(2);
|
||||
|
||||
expect(branches[0]).toEqual({ name: 'A', local: false, remote: true });
|
||||
});
|
||||
|
||||
it('correctly identifies local vs remote commits - one ahead', async function () {
|
||||
await localGitA.createAndCheckoutBranch('A');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'a.txt'), 'Hello World');
|
||||
await localGitA.commit('A commit');
|
||||
expect(await localGitA.push()).toBe(true);
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'a1.txt'), 'Hello World');
|
||||
await localGitA.commit('A1 commit');
|
||||
|
||||
const commits = await localGitA.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(3);
|
||||
expect(commits[0].isLocalAhead).toBe(true);
|
||||
expect(commits[1].isLocalAhead).toBeFalsy();
|
||||
});
|
||||
|
||||
it('correctly identifies local vs remote commits - branch not pushed', async function () {
|
||||
await localGitA.createAndCheckoutBranch('A');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'a.txt'), 'Hello World');
|
||||
await localGitA.commit('A commit');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'a1.txt'), 'Hello World');
|
||||
await localGitA.commit('A1 commit');
|
||||
|
||||
const commits = await localGitA.getCommitsCurrentBranch();
|
||||
expect(commits.length).toBe(3);
|
||||
expect(commits[0].isLocalAhead).toBe(true);
|
||||
expect(commits[1].isLocalAhead).toBe(true);
|
||||
});
|
||||
|
||||
it('Push when there are remote changes', async function () {
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'a.txt'), 'Hello World');
|
||||
await localGitA.commit('A commit');
|
||||
await localGitA.push({});
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'a.txt'), 'Hello World2');
|
||||
await localGitB.commit('A commit');
|
||||
|
||||
try {
|
||||
await localGitB.push({});
|
||||
expect(true).toBe(false);
|
||||
} catch (error) {
|
||||
expect(error.toString()).toContain(
|
||||
'Updates were rejected because there are new changes that you do not have locally.'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('getCommitsBetween returns the correct commits (squash)', async function () {
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'a.txt'), 'Hello World');
|
||||
await localGitA.commit('A commit');
|
||||
await localGitA.push();
|
||||
|
||||
await localGitB.pull({});
|
||||
await localGitB.createAndCheckoutBranch('A');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'b.txt'), 'Hello World');
|
||||
await localGitB.commit('B commit');
|
||||
await localGitB.push();
|
||||
|
||||
const headCommitId = await localGitA.getHeadCommitId();
|
||||
const branchCommitId = await localGitB.getHeadCommitId();
|
||||
|
||||
const allCommits = await localGitA.getCommitsCurrentBranch();
|
||||
expect(allCommits.length).toBe(2);
|
||||
expect(allCommits[0].message).toBe('A commit');
|
||||
expect(allCommits[1].message).toBe('initial commit');
|
||||
|
||||
const commits = await localGitB.getCommitsBetween(branchCommitId, headCommitId);
|
||||
expect(commits.length).toBe(1);
|
||||
expect(commits[0].message).toBe('B commit');
|
||||
|
||||
await localGitA.fetch({});
|
||||
await localGitA.mergeToCurrentBranch('origin/A');
|
||||
|
||||
const allCommits2 = await localGitA.getCommitsCurrentBranch();
|
||||
expect(allCommits2.length).toBe(3);
|
||||
expect(allCommits2[0].message).toBe("Squashed commit from branch 'origin/A'");
|
||||
expect(allCommits2[1].message).toBe('A commit');
|
||||
expect(allCommits2[2].message).toBe('initial commit');
|
||||
});
|
||||
|
||||
it('getCommitsBetween returns the correct commits', async function () {
|
||||
FileSystem.instance.writeFileSync(path.join(localDirA, 'a.txt'), 'Hello World');
|
||||
await localGitA.commit('A commit');
|
||||
await localGitA.push();
|
||||
|
||||
await localGitB.pull({});
|
||||
await localGitB.createAndCheckoutBranch('A');
|
||||
|
||||
FileSystem.instance.writeFileSync(path.join(localDirB, 'b.txt'), 'Hello World');
|
||||
await localGitB.commit('B commit');
|
||||
await localGitB.push();
|
||||
|
||||
const headCommitId = await localGitA.getHeadCommitId();
|
||||
const branchCommitId = await localGitB.getHeadCommitId();
|
||||
|
||||
const allCommits = await localGitA.getCommitsCurrentBranch();
|
||||
expect(allCommits.length).toBe(2);
|
||||
expect(allCommits[0].message).toBe('A commit');
|
||||
expect(allCommits[1].message).toBe('initial commit');
|
||||
|
||||
const commits = await localGitB.getCommitsBetween(branchCommitId, headCommitId);
|
||||
expect(commits.length).toBe(1);
|
||||
expect(commits[0].message).toBe('B commit');
|
||||
|
||||
await localGitA.fetch({});
|
||||
await localGitA.mergeToCurrentBranch('origin/A', false);
|
||||
|
||||
const allCommits2 = await localGitA.getCommitsCurrentBranch();
|
||||
expect(allCommits2.length).toBe(3);
|
||||
expect(allCommits2[0].message).toBe('B commit');
|
||||
expect(allCommits2[1].message).toBe('A commit');
|
||||
expect(allCommits2[2].message).toBe('initial commit');
|
||||
});
|
||||
});
|
||||
107
packages/noodl-editor/tests/git/git-stash-merge.spec.ts
Normal file
107
packages/noodl-editor/tests/git/git-stash-merge.spec.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
describe('Git stash tests', function () {
|
||||
let localGitA: Git, localGitB: Git;
|
||||
let remoteGit: Git;
|
||||
|
||||
let localDirA: string;
|
||||
let localDirB: string;
|
||||
let remoteDir: string;
|
||||
|
||||
beforeEach(async function () {
|
||||
// Logger.log(`[jest-before]: ${expect.getState().currentTestName}`)
|
||||
|
||||
remoteDir = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
localDirA = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
localDirB = path.join(app.getPath('temp'), '/noodlunittests-git-' + Utils.guid());
|
||||
|
||||
// Logger.log("remoteDir: " + remoteDir);
|
||||
// Logger.log("localDirA: " + localDirA);
|
||||
// Logger.log("localDirB: " + localDirB);
|
||||
|
||||
FileSystem.instance.makeDirectorySync(localDirA);
|
||||
FileSystem.instance.makeDirectorySync(localDirB);
|
||||
FileSystem.instance.makeDirectorySync(remoteDir);
|
||||
|
||||
localGitA = new Git(mergeProject);
|
||||
localGitB = new Git(mergeProject);
|
||||
remoteGit = new Git(mergeProject);
|
||||
|
||||
//init a bare repository as remote
|
||||
await remoteGit.initNewRepo(remoteDir, { bare: true });
|
||||
|
||||
//init a new local repo and push it to A (mimics how a new project is created)
|
||||
await localGitA.initNewRepo(localDirA);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
// Logger.log(`\r\n[jest-after]: ${expect.getState().currentTestName}`)
|
||||
|
||||
FileSystem.instance.removeDirectoryRecursive(remoteDir, () => {
|
||||
FileSystem.instance.removeDirectoryRecursive(localDirA, () => {
|
||||
FileSystem.instance.removeDirectoryRecursive(localDirB, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Issue: https://app.asana.com/0/1202061493156140/1202351418844106/f
|
||||
* >
|
||||
* > Michael Cartner:
|
||||
* > i mitt fall så var det två användare som öppnade samma 2.5 projekt.
|
||||
* > Båda skapade då en .gitignore lokalt
|
||||
* > Användare A commitade
|
||||
* > Användare B pullade => felmeddelandet ovan
|
||||
* >
|
||||
* >
|
||||
* > Related?: https://stackoverflow.com/questions/51275777/why-does-git-stash-pop-say-that-it-could-not-restore-untracked-files-from-stash
|
||||
*/
|
||||
it('pop-stash with merge issues', async function () {
|
||||
// Git A
|
||||
// 1. delete .gitignore
|
||||
const statusA1 = await localGitA.status();
|
||||
expect(statusA1.length).toEqual(2); // .gitignore and .gitattributes
|
||||
|
||||
fs.writeFileSync(localDirA + '/temp', 'temp');
|
||||
fs.unlinkSync(localDirA + '/.gitignore');
|
||||
|
||||
await localGitA.commit('initial commit without .gitignore');
|
||||
await localGitA.addRemote(remoteDir);
|
||||
await localGitA.push();
|
||||
|
||||
// Git B
|
||||
await localGitB.clone({ url: remoteDir, directory: localDirB });
|
||||
await localGitB.fetch({});
|
||||
|
||||
const statusB1 = await localGitB.status();
|
||||
expect(statusB1.length).toEqual(1);
|
||||
|
||||
await localGitB.commit('commit .gitignore');
|
||||
await localGitB.push();
|
||||
|
||||
// Git A
|
||||
// Recreate the repo (creating .gitignore)
|
||||
localGitA = new Git(mergeProject);
|
||||
await localGitA.openRepository(localDirA);
|
||||
|
||||
// Check that we have it
|
||||
const statusA2 = await localGitA.status();
|
||||
expect(statusA2.length).toEqual(1);
|
||||
|
||||
/**
|
||||
* GitError: .gitignore already exists, no checkout
|
||||
* error: could not restore untracked files from stash
|
||||
*/
|
||||
await localGitA.pull({});
|
||||
|
||||
const statusA3 = await localGitA.status();
|
||||
expect(statusA3.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
51
packages/noodl-editor/tests/git/git-stash.spec.ts
Normal file
51
packages/noodl-editor/tests/git/git-stash.spec.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
describe('Git stash tests', function () {
|
||||
let git: Git;
|
||||
let tempDir: string | undefined;
|
||||
|
||||
beforeEach(async function () {
|
||||
// Logger.log(`[jest-before]: ${expect.getState().currentTestName}`)
|
||||
|
||||
tempDir = app.getPath('temp') + '/noodlunittests-git-' + Utils.guid() + '/';
|
||||
FileSystem.instance.makeDirectorySync(tempDir);
|
||||
|
||||
git = new Git(mergeProject);
|
||||
await git.initNewRepo(tempDir);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
// Logger.log(`\r\n[jest-after]: ${expect.getState().currentTestName}`)
|
||||
FileSystem.instance.removeDirectoryRecursive(tempDir, done);
|
||||
tempDir = undefined;
|
||||
});
|
||||
|
||||
it('stash changes', async function () {
|
||||
// cant stash when there are no commits
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file.txt', 'text');
|
||||
await git.commit('initial commit');
|
||||
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file1.txt', 'text');
|
||||
expect(await git.stashPushChanges()).toBeTruthy();
|
||||
|
||||
const status1 = await git.status();
|
||||
expect(status1).toEqual([]);
|
||||
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file2.txt', 'text');
|
||||
expect(await git.stashPopChanges()).toBe(true);
|
||||
|
||||
// NOTE: Got some issue on OSX where pop was called using spawn process.
|
||||
// OSX didn't seem to like this and ignored the call, changed it to exec.
|
||||
|
||||
// Calling pop here again makes sure that the previous pop worked.
|
||||
expect(await git.stashPopChanges()).toBe(false);
|
||||
|
||||
const status2 = await git.status();
|
||||
expect(status2.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
76
packages/noodl-editor/tests/git/git-status.spec.ts
Normal file
76
packages/noodl-editor/tests/git/git-status.spec.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { app } from '@electron/remote';
|
||||
import { Git } from '@noodl/git';
|
||||
|
||||
import FileSystem from '@noodl-utils/filesystem';
|
||||
import { mergeProject } from '@noodl-utils/projectmerger';
|
||||
import Utils from '@noodl-utils/utils';
|
||||
|
||||
describe('Git status tests', function () {
|
||||
let git: Git;
|
||||
let tempDir: string | undefined;
|
||||
|
||||
beforeEach(async function () {
|
||||
tempDir = app.getPath('temp') + '/noodlunittests-git-' + Utils.guid() + '/';
|
||||
FileSystem.instance.makeDirectorySync(tempDir);
|
||||
|
||||
git = new Git(mergeProject);
|
||||
await git.initNewRepo(tempDir);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
FileSystem.instance.removeDirectoryRecursive(tempDir, done);
|
||||
tempDir = undefined;
|
||||
});
|
||||
|
||||
it('create file', async function () {
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file.txt', 'text');
|
||||
const status = await git.status();
|
||||
|
||||
expect(status).toEqual([
|
||||
{
|
||||
status: 'new',
|
||||
path: '.gitattributes'
|
||||
},
|
||||
{
|
||||
status: 'new',
|
||||
path: '.gitignore'
|
||||
},
|
||||
{
|
||||
status: 'new',
|
||||
path: 'file.txt'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('create file, commit and update file', async function () {
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file.txt', 'text');
|
||||
|
||||
const status1 = await git.status();
|
||||
expect(status1).toEqual([
|
||||
{
|
||||
status: 'new',
|
||||
path: '.gitattributes'
|
||||
},
|
||||
{
|
||||
status: 'new',
|
||||
path: '.gitignore'
|
||||
},
|
||||
{
|
||||
status: 'new',
|
||||
path: 'file.txt'
|
||||
}
|
||||
]);
|
||||
|
||||
await git.commit('add file.txt');
|
||||
|
||||
FileSystem.instance.writeFileSync(tempDir + 'file.txt', 'text2');
|
||||
|
||||
const status2 = await git.status();
|
||||
expect(status2).toEqual([
|
||||
{
|
||||
status: 'modified',
|
||||
path: 'file.txt'
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
13
packages/noodl-editor/tests/git/index.ts
Normal file
13
packages/noodl-editor/tests/git/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export * from './git-diff.spec';
|
||||
export * from './git-local.spec';
|
||||
export * from './git-local-merge.spec';
|
||||
export * from './git-real-project-tests.spec';
|
||||
export * from './git-log.spec';
|
||||
export * from './git-clone-and-fetch-remote.spec';
|
||||
export * from './git-remote-merge.spec';
|
||||
export * from './git-remote-squash.spec';
|
||||
export * from './git-remote.spec';
|
||||
export * from './git-stash-merge.spec';
|
||||
export * from './git-stash.spec';
|
||||
export * from './git-status.spec';
|
||||
export * from './git-local-misc.spec';
|
||||
Reference in New Issue
Block a user