Files
OpenNoodl/packages/noodl-editor/tests/git/git-local.spec.ts
Michael Cartner b9c60b07dc 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>
2024-01-26 11:52:55 +01:00

404 lines
13 KiB
TypeScript

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'
};
}
});