Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP]: test(UI+model): implement test code from plans and more #139

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ const config: Config = {
// ],

// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
// moduleNameMapper: {},
moduleNameMapper: {
},

// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
Expand Down
1,168 changes: 935 additions & 233 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
"prepare": "husky install",
"format-check": "prettier --check \"src/**/*.{ts,tsx}\"",
"test": "jest"
"test": "jest",
"testwatch": "jest --watch"
},
"dependencies": {
"@ant-design/icons": "^5.2.5",
"@react-three/drei": "^9.83.3",
"@react-three/fiber": "^8.13.7",
"@tensorflow/tfjs": "^4.11.0",
"@testing-library/jest-dom": "^6.1.3",
"@vitejs/plugin-react": "^4.1.0",
"antd": "^5.8.4",
"jest-environment-jsdom": "^29.7.0",
"jest-worker": "^29.7.0",
"onnxruntime-web": "^1.16.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -34,6 +38,8 @@
"@axe-core/react": "^4.7.3",
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@testing-library/dom": "^9.3.3",
"@testing-library/react": "^14.0.0",
"@types/react": "^18.2.22",
"@types/react-dom": "^18.2.7",
"@types/three": "^0.156.0",
Expand All @@ -52,6 +58,7 @@
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"file-url": "^4.0.0",
"husky": "^8.0.3",
"jest": "^29.7.0",
"lint-staged": "^14.0.1",
Expand Down
10 changes: 1 addition & 9 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import styled, { ThemeProvider } from 'styled-components';
import './App.css';
import Home from './pages';
import AboutPage from './pages/about';
import { SimulationParams } from './components/Simulation';
import { type IncomingMessage } from './workers/modelWorkerMessage';

const Main = styled.main`
Expand All @@ -31,9 +30,6 @@ function App(): React.ReactElement {
// save the current page in state
// 0 = home(index,simulation) 1 = about
const [page, setPage] = useState(0);
const [simulationParams, setSimulationParams] = useState<SimulationParams>(
new SimulationParams(),
);

const [lightTheme, setlightTheme] = useState<boolean>(false);
// TODO: implement auto theme ui switch
Expand Down Expand Up @@ -82,11 +78,7 @@ function App(): React.ReactElement {
case 0:
default:
mainPageComponent = (
<Home
worker={simWorker}
simulationParams={simulationParams}
setSimulationParams={setSimulationParams}
/>
<Home worker={simWorker} />
);
break;
}
Expand Down
33 changes: 16 additions & 17 deletions src/components/ParameterComponents/ParameterLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,23 @@ export default function ParameterLabel(props: {
title: string;
tooltip?: string;
}): React.ReactElement {
const tooltip: React.ReactElement[] = [];
if (props.tooltip !== undefined) {
tooltip.push(
<Styled>
<Tooltip
placement="right"
title={props.tooltip}
getPopupContainer={(tn) => tn}
>
<QuestionCircleOutlined />
</Tooltip>
</Styled>,
);
}

return (
<Lab>
{props.title}&nbsp;&nbsp;{tooltip}
<Lab key={props.title}>
<>
{props.title}&nbsp;&nbsp;
{ props.tooltip !== undefined &&
<Styled>
<Tooltip
placement="right"
title={props.tooltip}
key={props.tooltip}
getPopupContainer={(tn) => tn}
>
<QuestionCircleOutlined />
</Tooltip>
</Styled>
}
</>
</Lab>
);
}
17 changes: 11 additions & 6 deletions src/components/ParametersBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,13 @@ function ShowHideButton(props: {

return (
<BackButton
data-testid="back"

onClick={() => {
setVisible(!isVisible);
}}
>
{isVisible ? <DoubleLeftOutlined /> : <DoubleRightOutlined />}
{isVisible ? <DoubleLeftOutlined data-testid='leftarrow' /> : <DoubleRightOutlined data-testid='rightarrow' />}
</BackButton>
);
}
Expand All @@ -92,8 +94,8 @@ enum ControlDifficulty {
}

export default function ParametersBar(props: {
params: SimulationParams;
setParams: React.Dispatch<React.SetStateAction<SimulationParams>>;
params: SimulationParams | null;
setParams: React.Dispatch<React.SetStateAction<SimulationParams>> | null;
}): React.ReactElement {
const [isPaneVisible, setPaneVisible] = useState<boolean>(true);
const space: [SpaceSize, SpaceSize] = ['large', 'small'];
Expand All @@ -105,7 +107,7 @@ export default function ParametersBar(props: {

if (isPaneVisible) {
return (
<Container direction="vertical" size={space}>
<Container direction="vertical" size={space} data-testid="pane">
{/* hide button */}
<Row justify="end">
<ShowHideButton
Expand Down Expand Up @@ -166,7 +168,7 @@ export default function ParametersBar(props: {

// allows the user to change the colour of the simulation
function SimulationColour(props: {
setParams: React.Dispatch<React.SetStateAction<SimulationParams>>;
setParams: React.Dispatch<React.SetStateAction<SimulationParams>> | null;
}): React.ReactElement {
const setParams = props.setParams;

Expand All @@ -183,13 +185,16 @@ function SimulationColour(props: {
);

useEffect(() => {
if (setParams === null)
return

setParams((prev) => {
return {
...prev,
densityLowColour: new ThreeColor(colorLowString),
densityHighColour: new ThreeColor(colorHighString),
};
});
})
}, [colorLowString, colorHighString, setParams]);

return (
Expand Down
5 changes: 3 additions & 2 deletions src/components/Simulation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import {
} from '@react-three/fiber';
import type React from 'react';
import { useEffect, useMemo, useRef } from 'react';
import vertexShader from '../shaders/vert.glsl';
import fragmentShader from '../shaders/frag.glsl';

// WebGPU imports
import WebGPU from 'three/addons/capabilities/WebGPU.js';
import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';

import vertexShader from '../shaders/vert.glsl';
import fragmentShader from '../shaders/frag.glsl';

class SimulationParams {
// render options
densityLowColour: t.Color = new t.Color('blue');
Expand Down
13 changes: 8 additions & 5 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import ParBar from '../components/ParametersBar';
import ControlBar from '../components/ControlBar';
import {
DiffusionPlane,
type SimulationParams,
SimulationParams,
} from '../components/Simulation';
import { Canvas } from '@react-three/fiber';
import styled from 'styled-components';
import { useEffect, useMemo } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { type OutgoingMessage } from '../workers/modelWorkerMessage';
import { type ModelSave } from '../services/model/modelService';

Expand All @@ -33,13 +33,16 @@ const Simulator = styled(Canvas)`
`;

interface IndexProp {
simulationParams: SimulationParams;
setSimulationParams: React.Dispatch<React.SetStateAction<SimulationParams>>;
worker: Worker;
}

export default function Home(props: IndexProp): React.ReactElement {
const { simulationParams, setSimulationParams, worker } = props;
const { worker } = props;

const [simulationParams, setSimulationParams] = useState<SimulationParams>(
new SimulationParams(),
);

useEffect(() => {
const confirmExit = (e: BeforeUnloadEvent): void => {
console.log('beforeunload event triggered');
Expand Down
3 changes: 2 additions & 1 deletion src/services/model/TfjsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as tf from '@tensorflow/tfjs';
import { type Vector2 } from 'three';
import { type ModelService } from './modelService';

export class TfjsService implements ModelService {
export default class TfjsService implements ModelService {
model!: tf.GraphModel;
gridSize: [number, number];
batchSize: number;
Expand Down Expand Up @@ -37,6 +37,7 @@ export class TfjsService implements ModelService {
outputChannelSize = 3,
fpsLimit = 15,
): Promise<TfjsService> {

const service = new TfjsService();
service.model = await tf.loadGraphModel(modelPath);
service.gridSize = gridSize;
Expand Down
2 changes: 1 addition & 1 deletion src/services/model/modelService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type Vector2 } from 'three';
import { TfjsService } from './TfjsService';
import TfjsService from './TfjsService';
import ONNXService from './ONNXService';
import MockModelService from './MockModelService';

Expand Down
63 changes: 63 additions & 0 deletions tests/components/Parametersbar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @jest-environment jsdom
*/
import { render, fireEvent } from '@testing-library/react'
import ParametersBar from "../../src/components/ParametersBar"
import { describe, it, expect, beforeAll, jest } from "@jest/globals"
import '@testing-library/jest-dom/jest-globals'

beforeAll(() => {
Object.defineProperty(window, "matchMedia", {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // Deprecated
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
}))
});
});

describe('the parameter pane', () => {
it('should change arrow direction on back button', async() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean by Hide/Show btn?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes i did. will change

const { getByTestId } = render(<ParametersBar params={null} setParams={null}/>)
const backbutton = getByTestId('back')

const left = getByTestId('leftarrow')
expect(left).toBeInTheDocument()
fireEvent.click(backbutton)
const right = getByTestId('rightarrow')
expect(right).toBeInTheDocument()
})

it('should hide and show pane', async() => {
const { getByTestId } = render(<ParametersBar params={null} setParams={null}/>)
const backbutton = getByTestId('back')
const pane = getByTestId('pane')

expect(pane).toBeInTheDocument()
fireEvent.click(backbutton)
expect(pane).not.toBeInTheDocument()
})

// TODO: there aren't any easy/expert mode specific parameters yet. once they are added
// we should make sure to test them here
})


describe('the back button', () => {
it('back button should retain equal y position between states', async() => {
const { getByTestId } = render(<ParametersBar params={null} setParams={null}/>)
const backbutton = getByTestId('back')

expect(backbutton).toBeInTheDocument()
const openypos = backbutton.getBoundingClientRect().top
fireEvent.click(backbutton)
const closedypos = backbutton.getBoundingClientRect().top
expect(openypos).toBe(closedypos)
})
})
34 changes: 34 additions & 0 deletions tests/services/model/ONNXService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import ONNXService from "../../../src/services/model/ONNXService"
import { test, expect, beforeAll, beforeEach } from "@jest/globals"

let service: ONNXService
let initCond: number[][][][]

beforeAll(async () => {
// TODO
}, 100000)

beforeEach(() => {
service.loadDataArray(initCond)
})

test("Tfjs should be instantiable", async() => {
expect(service).not.toBeUndefined()
expect(service).not.toBeNull();
})

test("model should run callback", async() => {
service.bindOutput((data) => {
expect(data)
})
service.startSimulation()
service.pauseSimulation()
})

test("model should conserve mass", async() => {
const m1 = service.getMass()
service.startSimulation()
service.pauseSimulation()
const m2 = service.getMass()
expect(m1).toBeCloseTo(m2, 1)
})
36 changes: 36 additions & 0 deletions tests/services/model/TfjsService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import TfjsService from "../../../src/services/model/TfjsService"
import { test, expect, beforeAll, beforeEach } from "@jest/globals"

let service: TfjsService
let initCond: number[][][][]

beforeAll(async () => {
service = await TfjsService.createService("https://github.com/techlauncher-mlai-edge-physics/CFlowSim/raw/main/public/model/bno_small_new_web/model.json")
const resp = await fetch("https://github.com/techlauncher-mlai-edge-physics/CFlowSim/raw/main/public/initData/pvf_incomp_44_nonneg/pvf_incomp_44_nonneg_0.json")
initCond = await resp.json()
}, 100000)

beforeEach(() => {
service.loadDataArray(initCond)
})

test("Tfjs should be instantiable", async() => {
expect(service).not.toBeUndefined()
expect(service).not.toBeNull();
})

test("model should run callback", async() => {
service.bindOutput((data) => {
expect(data)
})
service.startSimulation()
service.pauseSimulation()
})

test("model should conserve mass", async() => {
const m1 = service.getMass()
service.startSimulation()
service.pauseSimulation()
const m2 = service.getMass()
expect(m1).toBeCloseTo(m2, 1)
})