added parser unit tests and organized config in gitGraphAst.ts

This commit is contained in:
Austin Fulbright
2024-08-10 10:37:24 -04:00
parent 62950c31a4
commit 269284c6d7
4 changed files with 121 additions and 130 deletions

View File

@@ -1,74 +0,0 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { parser } from './gitGraphParser.js';
import { db } from './gitGraphAst.js';
const parseInput = async (input: string) => {
await parser.parse(input);
};
const spyOn = vi.spyOn;
describe('GitGraph Parsing', function () {
beforeEach(() => {
db.clear();
});
it('should parse a default commit statement', async () => {
const input = `gitGraph:
commit
`;
const commitSpy = spyOn(db, 'commit');
await parseInput(input);
expect(commitSpy).toHaveBeenCalledWith('', undefined, 0, []);
commitSpy.mockRestore();
});
it('should parse a basic branch statement with just a name', async () => {
const input = `gitGraph:
branch newBranch
`;
const branchSpy = spyOn(db, 'branch');
await parseInput(input);
expect(branchSpy).toHaveBeenCalledWith('newBranch', 0);
branchSpy.mockRestore();
});
it('should parse a basic checkout statement', async () => {
const input = `gitGraph:
branch newBranch
checkout newBranch
`;
const checkoutSpy = spyOn(db, 'checkout');
await parseInput(input);
expect(checkoutSpy).toHaveBeenCalledWith('newBranch');
checkoutSpy.mockRestore();
});
it('should parse a basic merge statement', async () => {
const input = `gitGraph:
commit
branch newBranch
checkout newBranch
commit
checkout main
merge newBranch`;
const mergeSpy = spyOn(db, 'merge');
await parseInput(input);
expect(mergeSpy).toHaveBeenCalledWith('newBranch', '', undefined, []);
mergeSpy.mockRestore();
});
it('should parse cherry-picking', async () => {
const input = `gitGraph
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
cherry-pick id:"A"
`;
const cherryPickSpy = spyOn(db, 'cherryPick');
await parseInput(input);
expect(cherryPickSpy).toHaveBeenCalledWith('A', '', undefined, undefined);
cherryPickSpy.mockRestore();
});
});

View File

@@ -11,9 +11,10 @@ import type {
MergeAst,
CommitAst,
BranchAst,
GitGraphDBProvider,
} from './gitGraphTypes.js';
const populate = (ast: GitGraph) => {
const populate = (ast: GitGraph, db: GitGraphDBProvider) => {
populateCommonDb(ast, db);
// @ts-ignore: this wont exist if the direction is not specified
if (ast.dir) {
@@ -21,71 +22,107 @@ const populate = (ast: GitGraph) => {
db.setDirection(ast.dir);
}
for (const statement of ast.statements) {
parseStatement(statement);
parseStatement(statement, db);
}
};
const parseStatement = (statement: any) => {
switch (statement.$type) {
case 'Commit':
parseCommit(statement);
break;
case 'Branch':
parseBranch(statement);
break;
case 'Merge':
parseMerge(statement);
break;
case 'Checkout':
parseCheckout(statement);
break;
case 'CherryPicking':
parseCherryPicking(statement);
break;
default:
log.error(`Unknown statement type`);
const parseStatement = (statement: any, db: GitGraphDBProvider) => {
const parsers: Record<string, (stmt: any) => void> = {
Commit: (stmt) => db.commit(...parseCommit(stmt)),
Branch: (stmt) => db.branch(...parseBranch(stmt)),
Merge: (stmt) => db.merge(...parseMerge(stmt)),
Checkout: (stmt) => db.checkout(parseCheckout(stmt)),
CherryPicking: (stmt) => db.cherryPick(...parseCherryPicking(stmt)),
};
const parser = parsers[statement.$type];
if (parser) {
parser(statement);
} else {
log.error(`Unknown statement type: ${statement.$type}`);
}
};
const parseCommit = (commit: CommitAst) => {
const parseCommit = (commit: CommitAst): [string, string, number, string[] | undefined] => {
const id = commit.id;
const message = commit.message ?? '';
const type = commit.type !== undefined ? commitType[commit.type] : commitType.NORMAL;
const tags = commit.tags ?? undefined;
db.commit(message, id, type, tags);
return [message, id, type, tags];
};
const parseBranch = (branch: BranchAst) => {
const parseBranch = (branch: BranchAst): [string, number] => {
const name = branch.name;
const order = branch.order ?? 0;
db.branch(name, order);
return [name, order];
};
const parseMerge = (merge: MergeAst) => {
const parseMerge = (
merge: MergeAst
): [string, string, number | undefined, string[] | undefined] => {
const branch = merge.branch;
const id = merge.id ?? '';
const type = merge.type !== undefined ? commitType[merge.type] : undefined;
const tags = merge.tags ?? undefined;
db.merge(branch, id, type, tags);
return [branch, id, type, tags];
};
const parseCheckout = (checkout: CheckoutAst) => {
const parseCheckout = (checkout: CheckoutAst): string => {
const branch = checkout.branch;
db.checkout(branch);
return branch;
};
const parseCherryPicking = (cherryPicking: CherryPickingAst) => {
const parseCherryPicking = (
cherryPicking: CherryPickingAst
): [string, string, string[] | undefined, string] => {
const id = cherryPicking.id;
const tags = cherryPicking.tags?.length === 0 ? undefined : cherryPicking.tags;
const parent = cherryPicking.parent;
db.cherryPick(id, '', tags, parent);
return [id, '', tags, parent];
};
export const parser: ParserDefinition = {
parse: async (input: string): Promise<void> => {
const ast: GitGraph = await parse('gitGraph', input);
log.debug(ast);
populate(ast);
populate(ast, db);
},
};
if (import.meta.vitest) {
const { it, expect, describe } = import.meta.vitest;
const mockDB: GitGraphDBProvider = {
commitType: commitType,
setDirection: vi.fn(),
commit: vi.fn(),
branch: vi.fn(),
merge: vi.fn(),
cherryPick: vi.fn(),
checkout: vi.fn(),
};
describe('GitGraph Parser', () => {
it('should parse a commit statement', () => {
const commit = {
$type: 'Commit',
id: '1',
message: 'test',
tags: ['tag1', 'tag2'],
type: 'NORMAL',
};
parseStatement(commit, mockDB);
expect(mockDB.commit).toHaveBeenCalledWith('test', '1', 0, ['tag1', 'tag2']);
});
it('should parse a branch statement', () => {
const branch = {
$type: 'Branch',
name: 'newBranch',
order: 1,
};
parseStatement(branch, mockDB);
expect(mockDB.branch).toHaveBeenCalledWith('newBranch', 1);
});
});
}

View File

@@ -4,7 +4,12 @@ import { log } from '../../logger.js';
import utils from '../../utils.js';
import type { DrawDefinition } from '../../diagram-api/types.js';
import type d3 from 'd3';
import type { CommitType, Commit, GitGraphDB, DiagramOrientation } from './gitGraphTypes.js';
import type {
CommitType,
Commit,
GitGraphDBRenderProvider,
DiagramOrientation,
} from './gitGraphTypes.js';
import type { GitGraphDiagramConfig } from '../../config.type.js';
let allCommitsDict = new Map();
@@ -770,7 +775,9 @@ const drawArrow = (
}
}
}
if (lineDef === undefined) {
throw new Error('Line definition not found');
}
svg
.append('path')
.attr('d', lineDef)
@@ -889,7 +896,7 @@ export const draw: DrawDefinition = function (txt, id, ver, diagObj) {
throw new Error('GitGraph config not found');
}
const rotateCommitLabel = gitGraphConfig.rotateCommitLabel ?? false;
const db = diagObj.db as GitGraphDB;
const db = diagObj.db as GitGraphDBRenderProvider;
allCommitsDict = db.getCommits();
const branches = db.getBranchesAsObjArray();
dir = db.getDirection();

View File

@@ -1,5 +1,5 @@
import type { DiagramDB } from '../../diagram-api/types.js';
import type { GitGraphDiagramConfig } from '../../config.type.js';
import type { DiagramDBBase } from '../../diagram-api/types.js';
export interface CommitType {
NORMAL: number;
@@ -66,27 +66,13 @@ export interface CherryPickingAst {
parent: string;
}
export interface GitGraphDB extends DiagramDB {
// config
getConfig: () => GitGraphDiagramConfig | undefined;
// common db
clear: () => void;
setDiagramTitle: (title: string) => void;
getDiagramTitle: () => string;
setAccTitle: (title: string) => void;
getAccTitle: () => string;
setAccDescription: (description: string) => void;
getAccDescription: () => string;
// diagram db
export interface GitGraphDB extends DiagramDBBase<GitGraphDiagramConfig> {
commitType: CommitType;
setDirection: (direction: DiagramOrientation) => void;
getDirection: () => DiagramOrientation;
setOptions: (options: string) => void;
getOptions: () => string;
commit: (msg: string, id: string, type: number, tags?: string[] | undefined) => void;
branch: (name: string, order: number) => void;
setDirection: (dir: DiagramOrientation) => void;
setOptions: (rawOptString: string) => void;
getOptions: () => any;
commit: (msg: string, id: string, type: number, tags?: string[]) => void;
branch: (name: string, order?: number) => void;
merge: (
otherBranch: string,
customId?: string,
@@ -101,12 +87,47 @@ export interface GitGraphDB extends DiagramDB {
) => void;
checkout: (branch: string) => void;
prettyPrint: () => void;
clear: () => void;
getBranchesAsObjArray: () => { name: string }[];
getBranches: () => Map<string, string | null>;
getCommits: () => Map<string, Commit>;
getCommitsArray: () => Commit[];
getCurrentBranch: () => string;
getDirection: () => DiagramOrientation;
getHead: () => Commit | null;
}
export interface GitGraphDBParseProvider extends Partial<GitGraphDB> {
commitType: CommitType;
setDirection: (dir: DiagramOrientation) => void;
commit: (msg: string, id: string, type: number, tags?: string[]) => void;
branch: (name: string, order?: number) => void;
merge: (
otherBranch: string,
customId?: string,
overrideType?: number,
customTags?: string[]
) => void;
cherryPick: (
sourceId: string,
targetId: string,
tags: string[] | undefined,
parentCommitId: string
) => void;
checkout: (branch: string) => void;
}
export interface GitGraphDBRenderProvider extends Partial<GitGraphDB> {
prettyPrint: () => void;
clear: () => void;
getBranchesAsObjArray: () => { name: string }[];
getBranches: () => Map<string, string | null>;
getCommits: () => Map<string, Commit>;
getCommitsArray: () => Commit[];
getCurrentBranch: () => string;
getDirection: () => DiagramOrientation;
getHead: () => Commit | null;
getDiagramTitle: () => string;
}
export type DiagramOrientation = 'LR' | 'TB' | 'BT';