Tests for toggleFolderOpenState();

folders
Rick Carlino 2019-11-27 07:01:39 -06:00
parent a579ace0e2
commit 2ee4268048
4 changed files with 125 additions and 19 deletions

View File

@ -0,0 +1,65 @@
import { FolderNode } from "../constants";
import { ingest } from "../data_transfer";
import { findFolder, toggleFolderOpenState } from "../actions";
import { times, sample } from "lodash";
// Folder structure used in tests:
// ├─ One
// ├─ Two
// │ └─ Three
// ├─ Four
// │ └─ Five
// ├─ Six
// │ └─ Seven
// │ ├─ Eight
// │ └─ Nine
// ├─ Ten
// │ ├─ Eleven
// │ └─ Twelve
// │ └─ Thirteen
// └─ Fourteen
// ├─ Fifteen
// └─ Sixteen
// ├─ Seventeen
// └─ Eighteen
const FOLDERS: FolderNode[] = [
{ id: 1, parent_id: undefined, color: "blue", name: "One" },
{ id: 2, parent_id: undefined, color: "blue", name: "Two" },
{ id: 3, parent_id: 2, color: "blue", name: "Three" },
{ id: 4, parent_id: undefined, color: "blue", name: "Four" },
{ id: 5, parent_id: 4, color: "blue", name: "Five" },
{ id: 6, parent_id: undefined, color: "blue", name: "Six" },
{ id: 7, parent_id: 6, color: "blue", name: "Seven" },
{ id: 8, parent_id: 7, color: "blue", name: "Eight" },
{ id: 9, parent_id: 7, color: "blue", name: "Nine" },
{ id: 10, parent_id: undefined, color: "blue", name: "Ten" },
{ id: 11, parent_id: 10, color: "blue", name: "Eleven" },
{ id: 12, parent_id: 10, color: "blue", name: "Twelve" },
{ id: 13, parent_id: 12, color: "blue", name: "Thirteen" },
{ id: 14, parent_id: undefined, color: "blue", name: "Fourteen" },
{ id: 15, parent_id: 14, color: "blue", name: "Fifteen" },
{ id: 16, parent_id: 14, color: "blue", name: "Sixteen" },
{ id: 17, parent_id: 16, color: "blue", name: "Seventeen" },
{ id: 18, parent_id: 16, color: "blue", name: "Eighteen" }
];
const GRAPH = ingest(FOLDERS);
describe("toggleFolderOpenState", () => {
it("toggles the `open` value of a folder", () => {
times(10, async () => {
const node = sample(FOLDERS);
if (!node) {
throw new Error("Impossible");
}
const { id } = node;
const before = findFolder(GRAPH, id);
const nextGraph = await toggleFolderOpenState(GRAPH, id);
const after = findFolder(nextGraph, id);
if (before && after) {
expect(after.open).toEqual(!before.open);
} else {
fail("Could not find ID.");
}
});
});
});

View File

@ -1,14 +1,44 @@
import {
RootFolderNode as Tree
RootFolderNode as Tree,
FolderUnion
} from "./constants";
import { cloneAndClimb } from "./climb";
export const toggleFolderState = (_: Tree, _id: number) => {
return Promise.resolve(_);
export const findFolder = (tree: Tree, id: number) => {
let result: FolderUnion | undefined;
cloneAndClimb(tree, (node, halt) => {
if (node.id === id) {
result = node;
halt();
}
});
return result;
};
export const expandAll = (_: Tree) => Promise.reject("WIP");
export const collapseAll = (_: Tree) => Promise.reject("WIP");
export const setFolderColor = (_: Tree) => Promise.reject("WIP");
export const setFolderName = (_: Tree) => Promise.reject("WIP");
export const toggleFolderOpenState = (tree: Tree, id: number) => {
return Promise.resolve(cloneAndClimb(tree, (node, halt) => {
if (node.id === id) {
node.open = !node.open;
halt();
}
}));
};
export const expandAll = (tree: Tree) => {
return Promise.resolve(cloneAndClimb(tree, (node) => {
node.open = true;
}));
};
export const collapseAll = (tree: Tree) => {
return Promise.resolve(cloneAndClimb(tree, (node) => {
node.open = false;
}));
};
export const setFolderColor = (_: Tree, _id: number) => Promise.reject("WIP");
export const setFolderName =
(_: Tree, _id: number, _name: string) => Promise.reject("WIP");
export const createFolder = (_: Tree) => Promise.reject("WIP");
export const deleteFolder = (_: Tree) => Promise.reject("WIP");
export const moveFolderItem = (_: Tree) => Promise.reject("WIP");

View File

@ -1,18 +1,21 @@
import { RootFolderNode, FolderUnion } from "./constants";
import { RootFolderNode, FolderUnion, FolderNodeMedial, FolderNodeInitial } from "./constants";
import { defensiveClone } from "../util";
interface TreeClimberState {
active: boolean;
}
interface VisitorProps {
node: FolderUnion;
node: FolderNodeInitial | FolderNodeMedial;
callback: TreeClimber;
halt: Halt;
state: TreeClimberState;
}
type Halt = () => void;
type TreeClimber = (t: FolderUnion, halt: Function) => void;
type TreeClimber = (t: FolderUnion,
/** Calling this function stops tree climb from continuing. */
halt: Function) => void;
function visit(p: VisitorProps) {
const { node, callback, halt } = p;
@ -30,6 +33,7 @@ function visit(p: VisitorProps) {
});
}
/** Recursively climb a directory structure. */
export const climb = (t: RootFolderNode, callback: TreeClimber) => {
const state: TreeClimberState = { active: true };
const halt = () => { state.active = false; };
@ -37,5 +41,11 @@ export const climb = (t: RootFolderNode, callback: TreeClimber) => {
const props = { node, callback, halt, state };
state.active && visit(props);
});
return [];
return t;
};
/** TODO: Create strategies for non-destructively
* transforming a RootFolderNode. */
export const cloneAndClimb = (t: RootFolderNode, callback: TreeClimber) => {
return climb(defensiveClone(t), callback);
};

View File

@ -13,13 +13,14 @@ const setDefaultParentId = (input: FolderNode): Required<FolderNode> => {
return { ...input, parent_id: input.parent_id || -1 };
};
const addToIndex =
(accumulator: FoldersIndexedByParentId, item: Required<FolderNode>) => {
const key = item.parent_id;
const value = accumulator[key] || [];
return { ...accumulator, [key]: [...value, item] };
};
type AddToIndex = (a: FoldersIndexedByParentId, i: Required<FolderNode>) =>
Record<number, FolderNode[] | undefined>;
const addToIndex: AddToIndex = (accumulator, item) => {
const key = item.parent_id;
const lastValue: FolderNode[] = accumulator[key] || [];
const nextValue: FolderNode[] = [...lastValue, item];
return { ...accumulator, [key]: nextValue };
};
const emptyIndex: FoldersIndexedByParentId = {};
@ -42,7 +43,7 @@ export function ingest(input: FolderNode[]): RootFolderNode {
content: []
});
const initial = (x: FolderNode) => (index[x.id].length) ?
const initial = (x: FolderNode) => (index[x.id] || []).length ?
medial(x) : terminal(x);
childrenOf(-1).map((root) => {