Tests for toggleFolderOpenState();
parent
a579ace0e2
commit
2ee4268048
|
@ -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.");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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) => {
|
||||
|
|
Loading…
Reference in New Issue