Skip to content

Commit

Permalink
Enhance testing and tree formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-oser committed Mar 6, 2022
1 parent 221c885 commit d2359cf
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 34 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@structure-codes/utils",
"version": "0.0.2",
"description": "A library to test if a filename is considered a to be a standard non-dotted dotfile",
"description": "A library containing utilities to convert tree structures between JSON and string formats",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"exports": {
Expand All @@ -11,7 +11,8 @@
"scripts": {
"build": "rollup -c rollup.config.js",
"dev": "rollup -w -c rollup.config.js",
"test": "jest --verbose"
"test": "jest --verbose",
"coverage": "jest --coverage"
},
"repository": {
"type": "git",
Expand All @@ -38,5 +39,8 @@
"ts-jest": "^27.0.7",
"ts-node": "^10.4.0",
"typescript": "^4.4.4"
},
"dependencies": {
"@structure-codes/is-nondot": "^1.1.2"
}
}
60 changes: 53 additions & 7 deletions src/__tests__/tree.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { treeStringToJson } from "../treeStringToJson";
import * as fs from "fs";
import * as path from "path";
import { TreeType } from "../types";
import { defaultSettings, ISettings, TreeType } from "../types";
import { treeJsonToString } from "../treeJsonToString";

const dirPath = path.join(__dirname, "trees");
const trees = fs.readdirSync(dirPath);
trees.forEach((file) => {
const treeString = fs.readFileSync(path.join(dirPath, file)).toString();
const tabTrees = fs.readdirSync(dirPath + "/tab-tests");
const settingsTree = fs.readFileSync(dirPath + "/settings-tests/base.tree").toString();
const noDotsTree = fs.readFileSync(dirPath + "/settings-tests/no-dots.tree").toString();
const noFilesTree = fs.readFileSync(dirPath + "/settings-tests/no-files.tree").toString();
const depth2Tree = fs.readFileSync(dirPath + "/settings-tests/depth-2.tree").toString();

tabTrees.forEach((file) => {
const treeString = fs.readFileSync(path.join(dirPath + "/tab-tests", file)).toString();
let tree: TreeType[];
test(`can convert file to TreeType format: ${file}`, () => {
test(`treeStringToJson can convert file to TreeType format: ${file}`, () => {
tree = treeStringToJson(treeString);

// Number of parents
Expand All @@ -18,8 +23,49 @@ trees.forEach((file) => {
expect(tree[2].name).toBe("static");
});

test("can convert TreeType format back to original string", () => {
const newTreeString = treeJsonToString(tree);
test("treeJsonToString can convert TreeType format back to original string", () => {
const newTreeString = treeJsonToString({ tree });
expect(treeString === newTreeString);
});

});


test("treeJsonToString base options works", () => {
const baseTree = treeStringToJson(settingsTree);
const newTreeString = treeJsonToString({ tree: baseTree, options: defaultSettings });
expect(settingsTree === newTreeString);
});

test("treeJsonToString depth option works", () => {
const options: ISettings = {
...defaultSettings,
depth: 2,
};
const baseTree = treeStringToJson(settingsTree);
const newTreeString = treeJsonToString({ tree: baseTree, options });
expect(settingsTree !== newTreeString);
expect(depth2Tree === newTreeString);
});

test("treeJsonToString hideDots option works", () => {
const options: ISettings = {
...defaultSettings,
hideDots: true,
};
const baseTree = treeStringToJson(settingsTree);
const newTreeString = treeJsonToString({ tree: baseTree, options });
expect(settingsTree !== newTreeString);
expect(noDotsTree === newTreeString);
});

test("treeJsonToString hideFiles option works", () => {
const options: ISettings = {
...defaultSettings,
hideFiles: true,
};
const baseTree = treeStringToJson(settingsTree);
const newTreeString = treeJsonToString({ tree: baseTree, options });
expect(settingsTree !== newTreeString);
expect(noFilesTree === newTreeString);
});
28 changes: 28 additions & 0 deletions src/__tests__/trees/settings-tests/base.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
//
├── Jenkinsfile
├── LICENSE
├── config.js
├── .git
│ ├── articles
│ └── utils
├── functions
├── src
│ ├── articles
│ ├── components
│ │ ├── builder
│ │ │ ├── center
│ │ │ ├── left
│ │ │ │ └── sections-asdf //sdfa
│ │ │ ├── lists.sdf
│ │ │ └── right
│ │ │ └── sections.txt
│ │ ├── dashboard.js
│ │ ├── landing.ts
│ │ ├── router.jsx
│ │ └── shared
│ └── utils
└── static.xzxxx
└── images
├── screenshots
└── templates.txt
15 changes: 15 additions & 0 deletions src/__tests__/trees/settings-tests/depth-2.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
//
├── Jenkinsfile
├── LICENSE
├── config.js
├── .git
│ ├── articles
│ └── utils
├── functions
├── src
│ └── articles
│ └── components
│ └── utils
└── static.xzxxx
└── images
16 changes: 16 additions & 0 deletions src/__tests__/trees/settings-tests/no-dots.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
//
├── functions
├── src
│ ├── articles
│ ├── components
│ │ ├── builder
│ │ │ ├── center
│ │ │ ├── left
│ │ │ │ └── sections-asdf //sdfa
│ │ │ └── right
│ │ └── shared
│ └── utils
└── static.xzxxx
└── images
└── screenshots
19 changes: 19 additions & 0 deletions src/__tests__/trees/settings-tests/no-files.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
//
├── .git
│ ├── articles
│ └── utils
├── functions
├── src
│ ├── articles
│ ├── components
│ │ ├── builder
│ │ │ ├── center
│ │ │ ├── left
│ │ │ │ └── sections-asdf //sdfa
│ │ │ └── right
│ │ └── shared
│ └── utils
└── static.xzxxx
└── images
└── screenshots
File renamed without changes.
File renamed without changes.
File renamed without changes.
43 changes: 36 additions & 7 deletions src/treeJsonToString.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,54 @@
import { getBranchPrefix } from "./utils";
import { ISettings, TreeType } from "./types";
import { defaultSettings, ISettings, TreeType } from "./types";
// @ts-ignore
import isNondot from "@structure-codes/is-nondot";

/**
* Converts JSON representation of a tree back to string format
* @param tree
* @param {TreeType[]} tree - The tree to convert to a string
* @param {string} tabChar - The character(s) used to represent a tab in the string output (optional)
* @param {ISettings} options - Parameters which can influence how the output string is generated (optional)
* @returns string format of tree
*/
export const treeJsonToString = (tree: TreeType[], tabChar = " ", options?: ISettings): string => {
export const treeJsonToString = ({
tree,
tabChar = " ",
options = defaultSettings,
}: {
tree: TreeType[];
tabChar?: string;
options?: ISettings;
}): string => {
let treeString = "";
const parseBranches = (tree: TreeType[], depth: boolean[]) => {
tree.forEach((branch, index) => {
// Hide all dot files and directories in output
if (
options.hideDots &&
(branch.name.startsWith(".") || isNondot(branch.name))
)
return;
// Hide all files in output
// Note: All leaves with 0 children may not be directories, so we do our best to infer here
if (
options.hideFiles &&
branch.children.length === 0 &&
(branch.name.includes(".") || isNondot(branch.name))
)
return;
// Hide leaves below a certain depth
if (options.depth > 0 && depth.length + 1 > options.depth) return;

const isLastBranch = index === tree.length - 1;
const prefix = getBranchPrefix(depth, isLastBranch, tabChar);
const branchString = prefix + branch.name + "\n";
treeString = treeString.concat(branchString);
if (!branch.children) return;
parseBranches(branch.children, [...depth, isLastBranch]);
const newDepth = [...depth, isLastBranch];
parseBranches(branch.children, newDepth);
});
};
parseBranches(tree, []);
treeString = treeString.replace(/\n$/, "");

return treeString;
};
};
33 changes: 17 additions & 16 deletions src/treeStringToJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ export const INDEX_NAME = uuid();

/**
* Converts a tree string output to a json object
* @param text
* @param includeIndex whether or not the results need to include index
* @param text - String to convert to a tree JSON object
* @returns JSON object representing the tree
*/
export const treeStringToJson = (text: string): TreeType[] => {
const elements: TreeType[] = [];
// Current location in the tree to add new children too
let current: TreeType[] = elements;
// Path is a stack that contains nodes at each level of current position in tree
const path: TreeType[] = [];
let prevLine = "";
const path: string[] = [];

const tabChar = getTabChar(text);
if (!tabChar) {
Expand All @@ -29,33 +31,32 @@ export const treeStringToJson = (text: string): TreeType[] => {
// look for line breaks that works on all platforms
treeFormatted.split(EOL_MATCH).forEach((line, index) => {
const isTreeFormat = line.match(/^(\t+)?(│|├──|└──|\t)+.+/);
if (!isTreeFormat) return {};
if (!isTreeFormat) return;
const prevPrefix = prevLine.split(" ")[0];
const prevNumTabs = getNumberOfTabs(prevPrefix);
// thanks to our formatting, a space will always separate prefix and suffix
const prefix = line.split(" ")[0];
const numTabs = getNumberOfTabs(prefix);
const filename: string = line.substr(prefix.length).trim();
const filename: string = line.substring(prefix.length).trim();
// Pop a certain number of elements from path
const popCount = numTabs <= prevNumTabs ? prevNumTabs - numTabs + 1 : 0;
Array(popCount)
.fill("pop")
.forEach(() => path.pop());

// probably could be made more performant
let current = elements;
path.forEach((node) => {
const next = current?.find(c => c.name === node)?.children;
if (next) current = next;
});

current.push({
const node: TreeType = {
_index: index,
name: filename,
children: [],
index,
});
};

// If we are at root, add root node - else add it to previous parent's children
current = path.length > 0 ? path[path.length - 1].children : elements;
current.push(node);
// Add the new node to the path stack
path.push(node);

prevLine = line;
path.push(filename);
});

return elements;
Expand Down
22 changes: 20 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
export interface TreeType {
_index: number;
name: string;
index: number;
children: TreeType[]
attributes?: Record<string, string | number | boolean>,
}

/**
*
*/
export interface ISettings {
/**
* depth - How deep in the tree to print
*/
depth: number;
/**
* hideFiles - Whether or not to hide all files in output
*/
hideFiles: boolean;
hideDotDirs: boolean;
/**
* hideDots - Whether or not to hide dot directories and files in the output
*/
hideDots: boolean;
}

export const defaultSettings: ISettings = {
depth: 0,
hideFiles: false,
hideDots: false,
};

0 comments on commit d2359cf

Please sign in to comment.