Skip to content

Commit

Permalink
Add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgenherje committed Oct 5, 2023
1 parent 0cf6a85 commit 73d54d5
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { resolveClassNames } from "@lib/utils/resolveClassNames";

import { isEqual } from "lodash";

// Icons placed here due to limitation of jest for testing utils (cannot import svg)
import checkIcon from "./private-assets/check.svg";
import segmentIcon from "./private-assets/segment.svg";
import {
ParameterParentNodeNames,
createTreeDataNodeListFromParameters,
Expand All @@ -29,7 +32,7 @@ export const ParameterListFilter: React.FC<ParameterListFilterProps> = (props: P

let candidateTreeDataNodeList = treeDataNodeList;
if (parameters === null || !isEqual(props.parameters, parameters)) {
candidateTreeDataNodeList = createTreeDataNodeListFromParameters([...props.parameters]);
candidateTreeDataNodeList = createTreeDataNodeListFromParameters([...props.parameters], checkIcon, segmentIcon);
setParameters(props.parameters);
setTreeDataNodeList(candidateTreeDataNodeList);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { Parameter, ParameterIdent, ParameterType } from "@framework/EnsembleParameters";
import { TreeDataNode } from "@lib/components/SmartNodeSelector";

import checkIcon from "../private-assets/check.svg";
import segmentIcon from "../private-assets/segment.svg";

export const ParameterParentNodeNames = {
NAME: "Name",
GROUP: "Group",
Expand All @@ -25,13 +22,13 @@ export function fromParameterTypeToNodeName(type: ParameterType): string {
throw new Error(`Parameter type ${type} not supported`);
}

function createAndAddNode(treeNodeDataList: TreeDataNode[], nodeName: string, icon?: string): TreeDataNode {
export function createAndAddNode(treeNodeDataList: TreeDataNode[], nodeName: string, icon?: string): TreeDataNode {
const newNode: TreeDataNode = { name: nodeName, description: "", icon: icon };
treeNodeDataList.push(newNode);
return newNode;
}

function findOrCreateNode(treeNodeDataList: TreeDataNode[], nodeName: string, icon?: string): TreeDataNode {
export function findOrCreateNode(treeNodeDataList: TreeDataNode[], nodeName: string, icon?: string): TreeDataNode {
const existingNode = treeNodeDataList.find((node) => node.name === nodeName);
if (existingNode) {
return existingNode;
Expand All @@ -40,25 +37,33 @@ function findOrCreateNode(treeNodeDataList: TreeDataNode[], nodeName: string, ic
return createAndAddNode(treeNodeDataList, nodeName, icon);
}

function addParameterNameAndGroupToTreeDataNodeList(treeNodeDataList: TreeDataNode[], parameter: Parameter): void {
export function addParameterNameAndGroupToTreeDataNodeList(
treeNodeDataList: TreeDataNode[],
parameter: Parameter,
icon?: string
): void {
// Parameter Name
const nameParentNode = findOrCreateNode(treeNodeDataList, ParameterParentNodeNames.NAME, segmentIcon);
const nameParentNode = findOrCreateNode(treeNodeDataList, ParameterParentNodeNames.NAME, icon);
if (!nameParentNode.children) {
nameParentNode.children = [];
}
findOrCreateNode(nameParentNode.children, parameter.name);

// Parameter Group
if (parameter.groupName) {
const groupParentNode = findOrCreateNode(treeNodeDataList, ParameterParentNodeNames.GROUP, segmentIcon);
const groupParentNode = findOrCreateNode(treeNodeDataList, ParameterParentNodeNames.GROUP, icon);
if (!groupParentNode.children) {
groupParentNode.children = [];
}
findOrCreateNode(groupParentNode.children, parameter.groupName);
}
}

export function createTreeDataNodeListFromParameters(parameters: Parameter[]): TreeDataNode[] {
export function createTreeDataNodeListFromParameters(
parameters: Parameter[],
checkIcon?: string,
parentIcon?: string
): TreeDataNode[] {
if (parameters.length === 0) {
return [];
}
Expand All @@ -78,7 +83,7 @@ export function createTreeDataNodeListFromParameters(parameters: Parameter[]): T

// Add name and group for parameters
for (const parameter of parameters) {
addParameterNameAndGroupToTreeDataNodeList(treeDataNodeList, parameter);
addParameterNameAndGroupToTreeDataNodeList(treeDataNodeList, parameter, parentIcon);
}

return treeDataNodeList;
Expand All @@ -97,24 +102,39 @@ export function getParametersMatchingSelectedNodes(parameters: Parameter[], sele
.map((node) => node.split(delimiter, 2)[1]);
};

// Get parameter property filters
const selectedParameterNames = findSelectedParameterPropertiesFromName(ParameterParentNodeNames.NAME);
const selectedParameterGroups = findSelectedParameterPropertiesFromName(ParameterParentNodeNames.GROUP);
const isContinuousSelected = selectedNodes.includes(ParameterParentNodeNames.CONTINUOUS);
const isDiscreteSelected = selectedNodes.includes(ParameterParentNodeNames.DISCRETE);
const isConstantSelected = selectedNodes.includes(ParameterParentNodeNames.IS_CONSTANT);
const isNonConstantSelected = selectedNodes.includes(ParameterParentNodeNames.IS_NONCONSTANT);
const isLogarithmicSelected = selectedNodes.includes(ParameterParentNodeNames.IS_LOGARITHMIC);
const isLinearSelected = selectedNodes.includes(ParameterParentNodeNames.IS_LINEAR);

// Intersection filtering, i.e. parameter cannot be both continuous and discrete, constant and non-constant, logarithmic and linear
if (isContinuousSelected && isDiscreteSelected) return [];
if (isConstantSelected && isNonConstantSelected) return [];
if (isLogarithmicSelected && isLinearSelected) return [];

const selectedParameterNames = findSelectedParameterPropertiesFromName(ParameterParentNodeNames.NAME);
const selectedParameterGroups = findSelectedParameterPropertiesFromName(ParameterParentNodeNames.GROUP);

// Prevent invalid nodes
if (
!isContinuousSelected &&
!isDiscreteSelected &&
!isConstantSelected &&
!isNonConstantSelected &&
!isLogarithmicSelected &&
!isLinearSelected &&
selectedParameterNames.length === 0 &&
selectedParameterGroups.length === 0
) {
return [];
}

const selectedEnsembleParameters: Parameter[] = [];
for (const parameter of parameters) {
// Filter by parameter name
if (selectedParameterNames.length > 0 && !selectedParameterNames.includes(parameter.name)) {
if (selectedParameterNames.length !== 0 && !selectedParameterNames.includes(parameter.name)) {
continue;
}

Expand All @@ -138,7 +158,7 @@ export function getParametersMatchingSelectedNodes(parameters: Parameter[], sele
if (isConstantSelected && !parameter.isConstant) continue;
if (isNonConstantSelected && parameter.isConstant) continue;

// Filter by parameter is logarithmic/linear
// Filter by parameter is logarithmic/linear (only for continuous parameters)
if (isLogarithmicSelected && parameter.type === ParameterType.CONTINUOUS && !parameter.isLogarithmic) continue;
if (isLinearSelected && parameter.type === ParameterType.CONTINUOUS && parameter.isLogarithmic) continue;

Expand Down
163 changes: 163 additions & 0 deletions frontend/tests/unit-tests/ParameterListFilterUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { ContinuousParameter, DiscreteParameter, Parameter, ParameterType } from "@framework/EnsembleParameters";
import {
addParameterNameAndGroupToTreeDataNodeList,
createAndAddNode,
createTreeDataNodeListFromParameters,
findOrCreateNode,
fromParameterTypeToNodeName,
getParametersMatchingSelectedNodes,
} from "@framework/components/ParameterListFilter/private-utils/smartNodeSelectorUtils";
import { TreeDataNode } from "@lib/components/SmartNodeSelector";

const CONTINUOUS_PARAMETER: ContinuousParameter = {
type: ParameterType.CONTINUOUS,
name: "continuous parameter",
groupName: "group1",
description: "continuous parameter description",
isConstant: false,
isLogarithmic: false,
realizations: [1, 2, 3],
values: [10, 11, 12],
};

const SECOND_CONTINUOUS_PARAMETER: ContinuousParameter = {
type: ParameterType.CONTINUOUS,
name: "second continuous parameter",
groupName: "group1",
description: "continuous parameter description 2",
isConstant: true,
isLogarithmic: false,
realizations: [1, 2, 3],
values: [10, 11, 12],
};

const DISCRETE_PARAMETER: DiscreteParameter = {
type: ParameterType.DISCRETE,
name: "discrete parameter",
groupName: "group2",
description: "discrete parameter description",
isConstant: true,
realizations: [1, 2, 3],
values: [10, 11, 12],
};

describe("Test of utility functions for ParameterListFilter", () => {
test("Check from parameter type to node name conversion", () => {
expect(fromParameterTypeToNodeName(ParameterType.CONTINUOUS)).toBe("Continuous");
expect(fromParameterTypeToNodeName(ParameterType.DISCRETE)).toBe("Discrete");
});

test("Test create and add node", () => {
const myTestList: TreeDataNode[] = [];
const newNode = createAndAddNode(myTestList, "my node");
expect(newNode.name).toBe("my node");
expect(newNode.name).toBe(myTestList[0].name);
});

test("Test find node", () => {
const testNode = { name: "my node", description: "", icon: undefined };
const testNodes: TreeDataNode[] = [testNode];
const foundNode = findOrCreateNode(testNodes, "my node");
expect(foundNode.name).toBe("my node");
expect(testNodes.length).toBe(1);
});

test("Test create node", () => {
const testNodes: TreeDataNode[] = [];
const createdNode = findOrCreateNode(testNodes, "my node");
expect(createdNode.name).toBe("my node");
expect(testNodes.length).toBe(1);
});

test("Add parameter name and group to tree data node list", () => {
const testNodes: TreeDataNode[] = [];
addParameterNameAndGroupToTreeDataNodeList(testNodes, CONTINUOUS_PARAMETER);
expect(testNodes.length).toBe(2);
expect(testNodes[0].name).toBe("Name");
expect(testNodes[0].children?.length).toBe(1);
expect(testNodes[0].children?.[0].name).toBe("continuous parameter");
expect(testNodes[1].name).toBe("Group");
expect(testNodes[1].children?.length).toBe(1);
expect(testNodes[1].children?.[0].name).toBe("group1");

addParameterNameAndGroupToTreeDataNodeList(testNodes, DISCRETE_PARAMETER);
expect(testNodes.length).toBe(2);
expect(testNodes[0].name).toBe("Name");
expect(testNodes[0].children?.length).toBe(2);
expect(testNodes[0].children?.[0].name).toBe("continuous parameter");
expect(testNodes[0].children?.[1].name).toBe("discrete parameter");
expect(testNodes[1].name).toBe("Group");
expect(testNodes[1].children?.length).toBe(2);
expect(testNodes[1].children?.[0].name).toBe("group1");
expect(testNodes[1].children?.[1].name).toBe("group2");
});

test("Create tree data node list from parameters", () => {
const parameterList = [CONTINUOUS_PARAMETER, DISCRETE_PARAMETER];
const testNodes = createTreeDataNodeListFromParameters(parameterList);

expect(testNodes.length).toBe(8);
expect(testNodes[0].name).toBe("Continuous");
expect(testNodes[0].children).toBe(undefined);
expect(testNodes[1].name).toBe("Discrete");
expect(testNodes[1].children).toBe(undefined);
expect(testNodes[2].name).toBe("Constant");
expect(testNodes[2].children).toBe(undefined);
expect(testNodes[3].name).toBe("Nonconstant");
expect(testNodes[3].children).toBe(undefined);
expect(testNodes[4].name).toBe("Logarithmic");
expect(testNodes[4].children).toBe(undefined);
expect(testNodes[5].name).toBe("Linear");
expect(testNodes[5].children).toBe(undefined);
expect(testNodes[6].name).toBe("Name");
expect(testNodes[6].children?.length).toBe(2);
expect(testNodes[6].children?.[0].name).toBe("continuous parameter");
expect(testNodes[6].children?.[1].name).toBe("discrete parameter");
expect(testNodes[7].name).toBe("Group");
expect(testNodes[7].children?.length).toBe(2);
expect(testNodes[7].children?.[0].name).toBe("group1");
expect(testNodes[7].children?.[1].name).toBe("group2");
});

test("Get parameters matching selected nodes - invalid/conflicting", () => {
const parameterList = [CONTINUOUS_PARAMETER, DISCRETE_PARAMETER];

const invalidNodes = ["Invalid node"];
expect(getParametersMatchingSelectedNodes(parameterList, invalidNodes)).toEqual([]);
const conflictingNodes = ["Continuous", "Discrete"];
expect(getParametersMatchingSelectedNodes(parameterList, conflictingNodes)).toEqual([]);
});

test("Get parameters matching selected nodes - valid", () => {
const parameterList = [CONTINUOUS_PARAMETER, DISCRETE_PARAMETER];

expect(getParametersMatchingSelectedNodes(parameterList, ["Group:group1"])).toEqual([CONTINUOUS_PARAMETER]);
expect(getParametersMatchingSelectedNodes(parameterList, ["Name:discrete parameter"])).toEqual([
DISCRETE_PARAMETER,
]);
});

test("Get parameters matching selected nodes - multiple matches", () => {
const parameterList = [CONTINUOUS_PARAMETER, SECOND_CONTINUOUS_PARAMETER, DISCRETE_PARAMETER];

expect(getParametersMatchingSelectedNodes(parameterList, ["Group:group1"])).toEqual([
CONTINUOUS_PARAMETER,
SECOND_CONTINUOUS_PARAMETER,
]);
expect(getParametersMatchingSelectedNodes(parameterList, ["Constant"])).toEqual([
SECOND_CONTINUOUS_PARAMETER,
DISCRETE_PARAMETER,
]);
});

test("Get parameters matching selected nodes - multiple selected nodes", () => {
const parameterList = [CONTINUOUS_PARAMETER, SECOND_CONTINUOUS_PARAMETER, DISCRETE_PARAMETER];

expect(getParametersMatchingSelectedNodes(parameterList, ["Group:group1", "Constant"])).toEqual([
SECOND_CONTINUOUS_PARAMETER,
]);
expect(getParametersMatchingSelectedNodes(parameterList, ["Invalid Node", "Discrete"])).toEqual([
DISCRETE_PARAMETER,
]);
});
});

0 comments on commit 73d54d5

Please sign in to comment.