From 4e207c277cbe62604d3b245b0372e8ae68096809 Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Tue, 16 Jan 2024 09:58:56 +0200 Subject: [PATCH 01/15] Initial all experiments --- portal/mock-server/src/all-experiments.json | 19 +++++++++ portal/mock-server/src/router.ts | 8 ++++ .../all-experiments/Experiments.module.scss | 26 ++++++++++++ .../all-experiments/Experiments.test.tsx | 7 ++++ .../all-experiments/Experiments.tsx | 34 +++++++++++++++ .../components/all-experiments/hooks/index.ts | 1 + .../hooks/useExperimentsData.test.ts | 35 ++++++++++++++++ .../hooks/useExperimentsData.ts | 42 +++++++++++++++++++ .../navigation-tab/NavigationTab.module.scss | 2 +- .../shared/constants/navigation-tabs.const.ts | 9 ++-- portal/src/routes/index.jsx | 36 ++++++++-------- 11 files changed, 195 insertions(+), 24 deletions(-) create mode 100644 portal/mock-server/src/all-experiments.json create mode 100644 portal/src/app/components/all-experiments/Experiments.module.scss create mode 100644 portal/src/app/components/all-experiments/Experiments.test.tsx create mode 100644 portal/src/app/components/all-experiments/Experiments.tsx create mode 100644 portal/src/app/components/all-experiments/hooks/index.ts create mode 100644 portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts create mode 100644 portal/src/app/components/all-experiments/hooks/useExperimentsData.ts diff --git a/portal/mock-server/src/all-experiments.json b/portal/mock-server/src/all-experiments.json new file mode 100644 index 00000000..d01b785b --- /dev/null +++ b/portal/mock-server/src/all-experiments.json @@ -0,0 +1,19 @@ +{ + "experiments": [ + { + "id": 1, + "name": "Experiment 1", + "description": "This is the first experiment" + }, + { + "id": 2, + "name": "Experiment 2", + "description": "This is the second experiment" + }, + { + "id": 3, + "name": "Experiment 3", + "description": "This is the third experiment" + } + ] +} \ No newline at end of file diff --git a/portal/mock-server/src/router.ts b/portal/mock-server/src/router.ts index b6c112c6..15c0a10a 100644 --- a/portal/mock-server/src/router.ts +++ b/portal/mock-server/src/router.ts @@ -37,6 +37,14 @@ router.get('/qujata-api/test_suites/:testSuiteId', async (req: Request, res: Res }, 1500); }); +router.get('/qujata-api/experiments', async (req: Request, res: Response) => { + console.log(`-${req.method} ${req.url}`); + const data = (await import('./all-experiments.json')).default; + setTimeout(() => { + res.json(data); + }, 1500); +}); + router.put('/qujata-api/test_suites/:testSuiteId', async (req: Request, res: Response) => { console.log(`-${req.method} ${req.url}`); setTimeout(() => { diff --git a/portal/src/app/components/all-experiments/Experiments.module.scss b/portal/src/app/components/all-experiments/Experiments.module.scss new file mode 100644 index 00000000..3f713aa2 --- /dev/null +++ b/portal/src/app/components/all-experiments/Experiments.module.scss @@ -0,0 +1,26 @@ +@import "src/styles/variables-keys"; + +.app_wrapper { + padding-block-start: 20px; + padding-inline-start: 80px; + padding-block-end: 40px; +} + +.spinner_wrapper { + position: sticky; + inset-block-start: 50%; + inset-inline-start: 50%; + text-align: center; +} + +.spinner_overlay { + inline-size: 100%; + block-size: 100%; + position: absolute; + background-color: #fff; + opacity: 0.6; + inset-block-start: 0; + inset-inline-start: 0; + z-index: 4; +} + diff --git a/portal/src/app/components/all-experiments/Experiments.test.tsx b/portal/src/app/components/all-experiments/Experiments.test.tsx new file mode 100644 index 00000000..a63b4138 --- /dev/null +++ b/portal/src/app/components/all-experiments/Experiments.test.tsx @@ -0,0 +1,7 @@ +import { Experiments } from './Experiments'; + +describe('Experiments', () => { + test('should render Experiments', async () => { + expect(true).toBe(true); + }); +}); diff --git a/portal/src/app/components/all-experiments/Experiments.tsx b/portal/src/app/components/all-experiments/Experiments.tsx new file mode 100644 index 00000000..f1003ad5 --- /dev/null +++ b/portal/src/app/components/all-experiments/Experiments.tsx @@ -0,0 +1,34 @@ +import { useEffect } from 'react'; +import styles from './Experiments.module.scss'; +import { useNavigate } from "react-router-dom"; +import { IUseExperimentsData, useExperimentsData } from './hooks'; +import { FetchDataStatus } from '../../shared/hooks/useFetch'; +import { Spinner, SpinnerSize } from '../../shared/components/att-spinner'; + +export const Experiments: React.FC = () => { + const { experiments, status }: IUseExperimentsData = useExperimentsData(); + // const navigate = useNavigate(); + + // useEffect(() => { + // if (status === FetchDataStatus.Success && experiments) { + // navigate(`qujata-api/experiments`); + // } + // }, [navigate, status, experiments]); + + return ( +
+ {status === FetchDataStatus.Fetching ? renderSpinner() : {'Hello World!!!!'}} +
+ ); + +} + +function renderSpinner() { + return ( +
+
+ +
+
+ ); +} diff --git a/portal/src/app/components/all-experiments/hooks/index.ts b/portal/src/app/components/all-experiments/hooks/index.ts new file mode 100644 index 00000000..e1607221 --- /dev/null +++ b/portal/src/app/components/all-experiments/hooks/index.ts @@ -0,0 +1 @@ +export * from './useExperimentsData'; diff --git a/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts b/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts new file mode 100644 index 00000000..439b6f65 --- /dev/null +++ b/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts @@ -0,0 +1,35 @@ +import { renderHook } from '@testing-library/react'; +import { useFetch } from '../../../shared/hooks/useFetch'; +import { Experiment, useExperimentsData } from './useExperimentsData'; + +jest.mock('../../../shared/hooks/useFetch', () => ({ + useFetch: jest.fn(), +})); + +describe('useExperimentsData', () => { + test('Should be in Success mode', () => { + const allExperimentsMockData = { + experiments: [ + { + id: '1', + name: 'Experiment 1', + description: 'Experiment 1 description', + }, + { + id: '2', + name: 'Experiment 2', + description: 'Experiment 2 description', + }, + ] + }; + + (useFetch as jest.Mock).mockReturnValue({ + get: jest.fn(), + data: allExperimentsMockData, + cancelRequest: jest.fn(), + }); + + const { result } = renderHook(() => useExperimentsData()); + expect(result.current.experiments.length).toEqual(allExperimentsMockData.experiments.length); + }); +}); \ No newline at end of file diff --git a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts new file mode 100644 index 00000000..0f78fcc2 --- /dev/null +++ b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts @@ -0,0 +1,42 @@ +import { FetchDataStatus, IHttp, useFetch } from "../../../shared/hooks/useFetch"; +import { useEffect, useState } from "react"; +import { APIS } from "../../../apis"; +import { useFetchSpinner } from "../../../shared/hooks/useFetchSpinner"; +import { useErrorMessage } from "../../../hooks/useErrorMessage"; + +export interface IUseExperimentsData { + experiments: Experiment[]; + status: FetchDataStatus; +} + +export interface Experiment { + id: string; + name: string; + description: string; +} + +export function useExperimentsData(): IUseExperimentsData { + const [allExperiments, setAllExperiments] = useState([]); + const { get, data, status, cancelRequest, error }: IHttp = useFetch({ url: APIS.allExperiments }); + + useFetchSpinner(status); + useErrorMessage(error); + useEffect(() => { + get(); + return cancelRequest; + }, [get, cancelRequest]); + + + useEffect(() => { + if (status === FetchDataStatus.Success && data) { + const allExperiments: Experiment[] = data.experiments.map((experiment: Experiment) => ({ + id: experiment.id, + name: experiment.name, + description: experiment.description + })); + setAllExperiments(allExperiments); + } + }, [data, status]); + + return { experiments: allExperiments, status }; +} diff --git a/portal/src/app/shared/components/navigation-tab/NavigationTab.module.scss b/portal/src/app/shared/components/navigation-tab/NavigationTab.module.scss index 9e633f09..02d548dc 100644 --- a/portal/src/app/shared/components/navigation-tab/NavigationTab.module.scss +++ b/portal/src/app/shared/components/navigation-tab/NavigationTab.module.scss @@ -7,7 +7,7 @@ .tab { color: var($primaryWhite); - margin-inline-end: 20px; + margin-inline-end: 30px; text-decoration: none; } diff --git a/portal/src/app/shared/constants/navigation-tabs.const.ts b/portal/src/app/shared/constants/navigation-tabs.const.ts index 12ff222a..e697af6d 100644 --- a/portal/src/app/shared/constants/navigation-tabs.const.ts +++ b/portal/src/app/shared/constants/navigation-tabs.const.ts @@ -5,9 +5,8 @@ export const tabs = [ link: '/qujata', title: SHARED_EN.NAVIGATION_TABS.HOME, }, - // { - // link: '/all-experiments', - // title: SHARED_EN.NAVIGATION_TABS.ALL_EXPERIMENTS, - // disabled: true, - // } + { + link: '/experiments', + title: SHARED_EN.NAVIGATION_TABS.ALL_EXPERIMENTS, + } ]; diff --git a/portal/src/routes/index.jsx b/portal/src/routes/index.jsx index a84f73d0..6f466369 100644 --- a/portal/src/routes/index.jsx +++ b/portal/src/routes/index.jsx @@ -2,26 +2,26 @@ import { createBrowserRouter } from 'react-router-dom'; import Root from './Root'; import { Home } from '../app/components/home/Home'; import { Experiment } from '../app/components/home/components/experiment/Experiment'; +import { Experiments } from '../app/components/all-experiments/Experiments'; -const isAllExperimentTabEnabled = false; export const router = createBrowserRouter([ { - path: '/qujata', - element: , - children: [ - { - path: '', - index: true, - element: , - }, - { - path: 'experiment/:testSuiteId', - element: , - }, - ...(isAllExperimentTabEnabled ? [{ - path: 'All-Experiments', - element:
All Experiments
, - }] : []), - ], + path: '/qujata', + element: , + children: [ + { + path: '', + index: true, + element: , + }, + { + path: 'experiment/:testSuiteId', + element: , + }, + { + path: 'experiments', + element: , + }, + ], }, ]); From 555a972049ae847d65ced0950899a0e1bc57fbd1 Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Tue, 16 Jan 2024 17:33:54 +0200 Subject: [PATCH 02/15] Experiments table with dummy duplicate --- portal/mock-server/src/all-experiments.json | 45 +++++++-- .../all-experiments/Experiments.module.scss | 22 ++++- .../all-experiments/Experiments.tsx | 91 ++++++++++++++++--- .../hooks/useExperimentsData.ts | 14 ++- .../all-experiments/translate/en.ts | 28 ++++++ .../ExperimentTable.module.scss | 8 +- .../experiment-table/ExperimentTable.tsx | 4 +- .../shared/components/table/Table.module.scss | 5 - .../src/app/shared/components/table/Table.tsx | 31 ++++--- .../shared/constants/navigation-tabs.const.ts | 2 +- portal/src/assets/images/duplicate.svg | 11 +++ 11 files changed, 211 insertions(+), 50 deletions(-) create mode 100644 portal/src/app/components/all-experiments/translate/en.ts create mode 100644 portal/src/assets/images/duplicate.svg diff --git a/portal/mock-server/src/all-experiments.json b/portal/mock-server/src/all-experiments.json index d01b785b..202f22a9 100644 --- a/portal/mock-server/src/all-experiments.json +++ b/portal/mock-server/src/all-experiments.json @@ -2,18 +2,51 @@ "experiments": [ { "id": 1, - "name": "Experiment 1", - "description": "This is the first experiment" + "experimentName": "Experiment 1", + "algorithms": ["kyber512", "hqc128"], + "iterations": [2000, 2500, 10000], + "messageSizes": [512, 1024, 2048], + "date": "2022-11-11" }, { "id": 2, - "name": "Experiment 2", - "description": "This is the second experiment" + "experimentName": "Experiment 2", + "algorithms": ["hqc128", "prime256v1"], + "iterations": [1500, 1700, 20000], + "messageSizes": [1024, 2048], + "date": "2023-02-09" }, { "id": 3, - "name": "Experiment 3", - "description": "This is the third experiment" + "experimentName": "Experiment 3", + "algorithms": ["secp384r1"], + "iterations": [100000], + "messageSizes": [512], + "date": "2024-01-01" + }, + { + "id": 4, + "experimentName": "Experiment 4", + "algorithms": ["kyber768", "kyber1024", "secp384r1"], + "iterations": [2000, 1500], + "messageSizes": [1024], + "date": "2020-10-07" + }, + { + "id": 5, + "experimentName": "Experiment 5", + "algorithms": ["hqc256"], + "iterations": [100, 500], + "messageSizes": [2048], + "date": "2021-11-12" + }, + { + "id": 6, + "experimentName": "Experiment 6", + "algorithms": ["hqc256"], + "iterations": [], + "messageSizes": [2048], + "date": "2021-11-12" } ] } \ No newline at end of file diff --git a/portal/src/app/components/all-experiments/Experiments.module.scss b/portal/src/app/components/all-experiments/Experiments.module.scss index 3f713aa2..3d4872e0 100644 --- a/portal/src/app/components/all-experiments/Experiments.module.scss +++ b/portal/src/app/components/all-experiments/Experiments.module.scss @@ -1,9 +1,25 @@ @import "src/styles/variables-keys"; -.app_wrapper { - padding-block-start: 20px; - padding-inline-start: 80px; +.experiments_wrapper { + padding-block-start: 40px; padding-block-end: 40px; + padding-inline-start: 80px; + padding-inline-end: 80px; +} + +.experiments_title { + font-size: 20px; + font-family: var($fontMedium); + margin-inline-start: 150px; +} + +.experiments_table { + margin-block-start: 60px; + margin-block-end: 60px; + + // .duplicate_icon { + // margin-inline-end: 20px; + // } } .spinner_wrapper { diff --git a/portal/src/app/components/all-experiments/Experiments.tsx b/portal/src/app/components/all-experiments/Experiments.tsx index f1003ad5..a8c414ee 100644 --- a/portal/src/app/components/all-experiments/Experiments.tsx +++ b/portal/src/app/components/all-experiments/Experiments.tsx @@ -1,26 +1,91 @@ -import { useEffect } from 'react'; +import { ReactNode, useCallback, useMemo } from 'react'; import styles from './Experiments.module.scss'; -import { useNavigate } from "react-router-dom"; -import { IUseExperimentsData, useExperimentsData } from './hooks'; +import { Experiment, IUseExperimentsData, useExperimentsData } from './hooks'; import { FetchDataStatus } from '../../shared/hooks/useFetch'; import { Spinner, SpinnerSize } from '../../shared/components/att-spinner'; +import { ALL_EXPERIMENTS_TABLE_EN } from './translate/en'; +import { CellContext } from '@tanstack/react-table'; +import { Table } from '../../shared/components/table'; +import DuplicateSvg from '../../../assets/images/duplicate.svg'; +import { Button, ButtonActionType, ButtonSize, ButtonStyleType } from '../../shared/components/att-button'; +import { useNavigate } from 'react-router-dom'; +import { isEmpty } from 'lodash'; + +const DuplicateAriaLabel: string = ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.LINKS.DUPLICATE; export const Experiments: React.FC = () => { const { experiments, status }: IUseExperimentsData = useExperimentsData(); - // const navigate = useNavigate(); + const experimentsData = useMemo(() => (experiments ?? []), [experiments]); + const navigate = useNavigate(); + + const handleButtonClick = useCallback((row: Experiment) => { + console.log('Button clicked on row:', row); + // need to naviage with the entire row data, so we can duplicate it properly + navigate('/qujata'); + }, [navigate]); - // useEffect(() => { - // if (status === FetchDataStatus.Success && experiments) { - // navigate(`qujata-api/experiments`); - // } - // }, [navigate, status, experiments]); + const headers = useMemo(() => { + const columnDefs = [ + { + id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.EXPERIMENT_NAME.ID, + name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.EXPERIMENT_NAME.NAME, + accessor: (row: Experiment) => row.experimentName + }, + { + id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.ALGORITHMS.ID, + name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.ALGORITHMS.NAME, + accessor: (row: Experiment) => row.algorithms.join(', ') + }, + { + id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.ITERATIONS.ID, + name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.ITERATIONS.NAME, + accessor: (row: Experiment) => row.iterations.join(', ') + }, + { + id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.MESSAGE_SIZE.ID, + name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.MESSAGE_SIZE.NAME, + accessor: (row: Experiment) => row.messageSizes.join(', ') + }, + { + id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.DATE.ID, + name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.DATE.NAME, + accessor: (row: Experiment) => row.date + }, + { + id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.LINKS.DUPLICATE, + accessor: () => null, + cell: (cellInfo: CellContext) => ( + + ) + }, + ]; + + return columnDefs.map(({ id, name, accessor, cell }) => ({ + id, + header: () => {name}, + accessor, + cell: cell || ((cellInfo: CellContext) => {cellInfo.getValue() as ReactNode}) + })); + }, [handleButtonClick]); return ( -
- {status === FetchDataStatus.Fetching ? renderSpinner() : {'Hello World!!!!'}} +
+ {status === FetchDataStatus.Fetching ? renderSpinner() : ( +
+ + + + )} - ); - + ); } function renderSpinner() { diff --git a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts index 0f78fcc2..d82a0f83 100644 --- a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts +++ b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts @@ -11,8 +11,11 @@ export interface IUseExperimentsData { export interface Experiment { id: string; - name: string; - description: string; + experimentName: string; + algorithms: string[]; + iterations: number[]; + messageSizes: number[]; + date: string; } export function useExperimentsData(): IUseExperimentsData { @@ -31,8 +34,11 @@ export function useExperimentsData(): IUseExperimentsData { if (status === FetchDataStatus.Success && data) { const allExperiments: Experiment[] = data.experiments.map((experiment: Experiment) => ({ id: experiment.id, - name: experiment.name, - description: experiment.description + experimentName: experiment.experimentName, + algorithms: experiment.algorithms, + iterations: experiment.iterations, + messageSizes: experiment.messageSizes, + date: experiment.date, })); setAllExperiments(allExperiments); } diff --git a/portal/src/app/components/all-experiments/translate/en.ts b/portal/src/app/components/all-experiments/translate/en.ts new file mode 100644 index 00000000..cb118024 --- /dev/null +++ b/portal/src/app/components/all-experiments/translate/en.ts @@ -0,0 +1,28 @@ +export const ALL_EXPERIMENTS_TABLE_EN = { + TITLE: 'All Experiments', + TABLE_COLUMNS: { + EXPERIMENT_NAME: { + NAME: 'Experiment Name', + ID: 'experimentName' + }, + ALGORITHMS: { + NAME: 'Algorithms', + ID: 'algorithms' + }, + ITERATIONS: { + NAME: 'Iterations', + ID: 'iterations' + }, + MESSAGE_SIZE: { + NAME: 'Message Size (KB)', + ID: 'messageSizes' + }, + DATE: { + NAME: 'Date', + ID: 'date' + }, + LINKS: { + DUPLICATE: 'duplicate', + } + } +} diff --git a/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.module.scss b/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.module.scss index 19614b4a..ec5a99f8 100644 --- a/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.module.scss +++ b/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.module.scss @@ -1,5 +1,4 @@ @import "src/styles/variables-keys"; -@import "src/styles/z-index"; .experiment_table_wrapper { font-size: 14px; @@ -8,3 +7,10 @@ display: flex; flex-wrap: wrap; } + +.experiment_table { + th:first-child, + td:first-child { + inline-size: 80px; + } +} \ No newline at end of file diff --git a/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.tsx b/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.tsx index d468223b..c873d676 100644 --- a/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.tsx +++ b/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.tsx @@ -14,7 +14,7 @@ export interface ExperimentTableProps { export const ExperimentTable: React.FC = (props: ExperimentTableProps) => { const data = useMemo(() => (props.data ? props.data.testRuns : []), [props.data]); - const headers: TableColumn[] = useMemo(() => [ + const headers: TableColumn[] = useMemo(() => [ { id: 'hashtag', header: () => {EXPERIMENT_TABLE_EN.TABLE_TITLES.HASHTAG}, @@ -44,7 +44,7 @@ export const ExperimentTable: React.FC = (props: Experimen return (
-
+
); }; diff --git a/portal/src/app/shared/components/table/Table.module.scss b/portal/src/app/shared/components/table/Table.module.scss index ca419a35..8a7154d4 100644 --- a/portal/src/app/shared/components/table/Table.module.scss +++ b/portal/src/app/shared/components/table/Table.module.scss @@ -8,11 +8,6 @@ table { table-layout: fixed; } -th:first-child, -td:first-child { - inline-size: 80px; -} - .table_titles { padding: 16px; background-color: var($attPurple); diff --git a/portal/src/app/shared/components/table/Table.tsx b/portal/src/app/shared/components/table/Table.tsx index 70766ac8..29bf6d29 100644 --- a/portal/src/app/shared/components/table/Table.tsx +++ b/portal/src/app/shared/components/table/Table.tsx @@ -1,4 +1,5 @@ import styles from './Table.module.scss'; +import cn from 'classnames'; import { useMemo, useState } from 'react'; import { Cell, @@ -14,28 +15,28 @@ import { getCoreRowModel, useReactTable, } from '@tanstack/react-table'; -import { ITestRunResultData } from '../../models/test-run-result.interface'; import SortascendingSvg from '../../../../assets/images/sort-ascending.svg'; import SortDescendingSvg from '../../../../assets/images/sort-descending.svg'; const SortAscendingLabel: string = 'ascending'; const SortDescendingLabel: string = 'descending'; -export interface TableColumn { +export interface TableColumn { id: string; - header: (context: HeaderContext) => React.ReactNode; - accessor: (row: ITestRunResultData) => any; - cell?: (cellInfo: CellContext) => JSX.Element; + header: (context: HeaderContext) => React.ReactNode; + accessor: (row: T) => any; + cell?: (cellInfo: CellContext, row?: T) => JSX.Element; } -export interface TableProps { - headers: TableColumn[]; - data: ITestRunResultData[]; +export interface TableProps { + className?: string; + headers: TableColumn[]; + data: T[]; } -export const Table: React.FC = ({ headers, data }) => { +export const Table = ({ headers, data, className }: TableProps) => { const [sorting, setSorting] = useState([]) - const columns: ColumnDef[] = useMemo(() => { + const columns: ColumnDef[] = useMemo(() => { return headers.map(header => ({ id: header.id, header: header.header, @@ -56,11 +57,11 @@ export const Table: React.FC = ({ headers, data }) => { }); return ( -
+
- {table.getHeaderGroups().map((headerGroup: HeaderGroup) => ( + {table.getHeaderGroups().map((headerGroup: HeaderGroup) => ( - {headerGroup.headers.map((header: Header) => ( + {headerGroup.headers.map((header: Header) => ( - {table.getRowModel().rows.map((row: Row) => ( + {table.getRowModel().rows.map((row: Row) => ( - {row.getVisibleCells().map((cell: Cell) => ( + {row.getVisibleCells().map((cell: Cell) => ( diff --git a/portal/src/app/shared/constants/navigation-tabs.const.ts b/portal/src/app/shared/constants/navigation-tabs.const.ts index e697af6d..81e5b310 100644 --- a/portal/src/app/shared/constants/navigation-tabs.const.ts +++ b/portal/src/app/shared/constants/navigation-tabs.const.ts @@ -6,7 +6,7 @@ export const tabs = [ title: SHARED_EN.NAVIGATION_TABS.HOME, }, { - link: '/experiments', + link: '/qujata/experiments', title: SHARED_EN.NAVIGATION_TABS.ALL_EXPERIMENTS, } ]; diff --git a/portal/src/assets/images/duplicate.svg b/portal/src/assets/images/duplicate.svg new file mode 100644 index 00000000..214d3d93 --- /dev/null +++ b/portal/src/assets/images/duplicate.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + From d1a1a92d9e07147aa75ca22037dca65c122b139d Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Wed, 17 Jan 2024 16:02:47 +0200 Subject: [PATCH 03/15] using new mock data for all experiment and adjust new api --- portal/mock-server/src/all-experiments.json | 156 +++++++++++++----- portal/mock-server/src/router.ts | 2 +- .../all-experiments/Experiments.tsx | 33 ++-- .../hooks/useExperimentsData.test.ts | 60 +++++-- .../hooks/useExperimentsData.ts | 50 +++--- .../all-experiments/translate/en.ts | 4 - .../parse-experiments-data.utils.test.ts | 33 ++++ .../utils/parse-experiments-data.utils.ts | 25 +++ portal/src/app/components/home/Home.test.tsx | 2 + portal/src/app/components/home/Home.tsx | 16 +- .../protocol-query/ProtocolQuery.tsx | 10 +- .../shared/constants/navigation-tabs.const.ts | 2 +- portal/src/routes/index.jsx | 2 +- 13 files changed, 281 insertions(+), 114 deletions(-) create mode 100644 portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts create mode 100644 portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts diff --git a/portal/mock-server/src/all-experiments.json b/portal/mock-server/src/all-experiments.json index 202f22a9..dde5dd71 100644 --- a/portal/mock-server/src/all-experiments.json +++ b/portal/mock-server/src/all-experiments.json @@ -1,52 +1,122 @@ -{ - "experiments": [ +{ + "test_suites": [ { - "id": 1, - "experimentName": "Experiment 1", - "algorithms": ["kyber512", "hqc128"], - "iterations": [2000, 2500, 10000], - "messageSizes": [512, 1024, 2048], - "date": "2022-11-11" + "id": 15, + "name": "Experiment 1", + "end_time": 1705240065192, + "testRuns": [ + { + "id": 354, + "algorithm": "prime256v1", + "iterations": 100 + }, + { + "id": 355, + "algorithm": "prime256v1", + "iterations": 500 + }, + { + "id": 356, + "algorithm": "prime256v1", + "iterations": 1000 + }, + { + "id": 357, + "algorithm": "p256_kyber512", + "iterations": 100 + }, + { + "id": 358, + "algorithm": "p256_kyber512", + "iterations": 500 + }, + { + "id": 359, + "algorithm": "p256_kyber512", + "iterations": 1000 + }, + { + "id": 360, + "algorithm": "bikel3", + "iterations": 100 + }, + { + "id": 361, + "algorithm": "bikel3", + "iterations": 500 + }, + { + "id": 362, + "algorithm": "bikel3", + "iterations": 1000 + } + ] }, { - "id": 2, - "experimentName": "Experiment 2", - "algorithms": ["hqc128", "prime256v1"], - "iterations": [1500, 1700, 20000], - "messageSizes": [1024, 2048], - "date": "2023-02-09" + "id": 16, + "name": "Experiment 2", + "end_time": 1705389926549, + "testRuns": [ + { + "id": 363, + "algorithm": "kyber512", + "iterations": 1000 + }, + { + "id": 364, + "algorithm": "bikel3", + "iterations": 1000 + }, + { + "id": 365, + "algorithm": "prime256v1", + "iterations": 1000 + } + ] }, { - "id": 3, - "experimentName": "Experiment 3", - "algorithms": ["secp384r1"], - "iterations": [100000], - "messageSizes": [512], - "date": "2024-01-01" + "id": 17, + "name": "Experiment 3", + "end_time": 1705389926549, + "testRuns": [ + { + "id": 366, + "algorithm": "prime256v1", + "iterations": 500 + }, + { + "id": 367, + "algorithm": "bikel3", + "iterations": 1000 + }, + { + "id": 368, + "algorithm": "bikel3", + "iterations": 1000 + }, + { + "id": 369, + "algorithm": "prime256v1", + "iterations": 5000 + } + ] }, { - "id": 4, - "experimentName": "Experiment 4", - "algorithms": ["kyber768", "kyber1024", "secp384r1"], - "iterations": [2000, 1500], - "messageSizes": [1024], - "date": "2020-10-07" - }, - { - "id": 5, - "experimentName": "Experiment 5", - "algorithms": ["hqc256"], - "iterations": [100, 500], - "messageSizes": [2048], - "date": "2021-11-12" - }, - { - "id": 6, - "experimentName": "Experiment 6", - "algorithms": ["hqc256"], - "iterations": [], - "messageSizes": [2048], - "date": "2021-11-12" + "id": 18, + "name": "Experiment 4", + "end_time": 1705389926549, + "testRuns": [ + { + "id": 370, + "algorithm": "kyber512", + "iterations": 500 + }, + { + "id": 371, + "algorithm": "kyber512", + "iterations": 1000 + } + ] } ] -} \ No newline at end of file +} diff --git a/portal/mock-server/src/router.ts b/portal/mock-server/src/router.ts index 15c0a10a..8c3b27e9 100644 --- a/portal/mock-server/src/router.ts +++ b/portal/mock-server/src/router.ts @@ -37,7 +37,7 @@ router.get('/qujata-api/test_suites/:testSuiteId', async (req: Request, res: Res }, 1500); }); -router.get('/qujata-api/experiments', async (req: Request, res: Response) => { +router.get('/qujata-api/test_suites', async (req: Request, res: Response) => { console.log(`-${req.method} ${req.url}`); const data = (await import('./all-experiments.json')).default; setTimeout(() => { diff --git a/portal/src/app/components/all-experiments/Experiments.tsx b/portal/src/app/components/all-experiments/Experiments.tsx index a8c414ee..b46c05af 100644 --- a/portal/src/app/components/all-experiments/Experiments.tsx +++ b/portal/src/app/components/all-experiments/Experiments.tsx @@ -1,6 +1,6 @@ import { ReactNode, useCallback, useMemo } from 'react'; import styles from './Experiments.module.scss'; -import { Experiment, IUseExperimentsData, useExperimentsData } from './hooks'; +import { Experiment, ExperimentData, IUseExperimentsData, useExperimentsData } from './hooks'; import { FetchDataStatus } from '../../shared/hooks/useFetch'; import { Spinner, SpinnerSize } from '../../shared/components/att-spinner'; import { ALL_EXPERIMENTS_TABLE_EN } from './translate/en'; @@ -9,19 +9,17 @@ import { Table } from '../../shared/components/table'; import DuplicateSvg from '../../../assets/images/duplicate.svg'; import { Button, ButtonActionType, ButtonSize, ButtonStyleType } from '../../shared/components/att-button'; import { useNavigate } from 'react-router-dom'; -import { isEmpty } from 'lodash'; const DuplicateAriaLabel: string = ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.LINKS.DUPLICATE; export const Experiments: React.FC = () => { - const { experiments, status }: IUseExperimentsData = useExperimentsData(); - const experimentsData = useMemo(() => (experiments ?? []), [experiments]); + const { test_suites, status }: IUseExperimentsData = useExperimentsData(); + const experimentsData = useMemo(() => (test_suites ?? []), [test_suites]); const navigate = useNavigate(); - const handleButtonClick = useCallback((row: Experiment) => { - console.log('Button clicked on row:', row); - // need to naviage with the entire row data, so we can duplicate it properly - navigate('/qujata'); + const handleDuplicateClick = useCallback((row: Experiment) => { + // Navigate to the Home Page + navigate('/qujata', { state: { row } }); }, [navigate]); const headers = useMemo(() => { @@ -29,27 +27,22 @@ export const Experiments: React.FC = () => { { id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.EXPERIMENT_NAME.ID, name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.EXPERIMENT_NAME.NAME, - accessor: (row: Experiment) => row.experimentName + accessor: (row: ExperimentData) => row.name }, { id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.ALGORITHMS.ID, name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.ALGORITHMS.NAME, - accessor: (row: Experiment) => row.algorithms.join(', ') + accessor: (row: ExperimentData) => row.algorithms?.join(', ') }, { id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.ITERATIONS.ID, name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.ITERATIONS.NAME, - accessor: (row: Experiment) => row.iterations.join(', ') - }, - { - id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.MESSAGE_SIZE.ID, - name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.MESSAGE_SIZE.NAME, - accessor: (row: Experiment) => row.messageSizes.join(', ') + accessor: (row: ExperimentData) => row.iterations?.join(', ') }, { id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.DATE.ID, name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.DATE.NAME, - accessor: (row: Experiment) => row.date + accessor: (row: ExperimentData) => row.end_time }, { id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.LINKS.DUPLICATE, @@ -60,7 +53,7 @@ export const Experiments: React.FC = () => { size={ButtonSize.NONE} styleType={ButtonStyleType.WRAPPER} actionType={ButtonActionType.BUTTON} - onButtonClick={() => handleButtonClick(cellInfo.row.original)} + onButtonClick={() => handleDuplicateClick(cellInfo.row.original)} > {DuplicateAriaLabel} @@ -74,13 +67,13 @@ export const Experiments: React.FC = () => { accessor, cell: cell || ((cellInfo: CellContext) => {cellInfo.getValue() as ReactNode}) })); - }, [handleButtonClick]); + }, [handleDuplicateClick]); return (
{status === FetchDataStatus.Fetching ? renderSpinner() : (
- +
= ({ headers, data }) => { ))}
{flexRender(cell.column.columnDef.cell, cell.getContext())}
)} diff --git a/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts b/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts index 439b6f65..a79296f7 100644 --- a/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts +++ b/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts @@ -1,26 +1,62 @@ import { renderHook } from '@testing-library/react'; import { useFetch } from '../../../shared/hooks/useFetch'; -import { Experiment, useExperimentsData } from './useExperimentsData'; +import { useExperimentsData } from './useExperimentsData'; jest.mock('../../../shared/hooks/useFetch', () => ({ useFetch: jest.fn(), })); +jest.mock('../../../shared/hooks/useFetchSpinner'); +jest.mock('../../../hooks/useErrorMessage'); describe('useExperimentsData', () => { test('Should be in Success mode', () => { const allExperimentsMockData = { - experiments: [ + test_suites: [ { - id: '1', - name: 'Experiment 1', - description: 'Experiment 1 description', + id: 17, + name: "Experiment 3", + end_time: 1705389926549, + test_runs: [ + { + id: 366, + algorithm: "prime256v1", + iterations: 500 + }, + { + id: 367, + algorithm: "bikel3", + iterations: 1000 + }, + { + id: 368, + algorithm: "p256_kyber512", + iterations: 10000 + }, + { + id: 369, + algorithm: "prime256v1", + iterations: 5000 + } + ] }, - { - id: '2', - name: 'Experiment 2', - description: 'Experiment 2 description', - }, - ] + { + id: 18, + name: "Experiment 4", + end_time: 1705389926549, + test_runs: [ + { + id: 370, + algorithm: "kyber512", + iterations: 500 + }, + { + id: 371, + algorithm: "kyber512", + iterations: 1000 + } + ] + } + ] }; (useFetch as jest.Mock).mockReturnValue({ @@ -30,6 +66,6 @@ describe('useExperimentsData', () => { }); const { result } = renderHook(() => useExperimentsData()); - expect(result.current.experiments.length).toEqual(allExperimentsMockData.experiments.length); + expect(result.current.test_suites.length).toEqual(allExperimentsMockData.test_suites.length); }); }); \ No newline at end of file diff --git a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts index d82a0f83..b84a3eb9 100644 --- a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts +++ b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts @@ -1,26 +1,27 @@ -import { FetchDataStatus, IHttp, useFetch } from "../../../shared/hooks/useFetch"; -import { useEffect, useState } from "react"; -import { APIS } from "../../../apis"; -import { useFetchSpinner } from "../../../shared/hooks/useFetchSpinner"; -import { useErrorMessage } from "../../../hooks/useErrorMessage"; +import { FetchDataStatus, IHttp, useFetch } from '../../../shared/hooks/useFetch'; +import { useEffect, useState } from 'react'; +import { APIS } from '../../../apis'; +import { useFetchSpinner } from '../../../shared/hooks/useFetchSpinner'; +import { useErrorMessage } from '../../../hooks/useErrorMessage'; +import { ITestRunResult } from '../../../shared/models/test-run-result.interface'; +import { parseExperimentsData } from '../utils/parse-experiments-data.utils'; +export type Experiment = Partial; export interface IUseExperimentsData { - experiments: Experiment[]; + test_suites: Experiment[]; status: FetchDataStatus; } - -export interface Experiment { - id: string; - experimentName: string; - algorithms: string[]; - iterations: number[]; - messageSizes: number[]; - date: string; -} +export interface ExperimentData { + id?: number; + name?: string; + algorithms?: string[]; + iterations?: number[]; + end_time?: string; +}; export function useExperimentsData(): IUseExperimentsData { - const [allExperiments, setAllExperiments] = useState([]); - const { get, data, status, cancelRequest, error }: IHttp = useFetch({ url: APIS.allExperiments }); + const [allExperiments, setAllExperiments] = useState([]); + const { get, data, cancelRequest, status, error }: IHttp = useFetch({ url: APIS.allExperiments }); useFetchSpinner(status); useErrorMessage(error); @@ -31,18 +32,11 @@ export function useExperimentsData(): IUseExperimentsData { useEffect(() => { - if (status === FetchDataStatus.Success && data) { - const allExperiments: Experiment[] = data.experiments.map((experiment: Experiment) => ({ - id: experiment.id, - experimentName: experiment.experimentName, - algorithms: experiment.algorithms, - iterations: experiment.iterations, - messageSizes: experiment.messageSizes, - date: experiment.date, - })); - setAllExperiments(allExperiments); + if (data && data.test_suites) { + const experimentsData: ExperimentData[] = parseExperimentsData(data.test_suites); + setAllExperiments(experimentsData); } }, [data, status]); - return { experiments: allExperiments, status }; + return { test_suites: allExperiments, status }; } diff --git a/portal/src/app/components/all-experiments/translate/en.ts b/portal/src/app/components/all-experiments/translate/en.ts index cb118024..943ea9ce 100644 --- a/portal/src/app/components/all-experiments/translate/en.ts +++ b/portal/src/app/components/all-experiments/translate/en.ts @@ -13,10 +13,6 @@ export const ALL_EXPERIMENTS_TABLE_EN = { NAME: 'Iterations', ID: 'iterations' }, - MESSAGE_SIZE: { - NAME: 'Message Size (KB)', - ID: 'messageSizes' - }, DATE: { NAME: 'Date', ID: 'date' diff --git a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts new file mode 100644 index 00000000..8100e25e --- /dev/null +++ b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts @@ -0,0 +1,33 @@ +import { parseExperimentsData } from './parse-experiments-data.utils'; +import { ITestRunResultData } from '../../../shared/models/test-run-result.interface'; +import { Experiment, ExperimentData } from '../hooks'; + +describe('parseExperimentsData', () => { + it('should parse experiments data correctly', () => { + const mockExperiments: Experiment[] = [ + { + id: 1, + name: 'Experiment 1', + testRuns: [ + { algorithm: 'Algorithm 1', iterations: 1000 } as ITestRunResultData, + { algorithm: 'Algorithm 2', iterations: 5000 } as ITestRunResultData, + ], + end_time: '2022-01-01T00:00:00Z', + }, + ]; + + const expectedOutput: ExperimentData[] = [ + { + id: 1, + name: 'Experiment 1', + algorithms: ['Algorithm 1', 'Algorithm 2'], + iterations: [1000, 5000], + end_time: '2022-01-01T00:00:00Z', + }, + ]; + + const result = parseExperimentsData(mockExperiments); + + expect(result).toEqual(expectedOutput); + }); +}); diff --git a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts new file mode 100644 index 00000000..8af93fd3 --- /dev/null +++ b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts @@ -0,0 +1,25 @@ +import { ITestRunResultData } from '../../../shared/models/test-run-result.interface'; +import { Experiment, ExperimentData } from '../hooks'; + +export function parseExperimentsData(test_suites: Experiment[]) { + const experimentsData: ExperimentData[] = []; + + test_suites.forEach((experiment: Experiment) => { + const algorithms = new Set(); + const iterations = new Set(); + experiment.testRuns?.forEach((testRun: ITestRunResultData) => { + algorithms.add(testRun.algorithm); + iterations.add(testRun.iterations); + }); + + experimentsData.push({ + id: experiment.id ?? 0, + name: experiment.name ?? '', + algorithms: Array.from(algorithms), + iterations: Array.from(iterations), + end_time: experiment.end_time ?? '', + }); + }); + + return experimentsData; +} diff --git a/portal/src/app/components/home/Home.test.tsx b/portal/src/app/components/home/Home.test.tsx index e5b75f25..c76bf9f1 100644 --- a/portal/src/app/components/home/Home.test.tsx +++ b/portal/src/app/components/home/Home.test.tsx @@ -4,6 +4,7 @@ import { SubHeader, SubHeaderProps } from '../sub-header'; import { ProtocolQuery, ProtocolQueryProps } from '../protocol-query'; const mockUseNavigate = jest.fn(); +const mockUseLocation = jest.fn(); jest.mock('../sub-header'); jest.mock('../protocol-query'); @@ -17,6 +18,7 @@ jest.mock('../../hooks/useDashboardData', () => ({ jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: () => mockUseNavigate, + useLocation: () => mockUseLocation, })); describe('Home', () => { diff --git a/portal/src/app/components/home/Home.tsx b/portal/src/app/components/home/Home.tsx index ab4e551b..b844e5f4 100644 --- a/portal/src/app/components/home/Home.tsx +++ b/portal/src/app/components/home/Home.tsx @@ -5,7 +5,8 @@ import { ProtocolQuery } from "../protocol-query"; import { SubHeader } from "../sub-header"; import { useCallback, useEffect, useState } from 'react'; import styles from './Home.module.scss'; -import { useNavigate } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; +import { Experiment } from "../all-experiments/hooks"; export const Home: React.FC = () => { const [isSubHeaderOpen, setIsSubHeaderOpen] = useState(true); @@ -25,6 +26,13 @@ export const Home: React.FC = () => { export const HomeContent: React.FC = () => { const { handleRunQueryClick, status, testSuiteId }: IUseDashboardData = useDashboardData(); const navigate = useNavigate(); + const location = useLocation(); + const duplicateData: Experiment = location.state?.row; + + useEffect(() => { + // after the duplicate data has been created, we need to clear the state + location.state = undefined; + }, [location]); useEffect(() => { if (status === FetchDataStatus.Success && testSuiteId) { @@ -41,7 +49,11 @@ export const HomeContent: React.FC = () => { return (
- +
); }; diff --git a/portal/src/app/components/protocol-query/ProtocolQuery.tsx b/portal/src/app/components/protocol-query/ProtocolQuery.tsx index 0e87ab5a..6b923418 100644 --- a/portal/src/app/components/protocol-query/ProtocolQuery.tsx +++ b/portal/src/app/components/protocol-query/ProtocolQuery.tsx @@ -1,5 +1,5 @@ import { noop } from 'lodash'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { Options } from 'react-select'; import { ITestParams } from '../../shared/models/quantum.interface'; import { Button, ButtonActionType, ButtonSize, ButtonStyleType } from '../../shared/components/att-button'; @@ -10,6 +10,7 @@ import { Spinner, SpinnerSize } from '../../shared/components/att-spinner'; import { useGetAlgorithms, useGetIterations } from './hooks'; import { handleAlgorithmsSelection } from './utils'; import { AlgorithmsSelectorCustomOption, IterationsSelectorCustomOption } from '../../shared/components/selector-custom-option'; +import { Experiment } from '../all-experiments/hooks'; export type SelectOptionType = AttSelectOption | Options | null; type onTextChangedEvent = (e: React.ChangeEvent) => void; @@ -21,10 +22,11 @@ export interface ProtocolQueryProps { canExportFile?: boolean; onRunClick: (data: ITestParams) => void; onDownloadDataClicked?: () => void; + duplicateData?: Experiment; } export const ProtocolQuery: React.FC = (props: ProtocolQueryProps) => { - const { isFetching, canExportFile, onRunClick, onDownloadDataClicked } = props; + const { isFetching, canExportFile, onRunClick, onDownloadDataClicked, duplicateData } = props; const { algorithmOptions, algosBySection } = useGetAlgorithms(); const { iterationsOptions } = useGetIterations(); @@ -37,6 +39,10 @@ export const ProtocolQuery: React.FC = (props: ProtocolQuery const [showInputOption, setShowInputOption] = useState(false); const [inputValue, setInputValue] = useState(''); const [iterationsMenuIsOpen, setIterationsMenuIsOpen] = useState(false); + + useEffect(() => { + console.log('duplicateData ProtocolQuery', duplicateData); + }, [duplicateData]); const onSubmitHandler = (event: React.FormEvent) => { event.preventDefault(); diff --git a/portal/src/app/shared/constants/navigation-tabs.const.ts b/portal/src/app/shared/constants/navigation-tabs.const.ts index 81e5b310..0a3d00e2 100644 --- a/portal/src/app/shared/constants/navigation-tabs.const.ts +++ b/portal/src/app/shared/constants/navigation-tabs.const.ts @@ -6,7 +6,7 @@ export const tabs = [ title: SHARED_EN.NAVIGATION_TABS.HOME, }, { - link: '/qujata/experiments', + link: '/qujata/test_suites', title: SHARED_EN.NAVIGATION_TABS.ALL_EXPERIMENTS, } ]; diff --git a/portal/src/routes/index.jsx b/portal/src/routes/index.jsx index 6f466369..c9dabd47 100644 --- a/portal/src/routes/index.jsx +++ b/portal/src/routes/index.jsx @@ -19,7 +19,7 @@ export const router = createBrowserRouter([ element: , }, { - path: 'experiments', + path: 'test_suites', element: , }, ], From b9395701d711b4ec4809cc79ba55181edf18949b Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Wed, 17 Jan 2024 17:41:07 +0200 Subject: [PATCH 04/15] duplication implementation --- portal/mock-server/src/all-experiments.json | 2 +- portal/src/app/components/home/Home.tsx | 8 +++-- .../protocol-query/ProtocolQuery.test.tsx | 4 +-- .../protocol-query/ProtocolQuery.tsx | 34 +++++++++++++++---- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/portal/mock-server/src/all-experiments.json b/portal/mock-server/src/all-experiments.json index dde5dd71..522f9d47 100644 --- a/portal/mock-server/src/all-experiments.json +++ b/portal/mock-server/src/all-experiments.json @@ -114,7 +114,7 @@ { "id": 371, "algorithm": "kyber512", - "iterations": 1000 + "iterations": 1500 } ] } diff --git a/portal/src/app/components/home/Home.tsx b/portal/src/app/components/home/Home.tsx index b844e5f4..ebc822b7 100644 --- a/portal/src/app/components/home/Home.tsx +++ b/portal/src/app/components/home/Home.tsx @@ -6,7 +6,7 @@ import { SubHeader } from "../sub-header"; import { useCallback, useEffect, useState } from 'react'; import styles from './Home.module.scss'; import { useLocation, useNavigate } from "react-router-dom"; -import { Experiment } from "../all-experiments/hooks"; +import { Experiment, ExperimentData } from "../all-experiments/hooks"; export const Home: React.FC = () => { const [isSubHeaderOpen, setIsSubHeaderOpen] = useState(true); @@ -27,11 +27,12 @@ export const HomeContent: React.FC = () => { const { handleRunQueryClick, status, testSuiteId }: IUseDashboardData = useDashboardData(); const navigate = useNavigate(); const location = useLocation(); - const duplicateData: Experiment = location.state?.row; + const [duplicateData, setDuplicateData] = useState(location.state?.row); + // const duplicateData: Experiment = location.state?.row; useEffect(() => { // after the duplicate data has been created, we need to clear the state - location.state = undefined; + setDuplicateData(undefined); }, [location]); useEffect(() => { @@ -53,6 +54,7 @@ export const HomeContent: React.FC = () => { isFetching={status === FetchDataStatus.Fetching} onRunClick={handleRunClick} duplicateData={duplicateData} + setDuplicateData={setDuplicateData} /> ); diff --git a/portal/src/app/components/protocol-query/ProtocolQuery.test.tsx b/portal/src/app/components/protocol-query/ProtocolQuery.test.tsx index c599c9fc..c444fa76 100644 --- a/portal/src/app/components/protocol-query/ProtocolQuery.test.tsx +++ b/portal/src/app/components/protocol-query/ProtocolQuery.test.tsx @@ -6,12 +6,10 @@ import { PROTOCOL_QUERY_EN } from './translate/en'; describe('ProtocolQuery', () => { let props: ProtocolQueryProps; beforeAll(() => { - // Prepare the props for the ProtocolQuery component props = { isFetching: false, - canExportFile: true, onRunClick: jest.fn(), - onDownloadDataClicked: jest.fn(), + setDuplicateData: jest.fn() }; }); diff --git a/portal/src/app/components/protocol-query/ProtocolQuery.tsx b/portal/src/app/components/protocol-query/ProtocolQuery.tsx index 6b923418..0db92e65 100644 --- a/portal/src/app/components/protocol-query/ProtocolQuery.tsx +++ b/portal/src/app/components/protocol-query/ProtocolQuery.tsx @@ -1,5 +1,5 @@ import { noop } from 'lodash'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Options } from 'react-select'; import { ITestParams } from '../../shared/models/quantum.interface'; import { Button, ButtonActionType, ButtonSize, ButtonStyleType } from '../../shared/components/att-button'; @@ -10,7 +10,7 @@ import { Spinner, SpinnerSize } from '../../shared/components/att-spinner'; import { useGetAlgorithms, useGetIterations } from './hooks'; import { handleAlgorithmsSelection } from './utils'; import { AlgorithmsSelectorCustomOption, IterationsSelectorCustomOption } from '../../shared/components/selector-custom-option'; -import { Experiment } from '../all-experiments/hooks'; +import { ExperimentData } from '../all-experiments/hooks'; export type SelectOptionType = AttSelectOption | Options | null; type onTextChangedEvent = (e: React.ChangeEvent) => void; @@ -19,14 +19,13 @@ export type OnSelectChanged = (event: SelectOptionType) => void; export interface ProtocolQueryProps { isFetching: boolean; - canExportFile?: boolean; onRunClick: (data: ITestParams) => void; - onDownloadDataClicked?: () => void; - duplicateData?: Experiment; + duplicateData?: ExperimentData; + setDuplicateData: (data?: ExperimentData) => void; } export const ProtocolQuery: React.FC = (props: ProtocolQueryProps) => { - const { isFetching, canExportFile, onRunClick, onDownloadDataClicked, duplicateData } = props; + const { isFetching, onRunClick, duplicateData, setDuplicateData } = props; const { algorithmOptions, algosBySection } = useGetAlgorithms(); const { iterationsOptions } = useGetIterations(); @@ -40,9 +39,29 @@ export const ProtocolQuery: React.FC = (props: ProtocolQuery const [inputValue, setInputValue] = useState(''); const [iterationsMenuIsOpen, setIterationsMenuIsOpen] = useState(false); + // TODO: move this useEffect into different file useEffect(() => { console.log('duplicateData ProtocolQuery', duplicateData); - }, [duplicateData]); + if (duplicateData) { + if (duplicateData && duplicateData.name) { + setExperimentName(duplicateData.name); + } + if (duplicateData && duplicateData.algorithms) { + const algorithmOptions = duplicateData.algorithms.map((algorithm: string) => { + return { label: algorithm, value: algorithm } as AttSelectOption; + }); + setAlgorithms(algorithmOptions); + } + + if (duplicateData && duplicateData.iterations) { + const iterationsOptions = duplicateData.iterations.map((iteration: number) => { + return { label: iteration.toString(), value: iteration.toString() } as AttSelectOption; + }); + setIterationsCount(iterationsOptions); + } + setDuplicateData(undefined); + } + }, [duplicateData, setDuplicateData]); const onSubmitHandler = (event: React.FormEvent) => { event.preventDefault(); @@ -92,6 +111,7 @@ export const ProtocolQuery: React.FC = (props: ProtocolQuery Date: Mon, 22 Jan 2024 14:19:14 +0200 Subject: [PATCH 05/15] finalize all experiments --- portal/mock-server/src/all-experiments.json | 242 +++++++++--------- portal/mock-server/src/router.ts | 7 + portal/mock-server/src/test.json | 2 +- portal/package.json | 1 + portal/src/app/apis.ts | 8 +- .../all-experiments/Experiments.module.scss | 67 ++++- .../all-experiments/Experiments.tsx | 117 ++++++++- .../hooks/useExperimentsData.ts | 28 +- .../all-experiments/translate/en.ts | 4 + .../parse-experiments-data.utils.test.ts | 2 +- .../utils/parse-experiments-data.utils.ts | 11 +- portal/src/app/components/home/Home.tsx | 2 +- .../experiment/components/__mocks__/mocks.ts | 8 +- .../components/charts/__mocks__/mocks.ts | 2 +- .../components/charts/hooks/useChartsData.ts | 4 +- .../DeleteExperimentModal.test.tsx | 4 +- .../DeleteExperimentModal.tsx | 11 +- .../delete-experiment-modal/translate/en.ts | 2 +- .../experiment-table/ExperimentTable.tsx | 2 +- .../hooks/useExperimentData.test.ts | 4 +- .../components/hooks/useExperimentData.ts | 6 +- .../components/sub-header/SubHeader.tsx | 10 +- .../protocol-query/ProtocolQuery.tsx | 3 +- .../models/test-run-result.interface.ts | 2 +- portal/src/assets/images/trash-hover.svg | 4 + portal/yarn.lock | 5 + 26 files changed, 361 insertions(+), 197 deletions(-) create mode 100644 portal/src/assets/images/trash-hover.svg diff --git a/portal/mock-server/src/all-experiments.json b/portal/mock-server/src/all-experiments.json index 522f9d47..4b9a9061 100644 --- a/portal/mock-server/src/all-experiments.json +++ b/portal/mock-server/src/all-experiments.json @@ -1,122 +1,120 @@ -{ - "test_suites": [ - { - "id": 15, - "name": "Experiment 1", - "end_time": 1705240065192, - "testRuns": [ - { - "id": 354, - "algorithm": "prime256v1", - "iterations": 100 - }, - { - "id": 355, - "algorithm": "prime256v1", - "iterations": 500 - }, - { - "id": 356, - "algorithm": "prime256v1", - "iterations": 1000 - }, - { - "id": 357, - "algorithm": "p256_kyber512", - "iterations": 100 - }, - { - "id": 358, - "algorithm": "p256_kyber512", - "iterations": 500 - }, - { - "id": 359, - "algorithm": "p256_kyber512", - "iterations": 1000 - }, - { - "id": 360, - "algorithm": "bikel3", - "iterations": 100 - }, - { - "id": 361, - "algorithm": "bikel3", - "iterations": 500 - }, - { - "id": 362, - "algorithm": "bikel3", - "iterations": 1000 - } - ] - }, - { - "id": 16, - "name": "Experiment 2", - "end_time": 1705389926549, - "testRuns": [ - { - "id": 363, - "algorithm": "kyber512", - "iterations": 1000 - }, - { - "id": 364, - "algorithm": "bikel3", - "iterations": 1000 - }, - { - "id": 365, - "algorithm": "prime256v1", - "iterations": 1000 - } - ] - }, - { - "id": 17, - "name": "Experiment 3", - "end_time": 1705389926549, - "testRuns": [ - { - "id": 366, - "algorithm": "prime256v1", - "iterations": 500 - }, - { - "id": 367, - "algorithm": "bikel3", - "iterations": 1000 - }, - { - "id": 368, - "algorithm": "bikel3", - "iterations": 1000 - }, - { - "id": 369, - "algorithm": "prime256v1", - "iterations": 5000 - } - ] - }, - { - "id": 18, - "name": "Experiment 4", - "end_time": 1705389926549, - "testRuns": [ - { - "id": 370, - "algorithm": "kyber512", - "iterations": 500 - }, - { - "id": 371, - "algorithm": "kyber512", - "iterations": 1500 - } - ] - } - ] -} +[ + { + "id": 15, + "name": "Experiment 1", + "end_time": 1705240065192, + "test_runs": [ + { + "id": 354, + "algorithm": "prime256v1", + "iterations": 100 + }, + { + "id": 355, + "algorithm": "prime256v1", + "iterations": 500 + }, + { + "id": 356, + "algorithm": "prime256v1", + "iterations": 1000 + }, + { + "id": 357, + "algorithm": "p256_kyber512", + "iterations": 100 + }, + { + "id": 358, + "algorithm": "p256_kyber512", + "iterations": 500 + }, + { + "id": 359, + "algorithm": "p256_kyber512", + "iterations": 1000 + }, + { + "id": 360, + "algorithm": "bikel3", + "iterations": 100 + }, + { + "id": 361, + "algorithm": "bikel3", + "iterations": 500 + }, + { + "id": 362, + "algorithm": "bikel3", + "iterations": 1000 + } + ] + }, + { + "id": 16, + "name": "Experiment 2", + "end_time": 1705389926549, + "test_runs": [ + { + "id": 363, + "algorithm": "kyber512", + "iterations": 1000 + }, + { + "id": 364, + "algorithm": "bikel3", + "iterations": 1000 + }, + { + "id": 365, + "algorithm": "prime256v1", + "iterations": 1000 + } + ] + }, + { + "id": 17, + "name": "Experiment 3", + "end_time": 1705389926549, + "test_runs": [ + { + "id": 366, + "algorithm": "prime256v1", + "iterations": 500 + }, + { + "id": 367, + "algorithm": "bikel3", + "iterations": 1000 + }, + { + "id": 368, + "algorithm": "bikel3", + "iterations": 1000 + }, + { + "id": 369, + "algorithm": "prime256v1", + "iterations": 5000 + } + ] + }, + { + "id": 18, + "name": "Experiment 4", + "end_time": 1705389926549, + "test_runs": [ + { + "id": 370, + "algorithm": "kyber512", + "iterations": 500 + }, + { + "id": 371, + "algorithm": "kyber512", + "iterations": 1500 + } + ] + } +] diff --git a/portal/mock-server/src/router.ts b/portal/mock-server/src/router.ts index 8c3b27e9..5db6a10d 100644 --- a/portal/mock-server/src/router.ts +++ b/portal/mock-server/src/router.ts @@ -59,4 +59,11 @@ router.delete('/qujata-api/test_suites/:testSuiteId', async (req: Request, res: }, 1500); }); +router.post('/qujata-api/test_suites/delete', async (req: Request, res: Response) => { + console.log(`-${req.method} ${req.url}`); + setTimeout(() => { + res.status(200).send(); + }, 1500); +}); + export default router; diff --git a/portal/mock-server/src/test.json b/portal/mock-server/src/test.json index 7d9ef1e9..ed4ca2d7 100644 --- a/portal/mock-server/src/test.json +++ b/portal/mock-server/src/test.json @@ -15,7 +15,7 @@ "resourceName": "RELACE_WITH_RESOURCE_NAME" }, - "testRuns": [ + "test_runs": [ { "id":1, "algorithm": "bikel1", diff --git a/portal/package.json b/portal/package.json index 45831012..025ccd6b 100644 --- a/portal/package.json +++ b/portal/package.json @@ -8,6 +8,7 @@ "chart.js": "^4.4.0", "chartjs-plugin-annotation": "^3.0.1", "classnames": "^2.3.2", + "date-fns": "^3.3.0", "lodash": "^4.17.21", "react": "^18.2.0", "react-chartjs-2": "3.1.1", diff --git a/portal/src/app/apis.ts b/portal/src/app/apis.ts index 4897274c..409568eb 100644 --- a/portal/src/app/apis.ts +++ b/portal/src/app/apis.ts @@ -1,5 +1,5 @@ const testSuites = 'test_suites'; - + export const APIS: { [key in keyof typeof API_URLS]: string } = { analyze: 'analyze', algorithms: 'algorithms', @@ -7,8 +7,10 @@ export const APIS: { [key in keyof typeof API_URLS]: string } = { testRunResults: `${testSuites}/:testSuiteId`, editExperiment: `${testSuites}/:testSuiteId`, deleteExperiment: `${testSuites}/:testSuiteId`, + allExperiments: `${testSuites}`, + deleteExperiments: `${testSuites}/delete`, }; - + enum API_URLS { analyze, algorithms, @@ -16,4 +18,6 @@ enum API_URLS { testRunResults, editExperiment, deleteExperiment, + allExperiments, + deleteExperiments } diff --git a/portal/src/app/components/all-experiments/Experiments.module.scss b/portal/src/app/components/all-experiments/Experiments.module.scss index 3d4872e0..7fc3fb5f 100644 --- a/portal/src/app/components/all-experiments/Experiments.module.scss +++ b/portal/src/app/components/all-experiments/Experiments.module.scss @@ -7,19 +7,72 @@ padding-inline-end: 80px; } -.experiments_title { - font-size: 20px; - font-family: var($fontMedium); - margin-inline-start: 150px; +.title_options_container { + display: flex; + justify-content: space-between; + align-items: center; + + .experiments_title { + font-size: 20px; + font-family: var($fontMedium); + margin-inline-start: 150px; + } + + .options_wrapper { + margin-inline-end: 50px; + + .trash_icon { + background-color: #F5F1FF; + inline-size: 34px; + block-size: 34px; + border-radius: 50%; + } + } + + .options_wrapper:hover .hover_image { + display: block; + } + + .options_wrapper:hover .default_image { + display: none; + } + + .default_image { + padding-inline: 11px; + display: block; + } + + .hover_image { + display: none; + } } .experiments_table { margin-block-start: 60px; margin-block-end: 60px; - // .duplicate_icon { - // margin-inline-end: 20px; - // } + th, td { + text-align: left; + } + + th:first-child, + td:first-child { + text-align: center; + inline-size: 80px; + } +} + +.input_form_item { + display: none; +} + +.input_option { + margin-block-end: -5px; + + .input_option_checkbox_icon { + margin-inline-end: 10px; + cursor: pointer; + } } .spinner_wrapper { diff --git a/portal/src/app/components/all-experiments/Experiments.tsx b/portal/src/app/components/all-experiments/Experiments.tsx index b46c05af..53f6e8f9 100644 --- a/portal/src/app/components/all-experiments/Experiments.tsx +++ b/portal/src/app/components/all-experiments/Experiments.tsx @@ -1,29 +1,97 @@ -import { ReactNode, useCallback, useMemo } from 'react'; +import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; import styles from './Experiments.module.scss'; -import { Experiment, ExperimentData, IUseExperimentsData, useExperimentsData } from './hooks'; -import { FetchDataStatus } from '../../shared/hooks/useFetch'; +import cn from 'classnames'; +import { ExperimentData, IUseExperimentsData, useExperimentsData } from './hooks'; +import { FetchDataStatus, IHttp, useFetch } from '../../shared/hooks/useFetch'; import { Spinner, SpinnerSize } from '../../shared/components/att-spinner'; import { ALL_EXPERIMENTS_TABLE_EN } from './translate/en'; import { CellContext } from '@tanstack/react-table'; import { Table } from '../../shared/components/table'; -import DuplicateSvg from '../../../assets/images/duplicate.svg'; import { Button, ButtonActionType, ButtonSize, ButtonStyleType } from '../../shared/components/att-button'; +import { APIS } from '../../apis'; import { useNavigate } from 'react-router-dom'; +import { useFetchSpinner } from '../../shared/hooks/useFetchSpinner'; +import { useErrorMessage } from '../../hooks/useErrorMessage'; +import { formatDistanceToNow } from 'date-fns'; +import CheckedSvg from '../../../assets/images/checked.svg'; +import UnCheckedSvg from '../../../assets/images/unchecked.svg'; +import TrashSvg from '../../../assets/images/trash.svg'; +import TrashHoverSvg from '../../../assets/images/trash-hover.svg'; +import DuplicateSvg from '../../../assets/images/duplicate.svg'; +import { DeleteExperimentModal } from '../home/components/experiment/components/delete-experiment-modal'; +import { parseExperimentsData } from './utils/parse-experiments-data.utils'; +const DeleteAriaLabel: string = ALL_EXPERIMENTS_TABLE_EN.BUTTONS.DELETE; const DuplicateAriaLabel: string = ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.LINKS.DUPLICATE; export const Experiments: React.FC = () => { const { test_suites, status }: IUseExperimentsData = useExperimentsData(); - const experimentsData = useMemo(() => (test_suites ?? []), [test_suites]); + const [openDeleteModal, setOpenDeleteModal] = useState(false); + const [checkedRows, setCheckedRows] = useState>({}); + const experimentsData = useMemo(() => (parseExperimentsData(test_suites) ?? []), [test_suites]); const navigate = useNavigate(); - const handleDuplicateClick = useCallback((row: Experiment) => { + const { post, status: deleteStatus, error: deleteError, cancelRequest: cancelRequestDelete }: IHttp + = useFetch({ url: APIS.deleteExperiments }); + useFetchSpinner(deleteStatus); + useErrorMessage(deleteError); + useEffect(() => cancelRequestDelete, [cancelRequestDelete]); + + const handleDeleteClick: () => void = useCallback((): void => { + setOpenDeleteModal(true); + }, []); + + const handleCloseDeleteExperimentModal: (confirm?: boolean) => void = useCallback((confirm?: boolean): void => { + if (confirm) { + post({ + data: { + ids: Object.keys(checkedRows).map((key: string) => parseInt(key)) + } + }); + } + setOpenDeleteModal(false); + }, [post, checkedRows]); + + const handleCheckboxClick = useCallback((rowInfo: ExperimentData): void => { + const rowId = rowInfo.id as number; + setCheckedRows((prevState: Record) => ({ + ...prevState, + [rowId]: !prevState[rowId], + })); + }, []); + + const handleDuplicateClick = useCallback((row: ExperimentData) => { // Navigate to the Home Page navigate('/qujata', { state: { row } }); }, [navigate]); const headers = useMemo(() => { const columnDefs = [ + { + id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.CHECKBOX, + accessor: () => null, + cell: (cellInfo: CellContext) => { + const rowInfo: ExperimentData = cellInfo.row.original; + return ( +
+ row-option handleCheckboxClick(rowInfo)} + /> + handleCheckboxClick(rowInfo)} + /> +
+ ) + } + }, { id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.EXPERIMENT_NAME.ID, name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.EXPERIMENT_NAME.NAME, @@ -42,12 +110,12 @@ export const Experiments: React.FC = () => { { id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.DATE.ID, name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.DATE.NAME, - accessor: (row: ExperimentData) => row.end_time + accessor: (row: ExperimentData) => formatDistanceToNow(row.end_time as string, { addSuffix: true }) }, { id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.LINKS.DUPLICATE, accessor: () => null, - cell: (cellInfo: CellContext) => ( + cell: (cellInfo: CellContext) => ( ) }, @@ -65,17 +133,38 @@ export const Experiments: React.FC = () => { id, header: () => {name}, accessor, - cell: cell || ((cellInfo: CellContext) => {cellInfo.getValue() as ReactNode}) + cell: cell || ((cellInfo: CellContext) => {cellInfo.getValue() as ReactNode}) })); - }, [handleDuplicateClick]); + }, [checkedRows, handleCheckboxClick, handleDuplicateClick]); + + const checkedExperimentNames = experimentsData + .filter((experiment: ExperimentData) => checkedRows[experiment.id]) + .map((experiment: ExperimentData) => experiment.name); return (
{status === FetchDataStatus.Fetching ? renderSpinner() : ( -
- + <> +
+ + {Object.values(checkedRows).some((value: boolean) => value) && ( + + )} +
- + {openDeleteModal && } + )} ); diff --git a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts index b84a3eb9..541b63d0 100644 --- a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts +++ b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts @@ -3,25 +3,26 @@ import { useEffect, useState } from 'react'; import { APIS } from '../../../apis'; import { useFetchSpinner } from '../../../shared/hooks/useFetchSpinner'; import { useErrorMessage } from '../../../hooks/useErrorMessage'; -import { ITestRunResult } from '../../../shared/models/test-run-result.interface'; -import { parseExperimentsData } from '../utils/parse-experiments-data.utils'; +import { ITestRunResult, ITestRunResultData } from '../../../shared/models/test-run-result.interface'; + +export type TestRunSubset = Pick; +export type Experiment = Pick & { test_runs: TestRunSubset[] }; -export type Experiment = Partial; export interface IUseExperimentsData { test_suites: Experiment[]; status: FetchDataStatus; } export interface ExperimentData { - id?: number; - name?: string; - algorithms?: string[]; - iterations?: number[]; - end_time?: string; + id: number; + name: string; + algorithms: string[]; + iterations: number[]; + end_time: string; }; export function useExperimentsData(): IUseExperimentsData { - const [allExperiments, setAllExperiments] = useState([]); - const { get, data, cancelRequest, status, error }: IHttp = useFetch({ url: APIS.allExperiments }); + const [allExperiments, setAllExperiments] = useState([]); + const { get, data, cancelRequest, status, error }: IHttp = useFetch({ url: APIS.allExperiments }); useFetchSpinner(status); useErrorMessage(error); @@ -32,11 +33,10 @@ export function useExperimentsData(): IUseExperimentsData { useEffect(() => { - if (data && data.test_suites) { - const experimentsData: ExperimentData[] = parseExperimentsData(data.test_suites); - setAllExperiments(experimentsData); + if (status === FetchDataStatus.Success && data) { + setAllExperiments(data); } - }, [data, status]); + }, [data, status, allExperiments]); return { test_suites: allExperiments, status }; } diff --git a/portal/src/app/components/all-experiments/translate/en.ts b/portal/src/app/components/all-experiments/translate/en.ts index 943ea9ce..993412fa 100644 --- a/portal/src/app/components/all-experiments/translate/en.ts +++ b/portal/src/app/components/all-experiments/translate/en.ts @@ -1,6 +1,7 @@ export const ALL_EXPERIMENTS_TABLE_EN = { TITLE: 'All Experiments', TABLE_COLUMNS: { + CHECKBOX: 'checkbox', EXPERIMENT_NAME: { NAME: 'Experiment Name', ID: 'experimentName' @@ -20,5 +21,8 @@ export const ALL_EXPERIMENTS_TABLE_EN = { LINKS: { DUPLICATE: 'duplicate', } + }, + BUTTONS: { + DELETE: 'Delete', } } diff --git a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts index 8100e25e..309d952f 100644 --- a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts +++ b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts @@ -8,7 +8,7 @@ describe('parseExperimentsData', () => { { id: 1, name: 'Experiment 1', - testRuns: [ + test_runs: [ { algorithm: 'Algorithm 1', iterations: 1000 } as ITestRunResultData, { algorithm: 'Algorithm 2', iterations: 5000 } as ITestRunResultData, ], diff --git a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts index 8af93fd3..f1abd602 100644 --- a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts +++ b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts @@ -1,5 +1,4 @@ -import { ITestRunResultData } from '../../../shared/models/test-run-result.interface'; -import { Experiment, ExperimentData } from '../hooks'; +import { Experiment, ExperimentData, TestRunSubset } from '../hooks'; export function parseExperimentsData(test_suites: Experiment[]) { const experimentsData: ExperimentData[] = []; @@ -7,17 +6,17 @@ export function parseExperimentsData(test_suites: Experiment[]) { test_suites.forEach((experiment: Experiment) => { const algorithms = new Set(); const iterations = new Set(); - experiment.testRuns?.forEach((testRun: ITestRunResultData) => { + experiment.test_runs?.forEach((testRun: TestRunSubset) => { algorithms.add(testRun.algorithm); iterations.add(testRun.iterations); }); experimentsData.push({ - id: experiment.id ?? 0, - name: experiment.name ?? '', + id: experiment.id, + name: experiment.name, algorithms: Array.from(algorithms), iterations: Array.from(iterations), - end_time: experiment.end_time ?? '', + end_time: experiment.end_time }); }); diff --git a/portal/src/app/components/home/Home.tsx b/portal/src/app/components/home/Home.tsx index ebc822b7..bd2132f0 100644 --- a/portal/src/app/components/home/Home.tsx +++ b/portal/src/app/components/home/Home.tsx @@ -6,7 +6,7 @@ import { SubHeader } from "../sub-header"; import { useCallback, useEffect, useState } from 'react'; import styles from './Home.module.scss'; import { useLocation, useNavigate } from "react-router-dom"; -import { Experiment, ExperimentData } from "../all-experiments/hooks"; +import { ExperimentData } from "../all-experiments/hooks"; export const Home: React.FC = () => { const [isSubHeaderOpen, setIsSubHeaderOpen] = useState(true); diff --git a/portal/src/app/components/home/components/experiment/components/__mocks__/mocks.ts b/portal/src/app/components/home/components/experiment/components/__mocks__/mocks.ts index 04658e61..8337a1aa 100644 --- a/portal/src/app/components/home/components/experiment/components/__mocks__/mocks.ts +++ b/portal/src/app/components/home/components/experiment/components/__mocks__/mocks.ts @@ -17,7 +17,7 @@ export const MOCK_DATA_FOR_EXPERIMENT: ITestRunResult = { nodeSize: "Standard_D4s_v5", codeRelease: "1.1.0", }, - testRuns: [ + test_runs: [ { id: 1, algorithm: "Algorithm1", @@ -68,7 +68,7 @@ export const MOCK_DATA_FOR_EXPERIMENT_TABLE: ExperimentTableProps = { nodeSize: "Standard_D4s_v5", codeRelease: "1.1.0", }, - testRuns: [ + test_runs: [ { id: 1, algorithm: "Algorithm1", @@ -138,7 +138,7 @@ export const MOCK_DATA_FOR_EXPERIMENT_WITH_NO_TEST_RUNS: ExperimentTableProps = nodeSize: "Standard_D4s_v5", codeRelease: "1.1.0", }, - testRuns: [] + test_runs: [] }, selectedColumns: [ { @@ -176,7 +176,7 @@ export const MOCK_SUB_HEADER: ITestRunResult = { operatingSystem: 'codeRelease', resourceName: 'codeRelease', }, - testRuns: [ + test_runs: [ { id:1, algorithm: "bikel1", diff --git a/portal/src/app/components/home/components/experiment/components/charts/__mocks__/mocks.ts b/portal/src/app/components/home/components/experiment/components/charts/__mocks__/mocks.ts index 690e7966..5d2a5260 100644 --- a/portal/src/app/components/home/components/experiment/components/charts/__mocks__/mocks.ts +++ b/portal/src/app/components/home/components/experiment/components/charts/__mocks__/mocks.ts @@ -17,7 +17,7 @@ export const MOCK_DATA_FOR_CHARTS: IExperimentData = { nodeSize: "Standard_D4s_v5", codeRelease: "1.1.0", }, - testRuns: [ + test_runs: [ { id: 1, algorithm: "Algorithm1", diff --git a/portal/src/app/components/home/components/experiment/components/charts/hooks/useChartsData.ts b/portal/src/app/components/home/components/experiment/components/charts/hooks/useChartsData.ts index 40475047..73eb95ed 100644 --- a/portal/src/app/components/home/components/experiment/components/charts/hooks/useChartsData.ts +++ b/portal/src/app/components/home/components/experiment/components/charts/hooks/useChartsData.ts @@ -62,8 +62,8 @@ export function useChartsData(props: IExperimentData): IUseChartsData { const [lineChartData, setLineChartData] = useState(); useEffect(() => { - if(props.data && props.data.testRuns.length > 0) { - const testRuns: ITestRunResultData[] = props.data.testRuns; + if(props.data && props.data.test_runs.length > 0) { + const testRuns: ITestRunResultData[] = props.data.test_runs; setBarChartData(testRuns); const labels: string[] = getLabels(testRuns); setBarChartLabels(labels); diff --git a/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/DeleteExperimentModal.test.tsx b/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/DeleteExperimentModal.test.tsx index 4a45342e..ada5f26b 100644 --- a/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/DeleteExperimentModal.test.tsx +++ b/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/DeleteExperimentModal.test.tsx @@ -5,7 +5,7 @@ import { DeleteExperimentModal, DeleteExperimentModalProps } from './DeleteExper describe('EditExperimentModal', () => { test('renders edit Experiment modal correctly', () => { const props: DeleteExperimentModalProps = { - name: 'Test', + name: ['Test'], onClose: jest.fn(), }; const { baseElement }: RenderResult = render(TestMe); @@ -15,7 +15,7 @@ describe('EditExperimentModal', () => { test('click submit button', () => { const handleClose = jest.fn(); const props: DeleteExperimentModalProps = { - name: 'Test', + name: ['Test'], onClose: handleClose, }; const { getByRole }: RenderResult = render(TestMe); diff --git a/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/DeleteExperimentModal.tsx b/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/DeleteExperimentModal.tsx index 70f5b935..2083c2fa 100644 --- a/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/DeleteExperimentModal.tsx +++ b/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/DeleteExperimentModal.tsx @@ -4,18 +4,16 @@ import { BaseModal } from '../../../../../../shared/components/modal'; import { ButtonActionType, ButtonSize, ButtonStyleType, IButton } from '../../../../../../shared/components/att-button'; import { DELETE_EXPERIMENT_MODAL_EN } from './translate/en'; import { BaseModalSize } from '../../../../../../shared/components/modal/base-modal.const'; -import { translateParserService } from '../../../../../../shared/utils/translate-parser'; export interface DeleteExperimentModalProps { onClose: (confirm?: boolean) => void; - name: string; + name: string[]; } export const DeleteExperimentModal: React.FC = (props: DeleteExperimentModalProps) => { const { name, onClose } = props; const [actionButtons, setActionButtons] = useState([]); - const description: string = translateParserService.interpolateString(DELETE_EXPERIMENT_MODAL_EN.DESCRIPTION, { name }); - + const experimentToDelete = name.map((experimentName, index) =>
  • {experimentName}
  • ); useLayoutEffect(() => { const submitButton: IButton = { @@ -37,7 +35,10 @@ export const DeleteExperimentModal: React.FC = (prop actionButton={actionButtons} size={BaseModalSize.SMALL} > -
    {description}
    +
    +

    {DELETE_EXPERIMENT_MODAL_EN.DESCRIPTION}

    +
      {experimentToDelete}
    +
    ); }; diff --git a/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/translate/en.ts b/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/translate/en.ts index 37e635ca..5af4202e 100644 --- a/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/translate/en.ts +++ b/portal/src/app/components/home/components/experiment/components/delete-experiment-modal/translate/en.ts @@ -1,5 +1,5 @@ export const DELETE_EXPERIMENT_MODAL_EN = { SUBMIT_ACTION: 'Confirm', TITLE: 'Delete Experiment', - DESCRIPTION: 'Are you sure you want to delete "{{name}}" experiment?', + DESCRIPTION: 'Are you sure you want to delete the following experiment(s)?' }; diff --git a/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.tsx b/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.tsx index c873d676..49fa4dc2 100644 --- a/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.tsx +++ b/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.tsx @@ -12,7 +12,7 @@ export interface ExperimentTableProps { } export const ExperimentTable: React.FC = (props: ExperimentTableProps) => { - const data = useMemo(() => (props.data ? props.data.testRuns : []), [props.data]); + const data = useMemo(() => (props.data ? props.data.test_runs : []), [props.data]); const headers: TableColumn[] = useMemo(() => [ { diff --git a/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.test.ts b/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.test.ts index c59d722d..38a54179 100644 --- a/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.test.ts +++ b/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.test.ts @@ -21,10 +21,10 @@ describe('useExperimentData', () => { (useFetchSpinner as jest.Mock).mockImplementation(() => undefined); (useErrorMessage as jest.Mock).mockImplementation(() => undefined); - const mockDataNumOfTestRuns = MOCK_DATA_FOR_EXPERIMENT.testRuns.length; + const mockDataNumOfTestRuns = MOCK_DATA_FOR_EXPERIMENT.test_runs.length; const { result } = renderHook(() => useExperimentData()); - expect(result.current.data.testRuns.length).toEqual(mockDataNumOfTestRuns); + expect(result.current.data.test_runs.length).toEqual(mockDataNumOfTestRuns); }); test('Should not render data', () => { diff --git a/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.ts b/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.ts index 6039ed60..d1c0bf57 100644 --- a/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.ts +++ b/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.ts @@ -29,9 +29,9 @@ export function useExperimentData(): IUseExperimentData { }, [get, cancelRequest]); useEffect(() => { - if (data && data.testRuns) { - const sortedData: ITestRunResultData[] = sortDataByAlgorithm(data.testRuns); - setTestRunData({ ...data, testRuns: sortedData }); + if (data && data.test_runs) { + const sortedData: ITestRunResultData[] = sortDataByAlgorithm(data.test_runs); + setTestRunData({ ...data, test_runs: sortedData }); } }, [data]); diff --git a/portal/src/app/components/home/components/experiment/components/sub-header/SubHeader.tsx b/portal/src/app/components/home/components/experiment/components/sub-header/SubHeader.tsx index 6818c4f3..6c884ace 100644 --- a/portal/src/app/components/home/components/experiment/components/sub-header/SubHeader.tsx +++ b/portal/src/app/components/home/components/experiment/components/sub-header/SubHeader.tsx @@ -62,8 +62,8 @@ export const SubHeader: React.FC = (props: SubHeaderProps) => { const handleDownloadClick: () => void = useCallback((): void => { const csvFileName: string = `${SUB_HEADER_EN.CSV_REPORT.FILE_NAME}-${name || ''}.csv`; - downloadCsvFile(mapExperimentDataToCsvDataType(data.testRuns), csvFileName); - }, [data.testRuns, name]); + downloadCsvFile(mapExperimentDataToCsvDataType(data.test_runs), csvFileName); + }, [data.test_runs, name]); const handleCloseEditExperimentModal: (editData?: EditExperimentModalData) => void = useCallback((editData?: EditExperimentModalData): void => { if (editData) { @@ -103,11 +103,11 @@ export const SubHeader: React.FC = (props: SubHeaderProps) => {
    {SUB_HEADER_EN.ALGORITHM}
    -
    {getAlgorithmsName(data.testRuns)}
    +
    {getAlgorithmsName(data.test_runs)}
    {SUB_HEADER_EN.ITERATIONS}
    -
    {getIterations(data.testRuns)}
    +
    {getIterations(data.test_runs)}
    {experimentDescription}
    @@ -136,7 +136,7 @@ export const SubHeader: React.FC = (props: SubHeaderProps) => { {openEditModal && } - {openDeleteModal && } + {openDeleteModal && } ); } diff --git a/portal/src/app/components/protocol-query/ProtocolQuery.tsx b/portal/src/app/components/protocol-query/ProtocolQuery.tsx index 0db92e65..e35be1ab 100644 --- a/portal/src/app/components/protocol-query/ProtocolQuery.tsx +++ b/portal/src/app/components/protocol-query/ProtocolQuery.tsx @@ -1,5 +1,5 @@ import { noop } from 'lodash'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { Options } from 'react-select'; import { ITestParams } from '../../shared/models/quantum.interface'; import { Button, ButtonActionType, ButtonSize, ButtonStyleType } from '../../shared/components/att-button'; @@ -41,7 +41,6 @@ export const ProtocolQuery: React.FC = (props: ProtocolQuery // TODO: move this useEffect into different file useEffect(() => { - console.log('duplicateData ProtocolQuery', duplicateData); if (duplicateData) { if (duplicateData && duplicateData.name) { setExperimentName(duplicateData.name); diff --git a/portal/src/app/shared/models/test-run-result.interface.ts b/portal/src/app/shared/models/test-run-result.interface.ts index 809f3bd6..d81f30e3 100644 --- a/portal/src/app/shared/models/test-run-result.interface.ts +++ b/portal/src/app/shared/models/test-run-result.interface.ts @@ -27,5 +27,5 @@ export interface ITestRunResult { start_time: string; end_time: string; environment_info: IEnvironmentInfo; - testRuns: ITestRunResultData[]; + test_runs: ITestRunResultData[]; } diff --git a/portal/src/assets/images/trash-hover.svg b/portal/src/assets/images/trash-hover.svg new file mode 100644 index 00000000..81fe1736 --- /dev/null +++ b/portal/src/assets/images/trash-hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/portal/yarn.lock b/portal/yarn.lock index ddc70411..fcf80b7b 100644 --- a/portal/yarn.lock +++ b/portal/yarn.lock @@ -4034,6 +4034,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +date-fns@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.3.0.tgz#c1681691cf751a1d371279099a45e71409c7c761" + integrity sha512-xuouT0GuI2W8yXhCMn/AXbSl1Av3wu2hJXxMnnILTY3bYY0UgNK0qOwVXqdFBrobW5qbX1TuOTgMw7c2H2OuhA== + debug@2.6.9, debug@^2.6.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" From d92704f0c932322ca4db0f56264c2884872ad963 Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Mon, 22 Jan 2024 14:52:52 +0200 Subject: [PATCH 06/15] time from string to number --- .../all-experiments/Experiments.tsx | 2 +- .../hooks/useExperimentsData.test.ts | 64 +++++++++---------- .../hooks/useExperimentsData.ts | 4 +- .../parse-experiments-data.utils.test.ts | 4 +- .../components/experiment/Experiment.test.tsx | 2 +- .../experiment/components/__mocks__/mocks.ts | 30 ++++----- .../components/charts/__mocks__/mocks.ts | 4 +- .../__snapshots__/TableOptions.test.tsx.snap | 2 +- .../__snapshots__/ProtocolQuery.test.tsx.snap | 1 + .../models/test-run-result.interface.ts | 4 +- 10 files changed, 58 insertions(+), 59 deletions(-) diff --git a/portal/src/app/components/all-experiments/Experiments.tsx b/portal/src/app/components/all-experiments/Experiments.tsx index 53f6e8f9..d2505f60 100644 --- a/portal/src/app/components/all-experiments/Experiments.tsx +++ b/portal/src/app/components/all-experiments/Experiments.tsx @@ -110,7 +110,7 @@ export const Experiments: React.FC = () => { { id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.DATE.ID, name: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.DATE.NAME, - accessor: (row: ExperimentData) => formatDistanceToNow(row.end_time as string, { addSuffix: true }) + accessor: (row: ExperimentData) => formatDistanceToNow(row.end_time, { addSuffix: true }) }, { id: ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.LINKS.DUPLICATE, diff --git a/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts b/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts index a79296f7..cee957b7 100644 --- a/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts +++ b/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts @@ -1,6 +1,6 @@ import { renderHook } from '@testing-library/react'; import { useFetch } from '../../../shared/hooks/useFetch'; -import { useExperimentsData } from './useExperimentsData'; +import { Experiment, useExperimentsData } from './useExperimentsData'; jest.mock('../../../shared/hooks/useFetch', () => ({ useFetch: jest.fn(), @@ -10,35 +10,34 @@ jest.mock('../../../hooks/useErrorMessage'); describe('useExperimentsData', () => { test('Should be in Success mode', () => { - const allExperimentsMockData = { - test_suites: [ - { - id: 17, - name: "Experiment 3", - end_time: 1705389926549, - test_runs: [ - { - id: 366, - algorithm: "prime256v1", - iterations: 500 - }, - { - id: 367, - algorithm: "bikel3", - iterations: 1000 - }, - { - id: 368, - algorithm: "p256_kyber512", - iterations: 10000 - }, - { - id: 369, - algorithm: "prime256v1", - iterations: 5000 - } - ] - }, + const allExperimentsMockData: Experiment[] = [ + { + id: 17, + name: "Experiment 3", + end_time: 1705389926549, + test_runs: [ + { + id: 366, + algorithm: "prime256v1", + iterations: 500 + }, + { + id: 367, + algorithm: "bikel3", + iterations: 1000 + }, + { + id: 368, + algorithm: "p256_kyber512", + iterations: 10000 + }, + { + id: 369, + algorithm: "prime256v1", + iterations: 5000 + } + ] + }, { id: 18, name: "Experiment 4", @@ -56,8 +55,7 @@ describe('useExperimentsData', () => { } ] } - ] - }; + ]; (useFetch as jest.Mock).mockReturnValue({ get: jest.fn(), @@ -66,6 +64,6 @@ describe('useExperimentsData', () => { }); const { result } = renderHook(() => useExperimentsData()); - expect(result.current.test_suites.length).toEqual(allExperimentsMockData.test_suites.length); + expect(result.current.test_suites.length).toEqual(allExperimentsMockData.length); }); }); \ No newline at end of file diff --git a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts index 541b63d0..b10563e2 100644 --- a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts +++ b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts @@ -17,7 +17,7 @@ export interface ExperimentData { name: string; algorithms: string[]; iterations: number[]; - end_time: string; + end_time: number; }; export function useExperimentsData(): IUseExperimentsData { @@ -33,7 +33,7 @@ export function useExperimentsData(): IUseExperimentsData { useEffect(() => { - if (status === FetchDataStatus.Success && data) { + if (data) { setAllExperiments(data); } }, [data, status, allExperiments]); diff --git a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts index 309d952f..241ed2ff 100644 --- a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts +++ b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts @@ -12,7 +12,7 @@ describe('parseExperimentsData', () => { { algorithm: 'Algorithm 1', iterations: 1000 } as ITestRunResultData, { algorithm: 'Algorithm 2', iterations: 5000 } as ITestRunResultData, ], - end_time: '2022-01-01T00:00:00Z', + end_time: 1705240065192, }, ]; @@ -22,7 +22,7 @@ describe('parseExperimentsData', () => { name: 'Experiment 1', algorithms: ['Algorithm 1', 'Algorithm 2'], iterations: [1000, 5000], - end_time: '2022-01-01T00:00:00Z', + end_time: 1705240065192, }, ]; diff --git a/portal/src/app/components/home/components/experiment/Experiment.test.tsx b/portal/src/app/components/home/components/experiment/Experiment.test.tsx index 784fcfcd..d6c31b20 100644 --- a/portal/src/app/components/home/components/experiment/Experiment.test.tsx +++ b/portal/src/app/components/home/components/experiment/Experiment.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor, within } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import { SubHeader } from './components/sub-header'; import { Charts } from './components/charts'; import { Experiment, ExperimentContent } from './Experiment'; diff --git a/portal/src/app/components/home/components/experiment/components/__mocks__/mocks.ts b/portal/src/app/components/home/components/experiment/components/__mocks__/mocks.ts index 8337a1aa..4308d519 100644 --- a/portal/src/app/components/home/components/experiment/components/__mocks__/mocks.ts +++ b/portal/src/app/components/home/components/experiment/components/__mocks__/mocks.ts @@ -5,8 +5,8 @@ export const MOCK_DATA_FOR_EXPERIMENT: ITestRunResult = { id: 1, name: "TestRun1", description: "TestRun1", - start_time: "2021-07-26T12:00:00.000Z", - end_time: "2021-07-26T12:00:00.000Z", + start_time: 1705240065192, + end_time: 1705240065192, environment_info: { resourceName: "gddn-aks", operatingSystem: "Linux", @@ -56,8 +56,8 @@ export const MOCK_DATA_FOR_EXPERIMENT_TABLE: ExperimentTableProps = { id: 1, name: "TestRun1", description: "TestRun1", - start_time: "2021-07-26T12:00:00.000Z", - end_time: "2021-07-26T12:00:00.000Z", + start_time: 1705240065192, + end_time: 1705240065192, environment_info: { resourceName: "gddn-aks", operatingSystem: "Linux", @@ -126,8 +126,8 @@ export const MOCK_DATA_FOR_EXPERIMENT_WITH_NO_TEST_RUNS: ExperimentTableProps = id: 1, name: "TestRun1", description: "TestRun1", - start_time: "2021-07-26T12:00:00.000Z", - end_time: "2021-07-26T12:00:00.000Z", + start_time: 1705240065192, + end_time: 1705240065192, environment_info: { resourceName: "gddn-aks", operatingSystem: "Linux", @@ -163,18 +163,18 @@ export const MOCK_DATA_FOR_EXPERIMENT_WITH_NO_TEST_RUNS: ExperimentTableProps = export const MOCK_SUB_HEADER: ITestRunResult = { id: 1, name: 'name', - description: 'name', - start_time: 'name', - end_time: 'name', + description: 'description', + start_time: 1705240065192, + end_time: 1705240065192, environment_info: { codeRelease: 'codeRelease', - cpu: 'codeRelease', - cpuArchitecture: 'codeRelease', - cpuClockSpeed: 'codeRelease', + cpu: 'cpu', + cpuArchitecture: 'cpuArchitecture', + cpuClockSpeed: 'cpuClockSpeed', cpuCores: 2, - nodeSize: 'codeRelease', - operatingSystem: 'codeRelease', - resourceName: 'codeRelease', + nodeSize: 'nodeSize', + operatingSystem: 'operatingSystem', + resourceName: 'resourceName', }, test_runs: [ { diff --git a/portal/src/app/components/home/components/experiment/components/charts/__mocks__/mocks.ts b/portal/src/app/components/home/components/experiment/components/charts/__mocks__/mocks.ts index 5d2a5260..102dd207 100644 --- a/portal/src/app/components/home/components/experiment/components/charts/__mocks__/mocks.ts +++ b/portal/src/app/components/home/components/experiment/components/charts/__mocks__/mocks.ts @@ -5,8 +5,8 @@ export const MOCK_DATA_FOR_CHARTS: IExperimentData = { id: 1, name: "TestRun1", description: "TestRun1", - start_time: "2021-07-26T12:00:00.000Z", - end_time: "2021-07-26T12:00:00.000Z", + start_time: 1705240065192, + end_time: 1705240065192, environment_info: { resourceName: "gddn-aks", operatingSystem: "Linux", diff --git a/portal/src/app/components/home/components/experiment/components/table-options/__snapshots__/TableOptions.test.tsx.snap b/portal/src/app/components/home/components/experiment/components/table-options/__snapshots__/TableOptions.test.tsx.snap index 7ebde19e..da190153 100644 --- a/portal/src/app/components/home/components/experiment/components/table-options/__snapshots__/TableOptions.test.tsx.snap +++ b/portal/src/app/components/home/components/experiment/components/table-options/__snapshots__/TableOptions.test.tsx.snap @@ -6,7 +6,7 @@ exports[`TableOptions renders without crashing 1`] = ` > diff --git a/portal/src/app/components/protocol-query/__snapshots__/ProtocolQuery.test.tsx.snap b/portal/src/app/components/protocol-query/__snapshots__/ProtocolQuery.test.tsx.snap index f4846208..73a8f54e 100644 --- a/portal/src/app/components/protocol-query/__snapshots__/ProtocolQuery.test.tsx.snap +++ b/portal/src/app/components/protocol-query/__snapshots__/ProtocolQuery.test.tsx.snap @@ -45,6 +45,7 @@ exports[`ProtocolQuery should render ProtocolQuery 1`] = ` class="input_form_item" placeholder="" required="" + value="" />
    Date: Mon, 22 Jan 2024 15:35:38 +0200 Subject: [PATCH 07/15] useDuplicateData export --- portal/src/app/components/home/Home.tsx | 3 +- .../protocol-query/ProtocolQuery.tsx | 36 +------- .../components/protocol-query/hooks/index.ts | 1 + .../hooks/useDuplicateData.test.ts | 92 +++++++++++++++++++ .../protocol-query/hooks/useDuplicateData.ts | 35 +++++++ 5 files changed, 132 insertions(+), 35 deletions(-) create mode 100644 portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts create mode 100644 portal/src/app/components/protocol-query/hooks/useDuplicateData.ts diff --git a/portal/src/app/components/home/Home.tsx b/portal/src/app/components/home/Home.tsx index bd2132f0..d762aa59 100644 --- a/portal/src/app/components/home/Home.tsx +++ b/portal/src/app/components/home/Home.tsx @@ -28,10 +28,9 @@ export const HomeContent: React.FC = () => { const navigate = useNavigate(); const location = useLocation(); const [duplicateData, setDuplicateData] = useState(location.state?.row); - // const duplicateData: Experiment = location.state?.row; useEffect(() => { - // after the duplicate data has been created, we need to clear the state + // Clear the state after the duplicate data has been created setDuplicateData(undefined); }, [location]); diff --git a/portal/src/app/components/protocol-query/ProtocolQuery.tsx b/portal/src/app/components/protocol-query/ProtocolQuery.tsx index e35be1ab..f03a41cc 100644 --- a/portal/src/app/components/protocol-query/ProtocolQuery.tsx +++ b/portal/src/app/components/protocol-query/ProtocolQuery.tsx @@ -1,5 +1,5 @@ import { noop } from 'lodash'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { Options } from 'react-select'; import { ITestParams } from '../../shared/models/quantum.interface'; import { Button, ButtonActionType, ButtonSize, ButtonStyleType } from '../../shared/components/att-button'; @@ -11,6 +11,7 @@ import { useGetAlgorithms, useGetIterations } from './hooks'; import { handleAlgorithmsSelection } from './utils'; import { AlgorithmsSelectorCustomOption, IterationsSelectorCustomOption } from '../../shared/components/selector-custom-option'; import { ExperimentData } from '../all-experiments/hooks'; +import { useDuplicateData } from './hooks'; export type SelectOptionType = AttSelectOption | Options | null; type onTextChangedEvent = (e: React.ChangeEvent) => void; @@ -39,29 +40,8 @@ export const ProtocolQuery: React.FC = (props: ProtocolQuery const [inputValue, setInputValue] = useState(''); const [iterationsMenuIsOpen, setIterationsMenuIsOpen] = useState(false); - // TODO: move this useEffect into different file - useEffect(() => { - if (duplicateData) { - if (duplicateData && duplicateData.name) { - setExperimentName(duplicateData.name); - } - if (duplicateData && duplicateData.algorithms) { - const algorithmOptions = duplicateData.algorithms.map((algorithm: string) => { - return { label: algorithm, value: algorithm } as AttSelectOption; - }); - setAlgorithms(algorithmOptions); - } + useDuplicateData({ data: duplicateData, setDuplicateData, setExperimentName, setAlgorithms, setIterationsCount }); - if (duplicateData && duplicateData.iterations) { - const iterationsOptions = duplicateData.iterations.map((iteration: number) => { - return { label: iteration.toString(), value: iteration.toString() } as AttSelectOption; - }); - setIterationsCount(iterationsOptions); - } - setDuplicateData(undefined); - } - }, [duplicateData, setDuplicateData]); - const onSubmitHandler = (event: React.FormEvent) => { event.preventDefault(); onRunClick({ @@ -190,16 +170,6 @@ export const ProtocolQuery: React.FC = (props: ProtocolQuery
    } - {/* */} ); }; diff --git a/portal/src/app/components/protocol-query/hooks/index.ts b/portal/src/app/components/protocol-query/hooks/index.ts index b7318e3e..f5861164 100644 --- a/portal/src/app/components/protocol-query/hooks/index.ts +++ b/portal/src/app/components/protocol-query/hooks/index.ts @@ -1,2 +1,3 @@ export * from './useGetAlgorithms'; export * from './useGetIterations'; +export * from './useDuplicateData'; diff --git a/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts b/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts new file mode 100644 index 00000000..775bda54 --- /dev/null +++ b/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts @@ -0,0 +1,92 @@ +import { renderHook, act } from '@testing-library/react-hooks'; +import { useDuplicateData, DuplicateData } from './useDuplicateData'; +import { AttSelectOption } from '../../../shared/components/att-select'; +import { ExperimentData } from '../../all-experiments/hooks'; + +describe('useDuplicateData', () => { + // it('should set experiment name, algorithms, and iterations count when duplicate data is provided', () => { + // const setExperimentName = jest.fn(); + // const setAlgorithms = jest.fn(); + // const setIterationsCount = jest.fn(); + // const setDuplicateData = jest.fn(); + + // const duplicateData: ExperimentData = { + // id: 1111, + // name: 'test', + // algorithms: ['algorithm1', 'algorithm2'], + // iterations: [1, 2, 3], + // end_time: 1705240065192, + // }; + + // const { rerender } = renderHook((props: DuplicateData) => useDuplicateData(props), { + // initialProps: { + // data: undefined, + // setDuplicateData, + // setExperimentName, + // setAlgorithms, + // setIterationsCount, + // } as DuplicateData, + // }); + + // expect(setExperimentName).not.toHaveBeenCalled(); + // expect(setAlgorithms).not.toHaveBeenCalled(); + // expect(setIterationsCount).not.toHaveBeenCalled(); + // expect(setDuplicateData).not.toHaveBeenCalled(); + + // rerender({ + // data: duplicateData, + // setDuplicateData, + // setExperimentName, + // setAlgorithms, + // setIterationsCount, + // }); + + // expect(setExperimentName).toHaveBeenCalledWith(duplicateData.name); + // expect(setAlgorithms).toHaveBeenCalledWith(duplicateData.algorithms.map(algorithm => ({ label: algorithm, value: algorithm } as AttSelectOption))); + // expect(setIterationsCount).toHaveBeenCalledWith(duplicateData.iterations.map(iteration => ({ label: iteration.toString(), value: iteration.toString() } as AttSelectOption))); + // expect(setDuplicateData).toHaveBeenCalledWith(undefined); + // }); + + it('should set experiment name, algorithms, and iterations count when duplicate data is provided', () => { + const setExperimentName = jest.fn(); + const setAlgorithms = jest.fn(); + const setIterationsCount = jest.fn(); + const setDuplicateData = jest.fn(); + + const duplicateData: ExperimentData = { + id: 1111, + name: 'test', + algorithms: ['algorithm1', 'algorithm2'], + iterations: [1, 2, 3], + end_time: 1705240065192, + }; + + const { rerender } = renderHook((props: DuplicateData) => useDuplicateData(props), { + initialProps: { + data: undefined, + setDuplicateData, + setExperimentName, + setAlgorithms, + setIterationsCount, + } as DuplicateData, + }); + + expect(setExperimentName).not.toHaveBeenCalled(); + expect(setAlgorithms).not.toHaveBeenCalled(); + expect(setIterationsCount).not.toHaveBeenCalled(); + expect(setDuplicateData).not.toHaveBeenCalled(); + + rerender({ + data: duplicateData, + setDuplicateData, + setExperimentName, + setAlgorithms, + setIterationsCount, + }); + + expect(setExperimentName).toHaveBeenCalledWith(duplicateData.name); + expect(setAlgorithms).toHaveBeenCalledWith(duplicateData.algorithms.map(algorithm => ({ label: algorithm, value: algorithm } as AttSelectOption))); + expect(setIterationsCount).toHaveBeenCalledWith(duplicateData.iterations.map(iteration => ({ label: iteration.toString(), value: iteration.toString() } as AttSelectOption))); + expect(setDuplicateData).toHaveBeenCalledWith(undefined); + }); +}); diff --git a/portal/src/app/components/protocol-query/hooks/useDuplicateData.ts b/portal/src/app/components/protocol-query/hooks/useDuplicateData.ts new file mode 100644 index 00000000..d2e9b7c3 --- /dev/null +++ b/portal/src/app/components/protocol-query/hooks/useDuplicateData.ts @@ -0,0 +1,35 @@ +import { useEffect } from 'react'; +import { AttSelectOption } from '../../../shared/components/att-select'; +import { ExperimentData } from '../../all-experiments/hooks'; + +export type DuplicateData = { + data: ExperimentData | undefined, + setDuplicateData: (data: any) => void, + setExperimentName: (name: string) => void, + setAlgorithms: (options: AttSelectOption[]) => void, + setIterationsCount: (options: AttSelectOption[]) => void +} +export const useDuplicateData = (duplicate: DuplicateData) => { + useEffect(() => { + if (duplicate.data) { + const duplicateData = duplicate.data; + if (duplicateData.name) { + duplicate.setExperimentName(duplicateData.name); + } + if (duplicateData.algorithms) { + const algorithmOptions = duplicateData.algorithms.map((algorithm: string) => { + return { label: algorithm, value: algorithm } as AttSelectOption; + }); + duplicate.setAlgorithms(algorithmOptions); + } + + if (duplicateData.iterations) { + const iterationsOptions = duplicateData.iterations.map((iteration: number) => { + return { label: iteration.toString(), value: iteration.toString() } as AttSelectOption; + }); + duplicate.setIterationsCount(iterationsOptions); + } + duplicate.setDuplicateData(undefined); + } + }, [duplicate]); +}; From 19cceb0eea293934014c6ec954c3b592b6959449 Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Mon, 22 Jan 2024 16:05:35 +0200 Subject: [PATCH 08/15] no experiments handling --- portal/src/app/components/all-experiments/Experiments.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/portal/src/app/components/all-experiments/Experiments.tsx b/portal/src/app/components/all-experiments/Experiments.tsx index d2505f60..5ebb8243 100644 --- a/portal/src/app/components/all-experiments/Experiments.tsx +++ b/portal/src/app/components/all-experiments/Experiments.tsx @@ -162,7 +162,7 @@ export const Experiments: React.FC = () => { )} -
    + {experimentsData.length > 0 &&
    } {openDeleteModal && } )} From 76743bc903ed433fd5b9da4322e83d890210d127 Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Mon, 22 Jan 2024 16:58:00 +0200 Subject: [PATCH 09/15] unitests --- .../all-experiments/Experiments.test.tsx | 43 +++++++++++++++++-- .../all-experiments/Experiments.tsx | 7 ++- .../__snapshots__/Experiments.test.tsx.snap | 41 ++++++++++++++++++ .../hooks/useDuplicateData.test.ts | 43 ------------------- 4 files changed, 84 insertions(+), 50 deletions(-) create mode 100644 portal/src/app/components/all-experiments/__snapshots__/Experiments.test.tsx.snap diff --git a/portal/src/app/components/all-experiments/Experiments.test.tsx b/portal/src/app/components/all-experiments/Experiments.test.tsx index a63b4138..8999f49b 100644 --- a/portal/src/app/components/all-experiments/Experiments.test.tsx +++ b/portal/src/app/components/all-experiments/Experiments.test.tsx @@ -1,7 +1,44 @@ +import { render, fireEvent } from '@testing-library/react'; import { Experiments } from './Experiments'; +import { useExperimentsData } from './hooks'; +import { FetchDataStatus, useFetch } from '../../shared/hooks/useFetch'; + +jest.mock('./hooks'); +jest.mock('../../shared/hooks/useFetch'); +jest.mock('react-router-dom', () => ({ + useNavigate: jest.fn(), +})); describe('Experiments', () => { - test('should render Experiments', async () => { - expect(true).toBe(true); + it('renders correctly', () => { + (useExperimentsData as jest.Mock).mockReturnValue({ + test_suites: [{ + id: 15, + name: "Experiment 1", + end_time: 1705240065192, + test_runs: [ + { + id: 354, + algorithm: "prime256v1", + iterations: 100 + }, + { + id: 355, + algorithm: "prime256v1", + iterations: 500 + } + ] + }], + status: FetchDataStatus.Fetching, + }); + (useFetch as jest.Mock).mockReturnValue({ + post: jest.fn(), + status: FetchDataStatus.Fetching, + error: null, + cancelRequest: jest.fn(), + }); + + const { container } = render(); + expect(container).toMatchSnapshot(); }); -}); +}); \ No newline at end of file diff --git a/portal/src/app/components/all-experiments/Experiments.tsx b/portal/src/app/components/all-experiments/Experiments.tsx index 5ebb8243..67f76dd5 100644 --- a/portal/src/app/components/all-experiments/Experiments.tsx +++ b/portal/src/app/components/all-experiments/Experiments.tsx @@ -28,7 +28,7 @@ export const Experiments: React.FC = () => { const { test_suites, status }: IUseExperimentsData = useExperimentsData(); const [openDeleteModal, setOpenDeleteModal] = useState(false); const [checkedRows, setCheckedRows] = useState>({}); - const experimentsData = useMemo(() => (parseExperimentsData(test_suites) ?? []), [test_suites]); + const experimentsData = useMemo(() => (parseExperimentsData(test_suites)), [test_suites]); const navigate = useNavigate(); const { post, status: deleteStatus, error: deleteError, cancelRequest: cancelRequestDelete }: IHttp @@ -43,10 +43,9 @@ export const Experiments: React.FC = () => { const handleCloseDeleteExperimentModal: (confirm?: boolean) => void = useCallback((confirm?: boolean): void => { if (confirm) { + const ids: number[] = Object.keys(checkedRows).map((key: string) => parseInt(key)) post({ - data: { - ids: Object.keys(checkedRows).map((key: string) => parseInt(key)) - } + data: { ids } }); } setOpenDeleteModal(false); diff --git a/portal/src/app/components/all-experiments/__snapshots__/Experiments.test.tsx.snap b/portal/src/app/components/all-experiments/__snapshots__/Experiments.test.tsx.snap new file mode 100644 index 00000000..ac0e0190 --- /dev/null +++ b/portal/src/app/components/all-experiments/__snapshots__/Experiments.test.tsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Experiments renders correctly 1`] = ` +
    +
    +
    +
    + + + + + + +
    +
    +
    +
    +`; diff --git a/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts b/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts index 775bda54..ad54f078 100644 --- a/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts +++ b/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts @@ -4,49 +4,6 @@ import { AttSelectOption } from '../../../shared/components/att-select'; import { ExperimentData } from '../../all-experiments/hooks'; describe('useDuplicateData', () => { - // it('should set experiment name, algorithms, and iterations count when duplicate data is provided', () => { - // const setExperimentName = jest.fn(); - // const setAlgorithms = jest.fn(); - // const setIterationsCount = jest.fn(); - // const setDuplicateData = jest.fn(); - - // const duplicateData: ExperimentData = { - // id: 1111, - // name: 'test', - // algorithms: ['algorithm1', 'algorithm2'], - // iterations: [1, 2, 3], - // end_time: 1705240065192, - // }; - - // const { rerender } = renderHook((props: DuplicateData) => useDuplicateData(props), { - // initialProps: { - // data: undefined, - // setDuplicateData, - // setExperimentName, - // setAlgorithms, - // setIterationsCount, - // } as DuplicateData, - // }); - - // expect(setExperimentName).not.toHaveBeenCalled(); - // expect(setAlgorithms).not.toHaveBeenCalled(); - // expect(setIterationsCount).not.toHaveBeenCalled(); - // expect(setDuplicateData).not.toHaveBeenCalled(); - - // rerender({ - // data: duplicateData, - // setDuplicateData, - // setExperimentName, - // setAlgorithms, - // setIterationsCount, - // }); - - // expect(setExperimentName).toHaveBeenCalledWith(duplicateData.name); - // expect(setAlgorithms).toHaveBeenCalledWith(duplicateData.algorithms.map(algorithm => ({ label: algorithm, value: algorithm } as AttSelectOption))); - // expect(setIterationsCount).toHaveBeenCalledWith(duplicateData.iterations.map(iteration => ({ label: iteration.toString(), value: iteration.toString() } as AttSelectOption))); - // expect(setDuplicateData).toHaveBeenCalledWith(undefined); - // }); - it('should set experiment name, algorithms, and iterations count when duplicate data is provided', () => { const setExperimentName = jest.fn(); const setAlgorithms = jest.fn(); From 89df52ba7da2c88d18cb73e32113d00078bc4f42 Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Mon, 22 Jan 2024 17:16:26 +0200 Subject: [PATCH 10/15] remove irrelevant --- portal/src/app/components/all-experiments/Experiments.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/portal/src/app/components/all-experiments/Experiments.test.tsx b/portal/src/app/components/all-experiments/Experiments.test.tsx index 8999f49b..6106dd28 100644 --- a/portal/src/app/components/all-experiments/Experiments.test.tsx +++ b/portal/src/app/components/all-experiments/Experiments.test.tsx @@ -1,4 +1,4 @@ -import { render, fireEvent } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { Experiments } from './Experiments'; import { useExperimentsData } from './hooks'; import { FetchDataStatus, useFetch } from '../../shared/hooks/useFetch'; From 4d0eec5412ca585446962e5f48f517c101ed44bc Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Thu, 25 Jan 2024 14:22:17 +0200 Subject: [PATCH 11/15] after review --- .../all-experiments/Experiments.module.scss | 11 +++---- .../all-experiments/Experiments.tsx | 32 +++++++++++-------- .../hooks/useExperimentsData.test.ts | 5 +-- .../hooks/useExperimentsData.ts | 16 ++-------- .../models/experiments.interface.ts | 12 +++++++ .../parse-experiments-data.utils.test.ts | 2 +- .../utils/parse-experiments-data.utils.ts | 2 +- portal/src/app/components/home/Home.tsx | 4 +-- .../protocol-query/ProtocolQuery.tsx | 2 +- .../hooks/useDuplicateData.test.ts | 4 +-- .../protocol-query/hooks/useDuplicateData.ts | 2 +- 11 files changed, 48 insertions(+), 44 deletions(-) create mode 100644 portal/src/app/components/all-experiments/models/experiments.interface.ts diff --git a/portal/src/app/components/all-experiments/Experiments.module.scss b/portal/src/app/components/all-experiments/Experiments.module.scss index 7fc3fb5f..866d147f 100644 --- a/portal/src/app/components/all-experiments/Experiments.module.scss +++ b/portal/src/app/components/all-experiments/Experiments.module.scss @@ -1,10 +1,8 @@ @import "src/styles/variables-keys"; .experiments_wrapper { - padding-block-start: 40px; - padding-block-end: 40px; - padding-inline-start: 80px; - padding-inline-end: 80px; + padding-inline: 80px; + padding-block: 40px; } .title_options_container { @@ -48,8 +46,7 @@ } .experiments_table { - margin-block-start: 60px; - margin-block-end: 60px; + margin-block: 60px; th, td { text-align: left; @@ -86,7 +83,7 @@ inline-size: 100%; block-size: 100%; position: absolute; - background-color: #fff; + background-color: var($primaryWhite); opacity: 0.6; inset-block-start: 0; inset-inline-start: 0; diff --git a/portal/src/app/components/all-experiments/Experiments.tsx b/portal/src/app/components/all-experiments/Experiments.tsx index 67f76dd5..0d8c83e5 100644 --- a/portal/src/app/components/all-experiments/Experiments.tsx +++ b/portal/src/app/components/all-experiments/Experiments.tsx @@ -1,7 +1,7 @@ import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; import styles from './Experiments.module.scss'; import cn from 'classnames'; -import { ExperimentData, IUseExperimentsData, useExperimentsData } from './hooks'; +import { IUseExperimentsData, useExperimentsData } from './hooks'; import { FetchDataStatus, IHttp, useFetch } from '../../shared/hooks/useFetch'; import { Spinner, SpinnerSize } from '../../shared/components/att-spinner'; import { ALL_EXPERIMENTS_TABLE_EN } from './translate/en'; @@ -20,15 +20,18 @@ import TrashHoverSvg from '../../../assets/images/trash-hover.svg'; import DuplicateSvg from '../../../assets/images/duplicate.svg'; import { DeleteExperimentModal } from '../home/components/experiment/components/delete-experiment-modal'; import { parseExperimentsData } from './utils/parse-experiments-data.utils'; +import { ISpinner, useSpinnerContext } from '../../shared/context/spinner'; +import { ExperimentData } from './models/experiments.interface'; const DeleteAriaLabel: string = ALL_EXPERIMENTS_TABLE_EN.BUTTONS.DELETE; const DuplicateAriaLabel: string = ALL_EXPERIMENTS_TABLE_EN.TABLE_COLUMNS.LINKS.DUPLICATE; export const Experiments: React.FC = () => { - const { test_suites, status }: IUseExperimentsData = useExperimentsData(); + const { testSuites, status }: IUseExperimentsData = useExperimentsData(); const [openDeleteModal, setOpenDeleteModal] = useState(false); const [checkedRows, setCheckedRows] = useState>({}); - const experimentsData = useMemo(() => (parseExperimentsData(test_suites)), [test_suites]); + const experimentsData = useMemo(() => (testSuites ? parseExperimentsData(testSuites): []), [testSuites]); + const { isSpinnerOn }: ISpinner = useSpinnerContext(); const navigate = useNavigate(); const { post, status: deleteStatus, error: deleteError, cancelRequest: cancelRequestDelete }: IHttp @@ -142,10 +145,11 @@ export const Experiments: React.FC = () => { return (
    - {status === FetchDataStatus.Fetching ? renderSpinner() : ( - <> + <> + {isSpinnerOn && renderSpinner()} + { status === FetchDataStatus.Success &&
    - + {Object.values(checkedRows).some((value: boolean) => value) && ( )}
    - {experimentsData.length > 0 &&
    } - {openDeleteModal && } - - )} + } + {experimentsData.length > 0 &&
    } + {openDeleteModal && } + ); } function renderSpinner() { return ( -
    -
    - +
    +
    + +
    -
    ); } diff --git a/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts b/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts index cee957b7..9318385d 100644 --- a/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts +++ b/portal/src/app/components/all-experiments/hooks/useExperimentsData.test.ts @@ -1,6 +1,7 @@ import { renderHook } from '@testing-library/react'; import { useFetch } from '../../../shared/hooks/useFetch'; -import { Experiment, useExperimentsData } from './useExperimentsData'; +import { Experiment } from '../models/experiments.interface'; +import { useExperimentsData } from './useExperimentsData'; jest.mock('../../../shared/hooks/useFetch', () => ({ useFetch: jest.fn(), @@ -64,6 +65,6 @@ describe('useExperimentsData', () => { }); const { result } = renderHook(() => useExperimentsData()); - expect(result.current.test_suites.length).toEqual(allExperimentsMockData.length); + expect(result.current.testSuites.length).toEqual(allExperimentsMockData.length); }); }); \ No newline at end of file diff --git a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts index b10563e2..4e872e5e 100644 --- a/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts +++ b/portal/src/app/components/all-experiments/hooks/useExperimentsData.ts @@ -3,22 +3,12 @@ import { useEffect, useState } from 'react'; import { APIS } from '../../../apis'; import { useFetchSpinner } from '../../../shared/hooks/useFetchSpinner'; import { useErrorMessage } from '../../../hooks/useErrorMessage'; -import { ITestRunResult, ITestRunResultData } from '../../../shared/models/test-run-result.interface'; - -export type TestRunSubset = Pick; -export type Experiment = Pick & { test_runs: TestRunSubset[] }; +import { Experiment } from '../models/experiments.interface'; export interface IUseExperimentsData { - test_suites: Experiment[]; + testSuites: Experiment[]; status: FetchDataStatus; } -export interface ExperimentData { - id: number; - name: string; - algorithms: string[]; - iterations: number[]; - end_time: number; -}; export function useExperimentsData(): IUseExperimentsData { const [allExperiments, setAllExperiments] = useState([]); @@ -38,5 +28,5 @@ export function useExperimentsData(): IUseExperimentsData { } }, [data, status, allExperiments]); - return { test_suites: allExperiments, status }; + return { testSuites: allExperiments, status }; } diff --git a/portal/src/app/components/all-experiments/models/experiments.interface.ts b/portal/src/app/components/all-experiments/models/experiments.interface.ts new file mode 100644 index 00000000..ab9f8f73 --- /dev/null +++ b/portal/src/app/components/all-experiments/models/experiments.interface.ts @@ -0,0 +1,12 @@ +import { ITestRunResult, ITestRunResultData } from '../../../shared/models/test-run-result.interface'; + +export type TestRunSubset = Pick; +export type Experiment = Pick & { test_runs: TestRunSubset[] }; + +export interface ExperimentData { + id: number; + name: string; + algorithms: string[]; + iterations: number[]; + end_time: number; +}; diff --git a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts index 241ed2ff..6695e3da 100644 --- a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts +++ b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.test.ts @@ -1,6 +1,6 @@ import { parseExperimentsData } from './parse-experiments-data.utils'; import { ITestRunResultData } from '../../../shared/models/test-run-result.interface'; -import { Experiment, ExperimentData } from '../hooks'; +import { Experiment, ExperimentData } from '../models/experiments.interface'; describe('parseExperimentsData', () => { it('should parse experiments data correctly', () => { diff --git a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts index f1abd602..3ddfa6ac 100644 --- a/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts +++ b/portal/src/app/components/all-experiments/utils/parse-experiments-data.utils.ts @@ -1,4 +1,4 @@ -import { Experiment, ExperimentData, TestRunSubset } from '../hooks'; +import { Experiment, ExperimentData, TestRunSubset } from '../models/experiments.interface'; export function parseExperimentsData(test_suites: Experiment[]) { const experimentsData: ExperimentData[] = []; diff --git a/portal/src/app/components/home/Home.tsx b/portal/src/app/components/home/Home.tsx index d762aa59..e05f1b2b 100644 --- a/portal/src/app/components/home/Home.tsx +++ b/portal/src/app/components/home/Home.tsx @@ -6,7 +6,7 @@ import { SubHeader } from "../sub-header"; import { useCallback, useEffect, useState } from 'react'; import styles from './Home.module.scss'; import { useLocation, useNavigate } from "react-router-dom"; -import { ExperimentData } from "../all-experiments/hooks"; +import { ExperimentData } from "../all-experiments/models/experiments.interface"; export const Home: React.FC = () => { const [isSubHeaderOpen, setIsSubHeaderOpen] = useState(true); @@ -32,7 +32,7 @@ export const HomeContent: React.FC = () => { useEffect(() => { // Clear the state after the duplicate data has been created setDuplicateData(undefined); - }, [location]); + }, []); useEffect(() => { if (status === FetchDataStatus.Success && testSuiteId) { diff --git a/portal/src/app/components/protocol-query/ProtocolQuery.tsx b/portal/src/app/components/protocol-query/ProtocolQuery.tsx index f03a41cc..c4865ea5 100644 --- a/portal/src/app/components/protocol-query/ProtocolQuery.tsx +++ b/portal/src/app/components/protocol-query/ProtocolQuery.tsx @@ -10,7 +10,7 @@ import { Spinner, SpinnerSize } from '../../shared/components/att-spinner'; import { useGetAlgorithms, useGetIterations } from './hooks'; import { handleAlgorithmsSelection } from './utils'; import { AlgorithmsSelectorCustomOption, IterationsSelectorCustomOption } from '../../shared/components/selector-custom-option'; -import { ExperimentData } from '../all-experiments/hooks'; +import { ExperimentData } from '../all-experiments/models/experiments.interface'; import { useDuplicateData } from './hooks'; export type SelectOptionType = AttSelectOption | Options | null; diff --git a/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts b/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts index ad54f078..b1eefe96 100644 --- a/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts +++ b/portal/src/app/components/protocol-query/hooks/useDuplicateData.test.ts @@ -1,7 +1,7 @@ -import { renderHook, act } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; import { useDuplicateData, DuplicateData } from './useDuplicateData'; import { AttSelectOption } from '../../../shared/components/att-select'; -import { ExperimentData } from '../../all-experiments/hooks'; +import { ExperimentData } from '../../all-experiments/models/experiments.interface'; describe('useDuplicateData', () => { it('should set experiment name, algorithms, and iterations count when duplicate data is provided', () => { diff --git a/portal/src/app/components/protocol-query/hooks/useDuplicateData.ts b/portal/src/app/components/protocol-query/hooks/useDuplicateData.ts index d2e9b7c3..8cf59d88 100644 --- a/portal/src/app/components/protocol-query/hooks/useDuplicateData.ts +++ b/portal/src/app/components/protocol-query/hooks/useDuplicateData.ts @@ -1,6 +1,6 @@ import { useEffect } from 'react'; import { AttSelectOption } from '../../../shared/components/att-select'; -import { ExperimentData } from '../../all-experiments/hooks'; +import { ExperimentData } from '../../all-experiments/models/experiments.interface'; export type DuplicateData = { data: ExperimentData | undefined, From 9196a56403de68e04a85ecd713377e8ecc23cee0 Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Thu, 25 Jan 2024 14:45:18 +0200 Subject: [PATCH 12/15] table alignment --- .../components/all-experiments/Experiments.module.scss | 9 +++------ .../experiment-table/ExperimentTable.module.scss | 2 ++ .../src/app/shared/components/table/Table.module.scss | 10 +++------- portal/src/app/shared/components/table/Table.tsx | 2 +- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/portal/src/app/components/all-experiments/Experiments.module.scss b/portal/src/app/components/all-experiments/Experiments.module.scss index 866d147f..0a9e228d 100644 --- a/portal/src/app/components/all-experiments/Experiments.module.scss +++ b/portal/src/app/components/all-experiments/Experiments.module.scss @@ -3,6 +3,7 @@ .experiments_wrapper { padding-inline: 80px; padding-block: 40px; + margin-block: 60px; } .title_options_container { @@ -46,12 +47,8 @@ } .experiments_table { - margin-block: 60px; - - th, td { - text-align: left; - } - + text-align: left; + th:first-child, td:first-child { text-align: center; diff --git a/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.module.scss b/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.module.scss index ec5a99f8..f474ca8c 100644 --- a/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.module.scss +++ b/portal/src/app/components/home/components/experiment/components/experiment-table/ExperimentTable.module.scss @@ -9,6 +9,8 @@ } .experiment_table { + text-align: center; + th:first-child, td:first-child { inline-size: 80px; diff --git a/portal/src/app/shared/components/table/Table.module.scss b/portal/src/app/shared/components/table/Table.module.scss index 8a7154d4..670fff67 100644 --- a/portal/src/app/shared/components/table/Table.module.scss +++ b/portal/src/app/shared/components/table/Table.module.scss @@ -20,11 +20,7 @@ table { .table_content { background-color: var($backgroundColorWhite); - - td { - padding: 16px; - border-block-end: 1px solid var($backgroundColorGray); - text-align: center; - vertical-align: middle; - } + padding: 16px; + border-block-end: 1px solid var($backgroundColorGray); + vertical-align: middle; } diff --git a/portal/src/app/shared/components/table/Table.tsx b/portal/src/app/shared/components/table/Table.tsx index 29bf6d29..37386ed5 100644 --- a/portal/src/app/shared/components/table/Table.tsx +++ b/portal/src/app/shared/components/table/Table.tsx @@ -86,7 +86,7 @@ export const Table = ({ headers, data, className }: TableProps {table.getRowModel().rows.map((row: Row) => (
    {row.getVisibleCells().map((cell: Cell) => ( - ))} From 1a0194ecb71ca6869d377f2db91176bcc58d9dd6 Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Thu, 25 Jan 2024 14:47:52 +0200 Subject: [PATCH 13/15] Test fixes --- .../__snapshots__/Experiments.test.tsx.snap | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/portal/src/app/components/all-experiments/__snapshots__/Experiments.test.tsx.snap b/portal/src/app/components/all-experiments/__snapshots__/Experiments.test.tsx.snap index ac0e0190..8afcf59a 100644 --- a/portal/src/app/components/all-experiments/__snapshots__/Experiments.test.tsx.snap +++ b/portal/src/app/components/all-experiments/__snapshots__/Experiments.test.tsx.snap @@ -4,38 +4,6 @@ exports[`Experiments renders correctly 1`] = `
    -
    -
    - - - - - - -
    -
    -
    + />
    `; From 5af7570704b3f4d348e103eea8a6286677dcc65d Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Thu, 25 Jan 2024 15:43:00 +0200 Subject: [PATCH 14/15] Spinner + Style fixes --- .../all-experiments/Experiments.module.scss | 86 +++++++------------ .../all-experiments/Experiments.tsx | 14 --- .../experiment/Experiment.module.scss | 18 ---- .../home/components/experiment/Experiment.tsx | 19 +--- .../components/hooks/useExperimentData.ts | 6 +- portal/src/routes/Root.jsx | 15 ++++ portal/src/routes/Root.module.scss | 19 ++++ 7 files changed, 70 insertions(+), 107 deletions(-) create mode 100644 portal/src/routes/Root.module.scss diff --git a/portal/src/app/components/all-experiments/Experiments.module.scss b/portal/src/app/components/all-experiments/Experiments.module.scss index 0a9e228d..f60ea790 100644 --- a/portal/src/app/components/all-experiments/Experiments.module.scss +++ b/portal/src/app/components/all-experiments/Experiments.module.scss @@ -3,46 +3,43 @@ .experiments_wrapper { padding-inline: 80px; padding-block: 40px; - margin-block: 60px; -} -.title_options_container { - display: flex; - justify-content: space-between; - align-items: center; + .title_options_container { + display: flex; + justify-content: space-between; + align-items: center; - .experiments_title { - font-size: 20px; - font-family: var($fontMedium); - margin-inline-start: 150px; - } + .experiments_title { + font-size: 20px; + font-family: var($fontMedium); + margin-block-end: 40px; + } - .options_wrapper { - margin-inline-end: 50px; + .options_wrapper { + .trash_icon { + background-color: #F5F1FF; + inline-size: 34px; + block-size: 34px; + border-radius: 50%; + } + } - .trash_icon { - background-color: #F5F1FF; - inline-size: 34px; - block-size: 34px; - border-radius: 50%; + .options_wrapper:hover .hover_image { + display: block; + } + + .options_wrapper:hover .default_image { + display: none; + } + + .default_image { + padding-inline: 11px; + display: block; + } + + .hover_image { + display: none; } - } - - .options_wrapper:hover .hover_image { - display: block; - } - - .options_wrapper:hover .default_image { - display: none; - } - - .default_image { - padding-inline: 11px; - display: block; - } - - .hover_image { - display: none; } } @@ -68,22 +65,3 @@ cursor: pointer; } } - -.spinner_wrapper { - position: sticky; - inset-block-start: 50%; - inset-inline-start: 50%; - text-align: center; -} - -.spinner_overlay { - inline-size: 100%; - block-size: 100%; - position: absolute; - background-color: var($primaryWhite); - opacity: 0.6; - inset-block-start: 0; - inset-inline-start: 0; - z-index: 4; -} - diff --git a/portal/src/app/components/all-experiments/Experiments.tsx b/portal/src/app/components/all-experiments/Experiments.tsx index 0d8c83e5..024f3b52 100644 --- a/portal/src/app/components/all-experiments/Experiments.tsx +++ b/portal/src/app/components/all-experiments/Experiments.tsx @@ -3,7 +3,6 @@ import styles from './Experiments.module.scss'; import cn from 'classnames'; import { IUseExperimentsData, useExperimentsData } from './hooks'; import { FetchDataStatus, IHttp, useFetch } from '../../shared/hooks/useFetch'; -import { Spinner, SpinnerSize } from '../../shared/components/att-spinner'; import { ALL_EXPERIMENTS_TABLE_EN } from './translate/en'; import { CellContext } from '@tanstack/react-table'; import { Table } from '../../shared/components/table'; @@ -20,7 +19,6 @@ import TrashHoverSvg from '../../../assets/images/trash-hover.svg'; import DuplicateSvg from '../../../assets/images/duplicate.svg'; import { DeleteExperimentModal } from '../home/components/experiment/components/delete-experiment-modal'; import { parseExperimentsData } from './utils/parse-experiments-data.utils'; -import { ISpinner, useSpinnerContext } from '../../shared/context/spinner'; import { ExperimentData } from './models/experiments.interface'; const DeleteAriaLabel: string = ALL_EXPERIMENTS_TABLE_EN.BUTTONS.DELETE; @@ -31,7 +29,6 @@ export const Experiments: React.FC = () => { const [openDeleteModal, setOpenDeleteModal] = useState(false); const [checkedRows, setCheckedRows] = useState>({}); const experimentsData = useMemo(() => (testSuites ? parseExperimentsData(testSuites): []), [testSuites]); - const { isSpinnerOn }: ISpinner = useSpinnerContext(); const navigate = useNavigate(); const { post, status: deleteStatus, error: deleteError, cancelRequest: cancelRequestDelete }: IHttp @@ -146,7 +143,6 @@ export const Experiments: React.FC = () => { return (
    <> - {isSpinnerOn && renderSpinner()} { status === FetchDataStatus.Success &&
    @@ -172,13 +168,3 @@ export const Experiments: React.FC = () => {
    ); } - -function renderSpinner() { - return ( -
    -
    - -
    -
    - ); -} diff --git a/portal/src/app/components/home/components/experiment/Experiment.module.scss b/portal/src/app/components/home/components/experiment/Experiment.module.scss index 28406f9f..8fd1714a 100644 --- a/portal/src/app/components/home/components/experiment/Experiment.module.scss +++ b/portal/src/app/components/home/components/experiment/Experiment.module.scss @@ -19,21 +19,3 @@ .table_options_wrapper { position: relative; } - -.spinner_wrapper { - position: sticky; - inset-block-start: 50%; - inset-inline-start: 50%; - text-align: center; -} - -.spinner_overlay { - inline-size: 100%; - block-size: 100%; - position: absolute; - background-color: #fff; - opacity: 0.6; - inset-block-start: 0; - inset-inline-start: 0; - z-index: 4; -} diff --git a/portal/src/app/components/home/components/experiment/Experiment.tsx b/portal/src/app/components/home/components/experiment/Experiment.tsx index b9d36347..24b1b6f8 100644 --- a/portal/src/app/components/home/components/experiment/Experiment.tsx +++ b/portal/src/app/components/home/components/experiment/Experiment.tsx @@ -4,13 +4,10 @@ import { Charts } from './components/charts'; import { SubHeader } from './components/sub-header'; import { useExperimentData } from './components/hooks/useExperimentData'; import { ITestRunResult } from '../../../../shared/models/test-run-result.interface'; -import { FetchDataStatus } from '../../../../shared/hooks/useFetch'; -import { Spinner, SpinnerSize } from '../../../../shared/components/att-spinner'; import { useEffect, useRef, useState } from 'react'; import { EXPERIMENT_EN } from './translate/en'; import { ExperimentTabs } from './components/experiment-tabs'; import { handleSectionScrolling } from './utils'; -import { ISpinner, useSpinnerContext } from '../../../../shared/context/spinner'; import { TableOptions } from './components/table-options'; import { SelectColumnsPopup } from './components/table-options/components/select-columns-popup'; import { SelectedColumnsDefaultData, TableOptionsData } from './components/table-options/constants/table-options.const'; @@ -22,11 +19,11 @@ export type IExperimentData = { } export const Experiment: React.FC = () => { - const { data: testRunData, status } = useExperimentData(); + const { data: testRunData } = useExperimentData(); return (
    - {status === FetchDataStatus.Fetching ? renderSpinner() : testRunData && } + {testRunData && }
    ); } @@ -36,7 +33,6 @@ export const ExperimentContent: React.FC = (props: IExperimentD const [currentSection, setCurrentSection] = useState(EXPERIMENT_EN.TABS.RESULTS_DATA); const [selectedColumns, setSelectedColumns] = useState(SelectedColumnsDefaultData); - const { isSpinnerOn }: ISpinner = useSpinnerContext(); const resultsDataRef = useRef(null); const visualizationRef = useRef(null); const tableOptionsRef = useRef(null); @@ -71,7 +67,6 @@ export const ExperimentContent: React.FC = (props: IExperimentD return ( <> - {isSpinnerOn && renderSpinner()}
    @@ -99,13 +94,3 @@ export const ExperimentContent: React.FC = (props: IExperimentD ); } - -function renderSpinner() { - return ( -
    -
    - -
    -
    - ); -} diff --git a/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.ts b/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.ts index d1c0bf57..1c0898a7 100644 --- a/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.ts +++ b/portal/src/app/components/home/components/experiment/components/hooks/useExperimentData.ts @@ -4,14 +4,13 @@ import { replaceParams } from "../../../../../../shared/utils/replaceParams"; import { useParams } from "react-router-dom"; import { ITestRunResult, ITestRunResultData } from "../../../../../../shared/models/test-run-result.interface"; import { TestRunUrlParams } from "../../../../../../shared/models/url-params.interface"; -import { FetchDataStatus, IHttp, useFetch } from "../../../../../../shared/hooks/useFetch"; +import { IHttp, useFetch } from "../../../../../../shared/hooks/useFetch"; import { sortDataByAlgorithm } from "../charts/utils/test-run.utils"; import { useFetchSpinner } from "../../../../../../shared/hooks/useFetchSpinner"; import { useErrorMessage } from "../../../../../../hooks/useErrorMessage"; export interface IUseExperimentData { data: ITestRunResult; - status: FetchDataStatus; } export function useExperimentData(): IUseExperimentData { @@ -36,7 +35,6 @@ export function useExperimentData(): IUseExperimentData { }, [data]); return { - data: testRunData, - status, + data: testRunData } as IUseExperimentData; } diff --git a/portal/src/routes/Root.jsx b/portal/src/routes/Root.jsx index a4964046..c6bf76ca 100644 --- a/portal/src/routes/Root.jsx +++ b/portal/src/routes/Root.jsx @@ -1,12 +1,27 @@ +import styles from './Root.module.scss'; import { GlobalHeader } from '../app/shared/components/global-header/index'; import { Outlet } from 'react-router-dom'; import { tabs } from '../app/shared/constants/navigation-tabs.const'; +import { Spinner, SpinnerSize } from '../app/shared/components/att-spinner'; +import { useSpinnerContext } from '../app/shared/context/spinner'; export default function Root() { + const { isSpinnerOn } = useSpinnerContext(); return ( <> + {isSpinnerOn && renderSpinner()} ); } + +function renderSpinner() { + return ( +
    +
    + +
    +
    + ); +} diff --git a/portal/src/routes/Root.module.scss b/portal/src/routes/Root.module.scss new file mode 100644 index 00000000..a08a79d2 --- /dev/null +++ b/portal/src/routes/Root.module.scss @@ -0,0 +1,19 @@ +@import "src/styles/variables-keys"; + +.spinner_wrapper { + position: sticky; + inset-block-start: 50%; + inset-inline-start: 50%; + text-align: center; +} + +.spinner_overlay { + inline-size: 100%; + block-size: 100%; + position: absolute; + background-color: var($primaryWhite); + opacity: 0.6; + inset-block-start: 0; + inset-inline-start: 0; + z-index: 4; +} From bc92a2753e29c54508796ff0291fa71ae904708d Mon Sep 17 00:00:00 2001 From: Ohad Koren Date: Wed, 31 Jan 2024 11:12:40 +0200 Subject: [PATCH 15/15] text fix --- portal/src/app/components/sub-header/SubHeader.test.tsx | 2 +- .../components/sub-header/__snapshots__/SubHeader.test.tsx.snap | 2 +- portal/src/app/components/sub-header/translate/en.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/portal/src/app/components/sub-header/SubHeader.test.tsx b/portal/src/app/components/sub-header/SubHeader.test.tsx index 5ee886f9..08570f2b 100644 --- a/portal/src/app/components/sub-header/SubHeader.test.tsx +++ b/portal/src/app/components/sub-header/SubHeader.test.tsx @@ -10,6 +10,6 @@ describe('SubHeader', () => { (Button as jest.Mock).mockImplementation(() =>
    Button
    ); const { container, getByText }: RenderResult = render(, { wrapper: MemoryRouter }); expect(container.firstChild).toMatchSnapshot(); - expect(getByText('That’s what are we doing on each iteration:')).toBeTruthy(); + expect(getByText('That’s what we are doing on each iteration:')).toBeTruthy(); }); }); \ No newline at end of file diff --git a/portal/src/app/components/sub-header/__snapshots__/SubHeader.test.tsx.snap b/portal/src/app/components/sub-header/__snapshots__/SubHeader.test.tsx.snap index f1d10960..23777f36 100644 --- a/portal/src/app/components/sub-header/__snapshots__/SubHeader.test.tsx.snap +++ b/portal/src/app/components/sub-header/__snapshots__/SubHeader.test.tsx.snap @@ -10,7 +10,7 @@ exports[`SubHeader renders sub header 1`] = `
    - That’s what are we doing on each iteration: + That’s what we are doing on each iteration:
    + {flexRender(cell.column.columnDef.cell, cell.getContext())}