From 26183f233977efc54030b7a8037564c351064e64 Mon Sep 17 00:00:00 2001 From: joon-at-sri Date: Tue, 16 Jul 2024 10:22:12 -0700 Subject: [PATCH 1/4] tests wip --- jest.config.js | 2 +- package-lock.json | 11 + package.json | 1 + .../Details/SavedQueriesComponent.tsx | 2 +- src/components/Details/SettingsComponent.tsx | 8 +- .../ModalDialog/ModalDialogComponent.tsx | 1 + src/logics/actionHelper.ts | 3 + src/logics/utils.ts | 6 +- src/reducers/dialogReducer.ts | 1 + src/reducers/graphReducer.ts | 1 + .../Details/DetailsComponent.test.tsx | 163 +++++--- .../Details/QueryComponent.test.tsx | 66 +++- .../Details/SavedQueriesComponent.test.tsx | 115 ++++++ .../Details/SettingsComponent.test.tsx | 298 ++++++++++++++ .../Details/TableComponent.test.tsx | 364 ++++++++++++++++++ .../ModalDialog/ModalDialogComponent.test.tsx | 199 ++++++++++ test/components/logics/utils.test.ts | 107 +++++ 17 files changed, 1275 insertions(+), 73 deletions(-) create mode 100644 test/components/Details/SavedQueriesComponent.test.tsx create mode 100644 test/components/Details/SettingsComponent.test.tsx create mode 100644 test/components/Details/TableComponent.test.tsx create mode 100644 test/components/ModalDialog/ModalDialogComponent.test.tsx create mode 100644 test/components/logics/utils.test.ts diff --git a/jest.config.js b/jest.config.js index 29078fa..b73d5e7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -142,7 +142,7 @@ const config = { // runner: "jest-runner", // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: ["jest-webgl-canvas-mock"], + setupFiles: ["jest-canvas-mock"], // A list of paths to modules that run some code to configure or set up the testing framework before each test // setupFilesAfterEnv: ["jest-webgl-canvas-mock"], diff --git a/package-lock.json b/package-lock.json index 6115681..db15db5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,7 @@ "identity-obj-proxy": "^3.0.0", "jest": "^28.1.3", "jest-axe": "^9.0.0", + "jest-canvas-mock": "^2.5.2", "jest-environment-jsdom": "^29.7.0", "jest-puppeteer": "^10.0.1", "jest-webgl-canvas-mock": "^2.5.3", @@ -15063,6 +15064,16 @@ "node": ">=8" } }, + "node_modules/jest-canvas-mock": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.5.2.tgz", + "integrity": "sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==", + "dev": true, + "dependencies": { + "cssfontparser": "^1.2.1", + "moo-color": "^1.0.2" + } + }, "node_modules/jest-changed-files": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", diff --git a/package.json b/package.json index 838ea1a..5d2fa33 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "identity-obj-proxy": "^3.0.0", "jest": "^28.1.3", "jest-axe": "^9.0.0", + "jest-canvas-mock": "^2.5.2", "jest-environment-jsdom": "^29.7.0", "jest-puppeteer": "^10.0.1", "jest-webgl-canvas-mock": "^2.5.3", diff --git a/src/components/Details/SavedQueriesComponent.tsx b/src/components/Details/SavedQueriesComponent.tsx index 10b9bc8..f0516de 100644 --- a/src/components/Details/SavedQueriesComponent.tsx +++ b/src/components/Details/SavedQueriesComponent.tsx @@ -61,7 +61,7 @@ const SavedQueries = ({ }) => { handleListItemClick(event, ndx)}> - handlePlay(ndx)} edge="end" size="large" sx={{ color: 'rgb(30, 144, 255)' }}> + handlePlay(ndx)} edge="end" size="large" aria-label={`Play-${ndx}`}sx={{ color: 'rgb(30, 144, 255)' }}> diff --git a/src/components/Details/SettingsComponent.tsx b/src/components/Details/SettingsComponent.tsx index cec2cd7..d27d0dd 100644 --- a/src/components/Details/SettingsComponent.tsx +++ b/src/components/Details/SettingsComponent.tsx @@ -110,6 +110,7 @@ const NodeLabelList = ({ nodeLabels }: NodeLabelListProps) => { {...params} id="standard-basic" label="Label Field" + data-testid={`label-field-${ndx}`} InputLabelProps={{ shrink: true }} onChange={(event) => { const field = event.target.value; @@ -243,7 +244,7 @@ export const Settings = () => { onHostChanged(event.target.value)} - id="standard-basic" + id="host-field" label="host" style={{ width: '100%' }} variant="standard" @@ -251,8 +252,9 @@ export const Settings = () => { onPortChanged(event.target.value)} - id="standard-basic" + id="port-field" label="port" + data-testid = "port-label" style={{ width: '100%' }} variant="standard" /> @@ -319,6 +321,7 @@ export const Settings = () => { @@ -368,6 +371,7 @@ export const Settings = () => { { { headers: { 'Content-Type': 'application/json' } } ) .then((response) => { + console.log(JSON.stringify(response, null, 2)); const addedElement = isNodeDialog ? [{ ...response.data[0], x, y }] : response.data manualAddElement(isNodeDialog, addedElement, nodeLabels, dispatch); }) diff --git a/src/logics/actionHelper.ts b/src/logics/actionHelper.ts index faf555e..57e282c 100644 --- a/src/logics/actionHelper.ts +++ b/src/logics/actionHelper.ts @@ -5,10 +5,13 @@ import store, { AppDispatch } from '../app/store'; import { IdType } from "vis-network"; export const onFetchQuery = (result: any, query: string, oldNodeLabels: NodeLabel[], dispatch: AppDispatch) => { + console.log(JSON.stringify(result.data, null, 2)); + console.log(JSON.stringify(oldNodeLabels, null, 2)); const { nodes, edges, nodeLabels } = extractEdgesAndNodes( result.data, oldNodeLabels ); + dispatch(addNodes(nodes)); dispatch(addEdges(edges)); dispatch(setNodeLabels(nodeLabels)); diff --git a/src/logics/utils.ts b/src/logics/utils.ts index 238e880..a446299 100644 --- a/src/logics/utils.ts +++ b/src/logics/utils.ts @@ -68,7 +68,8 @@ export interface TempFieldSuggestions { } } -const storeSuggestions = (nodes: Array, edges: Array) => { +export const storeSuggestions = (nodes: Array, edges: Array) => { + console.log("storeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") const suggestions: Suggestions = { [DIALOG_TYPES.NODE]: { types: [], labels: {} }, [DIALOG_TYPES.EDGE]: { types: [], labels: {} } @@ -139,6 +140,9 @@ export const extractEdgesAndNodes = (nodeList: Array, oldNodeLabels: N } }); storeSuggestions(nodes as NodeData[], edges as EdgeData[]); + console.log(JSON.stringify(nodes, null, 2)); + console.log(JSON.stringify(edges, null, 2)); + console.log(JSON.stringify(nodeLabels, null, 2)); return { edges, nodes, nodeLabels }; }; diff --git a/src/reducers/dialogReducer.ts b/src/reducers/dialogReducer.ts index 57ec0d3..dd48136 100644 --- a/src/reducers/dialogReducer.ts +++ b/src/reducers/dialogReducer.ts @@ -57,6 +57,7 @@ const slice = createSlice({ state.dialogType = action.payload; }, setSuggestions: (state, action) => { + console.log("setSuggestions"); const suggestions = action.payload as Suggestions; Object.entries(suggestions).forEach(([dialogType, elementSuggestions]) => { diff --git a/src/reducers/graphReducer.ts b/src/reducers/graphReducer.ts index 5e35e34..0b073b1 100644 --- a/src/reducers/graphReducer.ts +++ b/src/reducers/graphReducer.ts @@ -43,6 +43,7 @@ const slice = createSlice({ return state; }, addNodes: (state, action) => { + console.log('addNodes'); state = Object.assign({}, state); const newNodes = _.differenceBy(action.payload, state.nodes, (node: any) => node.id); state.nodes = [...state.nodes, ...newNodes]; diff --git a/test/components/Details/DetailsComponent.test.tsx b/test/components/Details/DetailsComponent.test.tsx index 0936828..5f84abf 100644 --- a/test/components/Details/DetailsComponent.test.tsx +++ b/test/components/Details/DetailsComponent.test.tsx @@ -8,7 +8,7 @@ import userEvent from '@testing-library/user-event'; import { defaultNodeLabel, EdgeData, NodeData } from "../../../src/logics/utils"; import { setupStore } from "../../../src/app/store"; import axios from 'axios'; -import { QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; +import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; import { Store, AnyAction } from 'redux'; import { updateNode } from '../../../src/reducers/graphReducer'; import { onFetchQuery } from '../../../src/logics/actionHelper'; @@ -16,7 +16,7 @@ jest.mock('../../../src/logics/graph', () => ({ applyLayout: jest.fn(), getNodePositions: jest.fn(), setNodePositions: jest.fn(), - layoutOptions: ['random', 'hierarchical'] + layoutOptions: ['random', 'hierarchical'] })); jest.mock("axios", () => ({ @@ -76,7 +76,6 @@ const initialState: State = { describe('node tests', () => { - test('renders node details correctly', async () => { const mockStore = configureStore(); let store = mockStore(initialState); @@ -91,13 +90,13 @@ describe('node tests', () => { fireEvent.click(detailsTab); }) expect(screen.getByText('Information: Node')).toBeInTheDocument(); - expect(screen.getByText('Bob')).toBeInTheDocument(); - expect(screen.getByText('21')).toBeInTheDocument(); + expect(screen.getByText('Bob')).toBeInTheDocument(); + expect(screen.getByText('21')).toBeInTheDocument(); }); - test("sends correct axios post when deleting a node property", async () => { + test("sends axios post to delete a node property", async () => { let user = userEvent.setup(); const mockStore = configureStore(); let store = mockStore(initialState); @@ -117,22 +116,23 @@ describe('node tests', () => { const drop_query = `g.V('1').properties("name").drop()`; await waitFor(() => { - expect(axios.post).toHaveBeenCalledTimes(2); - expect(axios.post).toHaveBeenNthCalledWith(1, + expect(mockedAxios.post).toHaveBeenCalledWith( QUERY_RAW_ENDPOINT, - { + expect.objectContaining({ host: initialState.gremlin.host, port: initialState.gremlin.port, query: drop_query, nodeLimit: initialState.options.nodeLimit - }, - { headers: { 'Content-Type': 'application/json' } } + }), + expect.objectContaining({ + headers: { 'Content-Type': 'application/json' } + }) ); }); }) - test("onTraverse out triggers 4 dispatches in OnFetchQuery", async () => { + test(`clicking "Traverse Out Edges" sends addNodes`, async () => { let user = userEvent.setup(); const mockStore = configureStore(); let store = mockStore(initialState); @@ -151,29 +151,16 @@ describe('node tests', () => { const button = screen.getByRole('button', { name: /Traverse Out Edges/i }); await user.click(button); await waitFor(() => { - expect(store.dispatch).toHaveBeenCalledTimes(4); expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ type: 'graph/addNodes', - payload: expect.anything() - })); - expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ - type: 'graph/addEdges', - payload: expect.anything() - })); - expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ - type: 'options/setNodeLabels', - payload: expect.anything() - })); - expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ - type: 'options/addQueryHistory', - payload: expect.anything() + payload: expect.anything() })); }); }) - test("onTraverse In triggers triggers 4 dispatches in onFetchQuery", async () => { + test(`clicking "Traverse In Edges" sends addNodes`, async () => { let user = userEvent.setup(); const mockStore = configureStore(); let store = mockStore(initialState); @@ -195,29 +182,16 @@ describe('node tests', () => { const button = screen.getByRole('button', { name: /Traverse In Edges/i }); await user.click(button); await waitFor(() => { - expect(store.dispatch).toHaveBeenCalledTimes(4); expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ type: 'graph/addNodes', - payload: expect.anything() - })); - expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ - type: 'graph/addEdges', - payload: expect.anything() - })); - expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ - type: 'options/setNodeLabels', - payload: expect.anything() - })); - expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ - type: 'options/addQueryHistory', - payload: expect.anything() + payload: expect.anything() })); }); }) - test('clicking add property and confirming calls axios post with right arguments', async () => { + test('node clicking add property and confirming calls axios post with right arguments', async () => { let user = userEvent.setup(); const mockStore = configureStore(); let store = mockStore(initialState); @@ -248,19 +222,20 @@ describe('node tests', () => { const expected_query = "g.V('1').property(\"height\", \"170\")" await waitFor(() => { - expect(axios.post).toHaveBeenCalledTimes(1); - expect(axios.post).toHaveBeenNthCalledWith(1, + expect(mockedAxios.post).toHaveBeenCalledWith( QUERY_ENDPOINT, - { + expect.objectContaining({ host: initialState.gremlin.host, port: initialState.gremlin.port, query: expected_query, nodeLimit: initialState.options.nodeLimit - }, - { headers: { 'Content-Type': 'application/json' } } + }), + expect.objectContaining({ + headers: { 'Content-Type': 'application/json' } + }) ); }); - + }); @@ -289,7 +264,95 @@ describe("edge tests", () => { }) expect(screen.getByText('Information: Edge')).toBeInTheDocument(); - expect(screen.getByText('created')).toBeInTheDocument(); - expect(screen.getByText('0')).toBeInTheDocument(); + expect(screen.getByText('created')).toBeInTheDocument(); + expect(screen.getByText('0')).toBeInTheDocument(); }); + + test("sends axios post to delete an edge property", async () => { + let user = userEvent.setup(); + const mockStore = configureStore(); + let store = mockStore({ ...initialState, graph: { selectedNode: null, selectedEdge: selectedEdgeDummy } }); + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + + render( + + { }} /> + + ); + + const detailsTab = screen.getByRole('tab', { name: 'Details' }); + await user.click(detailsTab); + const editText = screen.findByTestId("deleteButton-age"); + await user.click(await editText); + + const drop_query = `g.E(1${EDGE_ID_APPEND}).properties("age").drop()`; + + await waitFor(() => { + expect(mockedAxios.post).toHaveBeenCalledWith( + QUERY_RAW_ENDPOINT, + expect.objectContaining({ + host: initialState.gremlin.host, + port: initialState.gremlin.port, + query: drop_query, + nodeLimit: initialState.options.nodeLimit + }), + expect.objectContaining({ + headers: { 'Content-Type': 'application/json' } + }) + ); + }); + }) + + + + test('edge clicking add property and confirming calls axios post with right arguments', async () => { + let user = userEvent.setup(); + const mockStore = configureStore(); + let store = mockStore({ ...initialState, graph: { selectedNode: null, selectedEdge: selectedEdgeDummy } }); + store.dispatch = jest.fn(); + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + render( + + { }} /> + + ); + const detailsTab = screen.getByRole('tab', { name: 'Details' }); + await act(async () => { + fireEvent.click(detailsTab); + }) + + const button = screen.getByRole('button', { name: /Add Property/i }); + await user.click(button); + + const propertyNameInput = screen.getByRole('textbox', { name: 'Property Name' }); + const propertyValueInput = screen.getByRole('textbox', { name: 'Property Value' }); + + await user.type(propertyNameInput, 'height'); + await user.type(propertyValueInput, '170'); + + const submitButton = screen.getByRole('button', { name: 'Add' }); + await user.click(submitButton); + + const expected_query = `g.E(1${EDGE_ID_APPEND}).property(\"height\", \"170\")` + await waitFor(() => { + expect(mockedAxios.post).toHaveBeenCalledWith( + QUERY_RAW_ENDPOINT, + expect.objectContaining({ + host: initialState.gremlin.host, + port: initialState.gremlin.port, + query: expected_query, + nodeLimit: initialState.options.nodeLimit + }), + expect.objectContaining({ + headers: { 'Content-Type': 'application/json' } + }) + ); + }); + + + + }); + }) diff --git a/test/components/Details/QueryComponent.test.tsx b/test/components/Details/QueryComponent.test.tsx index 7b2a5ab..d70afb2 100644 --- a/test/components/Details/QueryComponent.test.tsx +++ b/test/components/Details/QueryComponent.test.tsx @@ -68,44 +68,43 @@ test('queryComponent renders with g.V()', () => { expect(gremlin_query_text).toEqual("g.V()"); }); -test('graph renders adding 1 node', async () => { +test('adding one node query sends axios post and dispatches addNode', async () => { + let user = userEvent.setup(); const mockedAxios = axios as jest.Mocked; mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); const mockStore = configureStore(); let store = mockStore(initialState); store.dispatch = jest.fn(); + jest.spyOn(store, 'dispatch'); render( ); - + const textField = screen.getByLabelText('gremlin query'); + const newQuery = 'g.addV("person").property("name", "Alice")'; + await user.clear(textField); + await user.type(textField, newQuery); const button = screen.getByText('Execute'); await userEvent.click(button); await waitFor(() => { expect(axios.post).toHaveBeenCalledTimes(1); - expect(axios.post).toHaveBeenCalledWith( - QUERY_ENDPOINT, - { - host: initialState.gremlin.host, - port: initialState.gremlin.port, - query: initialState.gremlin.query, - nodeLimit: initialState.options.nodeLimit - }, - { headers: { 'Content-Type': 'application/json' } } - ); }); await waitFor(() => { - expect(store.dispatch).toHaveBeenCalled(); + expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ + type: 'graph/addNodes', + payload: expect.anything() + })); }); }); + test('executed query is added into query history list', async () => { // const mockStore = configureStore(); // let store = mockStore(initialState); @@ -132,13 +131,8 @@ test('executed query is added into query history list', async () => { // }) await user.clear(textField); await user.type(textField, newQuery); - const button = screen.getByText('Execute'); await userEvent.click(button); - - - - // expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ // type: 'gremlin/setQuery', // payload: expect.anything() @@ -148,7 +142,43 @@ test('executed query is added into query history list', async () => { const matchingElements = screen.getAllByText(newQuery); expect(matchingElements.length).toBe(2); }); +}); + +test('execute then clear', async () => { + // const mockStore = configureStore(); + // let store = mockStore(initialState); + // store.dispatch = jest.fn(); + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + let user = userEvent.setup(); + let store = setupStore(); + // jest.spyOn(store, 'dispatch'); + render( + + { }} /> + + ); + // const detailsTab = screen.getByRole('tab', { name: 'Details' }); + // await act(async () => { + // fireEvent.click(detailsTab); + // }) + + + const button = screen.getByText('Execute'); + await userEvent.click(button); + + // expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ + // type: 'gremlin/setQuery', + // payload: expect.anything() + // })); + const clearButton = screen.getByText('Clear Graph'); + await userEvent.click(clearButton); + await waitFor(() => { + const matchingElements = screen.getAllByText("g.V()"); + expect(matchingElements.length).toBe(1); + }); + }); \ No newline at end of file diff --git a/test/components/Details/SavedQueriesComponent.test.tsx b/test/components/Details/SavedQueriesComponent.test.tsx new file mode 100644 index 0000000..5a045c4 --- /dev/null +++ b/test/components/Details/SavedQueriesComponent.test.tsx @@ -0,0 +1,115 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import { render, screen, waitFor, fireEvent, act } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { SidebarComponent } from '../../../src/components/Details/SidebarComponent'; +import userEvent from '@testing-library/user-event'; +import { defaultNodeLabel, EdgeData, NodeData } from "../../../src/logics/utils"; +import { setupStore } from "../../../src/app/store"; +import axios from 'axios'; +import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; +import { Store, AnyAction } from 'redux'; +import { updateNode } from '../../../src/reducers/graphReducer'; +import { onFetchQuery } from '../../../src/logics/actionHelper'; + +jest.mock('../../../src/logics/graph', () => ({ + applyLayout: jest.fn(), + getNodePositions: jest.fn(), + setNodePositions: jest.fn(), + layoutOptions: ['random', 'hierarchical'] +})); + +jest.mock("axios", () => ({ + ...jest.requireActual("axios"), + post: jest.fn(), +})); + +const customQueries = { + "get node with name marko" : "g.V().has('name', 'marko')", + "get person nodes that marko has outgoing edges to" : "g.V().has('name', 'marko').out().hasLabel('person')" + } +// Replace SAVED_QUERIES import in SavedQueries component with customQueries for testing +jest.mock('../../../src/constants', () => ({ + SAVED_QUERIES: customQueries, + INITIAL_LABEL_MAPPINGS: [] +})); + +type State = { + gremlin: { + host: string; + port: string; + query: string; + }; + options: { + nodeLabels: string[]; + nodeLimit: number; + queryHistory: string[]; + }; + graph: { + selectedNode: NodeData | null; + selectedEdge: EdgeData | null; + nodes: NodeData[], + edges: NodeData[], + }; + + +}; + +const initialState: State = { + gremlin: { + host: 'localhost', + port: '8182', + query: 'g.V()' + }, + options: { + nodeLabels: [], + nodeLimit: 50, + queryHistory: [] + }, + + graph: { + selectedNode: null, + selectedEdge: null, + nodes: [], + edges: [], + } +}; + + +test("clicking play button for first saved query sends axios", async () => { + let user = userEvent.setup(); + const mockStore = configureStore(); + let store = mockStore(initialState); + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + + render( + + { }} /> + + ); + + const savedQueriesTab = screen.getByRole('tab', { name: 'Saved Queries' }); + await user.click(savedQueriesTab); + const firstQueryButton = screen.getByRole('button', { name: "Play-0" }); + expect(firstQueryButton).toBeInTheDocument(); + + await user.click(firstQueryButton); + + const query = "g.V().has('name', 'marko')"; + await waitFor(() => { + expect(mockedAxios.post).toHaveBeenCalledWith( + QUERY_ENDPOINT, + expect.objectContaining({ + host: initialState.gremlin.host, + port: initialState.gremlin.port, + query: query, + nodeLimit: initialState.options.nodeLimit + }), + expect.objectContaining({ + headers: { 'Content-Type': 'application/json' } + }) + ); + }); +}) \ No newline at end of file diff --git a/test/components/Details/SettingsComponent.test.tsx b/test/components/Details/SettingsComponent.test.tsx new file mode 100644 index 0000000..f6b9331 --- /dev/null +++ b/test/components/Details/SettingsComponent.test.tsx @@ -0,0 +1,298 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import { render, screen, waitFor, fireEvent, act, within } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { SidebarComponent } from '../../../src/components/Details/SidebarComponent'; +import userEvent from '@testing-library/user-event'; +import { defaultNodeLabel, EdgeData, NodeData } from "../../../src/logics/utils"; +import { setupStore } from "../../../src/app/store"; +import axios from 'axios'; +import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; +import { setNodePositions } from '../../../src/logics/graph'; + + +jest.mock('../../../src/logics/graph', () => ({ + applyLayout: jest.fn(), + getNodePositions: jest.fn(), + setNodePositions: jest.fn(), + layoutOptions: ['force-directed', 'hierarchical'] +})); + +jest.mock("axios", () => ({ + ...jest.requireActual("axios"), + post: jest.fn(), +})); + +const customQueries = { + "get node with name marko": "g.V().has('name', 'marko')", + "get person nodes that marko has outgoing edges to": "g.V().has('name', 'marko').out().hasLabel('person')" +} +// Replace SAVED_QUERIES import in SavedQueries component with customQueries for testing +jest.mock('../../../src/constants', () => ({ + SAVED_QUERIES: customQueries, + INITIAL_LABEL_MAPPINGS: { + person: 'name' + }, + GRAPH_IMPL: "vis" + +})); + +type State = { + gremlin: { + host: string; + port: string; + query: string; + }; + options: { + nodeLabels: string[]; + nodeLimit: number; + queryHistory: string[]; + }; + graph: { + selectedNode: NodeData | null; + selectedEdge: EdgeData | null; + nodes: NodeData[], + edges: NodeData[], + }; + + +}; + +const initialState: State = { + gremlin: { + host: 'localhost', + port: '8182', + query: 'g.V()' + }, + options: { + nodeLabels: [], + nodeLimit: 50, + queryHistory: [] + }, + + graph: { + selectedNode: null, + selectedEdge: null, + nodes: [], + edges: [], + } +}; + + +test("refreshing a node label sends update/refresheNodeLabels dispatch", async () => { + let user = userEvent.setup(); + const mockStore = configureStore(); + let store = setupStore(); + const mockedAxios = axios as jest.Mocked; + store.dispatch = jest.fn(); + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + + render( + + { }} /> + + ); + + + const settingsTab = screen.getByRole('tab', { name: 'Settings' }); + await user.click(settingsTab); + + // expect(screen.getByText('name')).toBeInTheDocument(); + + + const labelTextField = screen.getByTestId(`label-field-0`); + + expect(labelTextField).toBeInTheDocument(); + + + + await user.click(labelTextField); + + // await user.clear(labelTextField) + await user.type(labelTextField, '{backspace}{backspace}{backspace}{backspace}'); + await user.type(labelTextField, 'age'); + + const button = screen.getByRole('button', { name: /Refresh/i }); + await user.click(button); + + + await waitFor(() => { + expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ + type: 'options/editNodeLabel', + payload: expect.anything() + })); + expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ + type: 'graph/refreshNodeLabels', + payload: expect.anything() + })); + + + }) + +}); + + + + + + +test("save workspace as 'saved workspace' and confirm it appears as one of the options in load workspace", async () => { + let user = userEvent.setup(); + const mockStore = configureStore(); + let store = setupStore(); + const mockedAxios = axios as jest.Mocked; + // store.dispatch = jest.fn(); + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + jest.spyOn(store, 'dispatch'); + + render( + + { }} /> + + ); + + + const settingsTab = screen.getByRole('tab', { name: 'Settings' }); + await user.click(settingsTab); + + // expect(screen.getByText('name')).toBeInTheDocument(); + + + const labelTextField = screen.getByTestId(`label-field-0`); + + expect(labelTextField).toBeInTheDocument(); + + + + await user.click(labelTextField); + + + + const saveWorkspaceButton = screen.getByRole('button', { name: /Save Workspace/i }); + await user.click(saveWorkspaceButton); + + const workspaceNameInput = screen.getByRole('textbox', { name: 'Workspace Name' }); + await user.click(workspaceNameInput); + await user.type(workspaceNameInput, 'saved workspace'); + const saveButton = screen.getByRole('button', { name: /Save/i }); + await user.click(saveButton); + await waitFor(() => { + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + }); + const loadWorkspaceButton = screen.getByRole('button', { name: /Load Workspace/i }); + await user.click(loadWorkspaceButton); + + const dropdown = within(await screen.findByTestId("workspace-select")).getByRole( + "combobox", + ); + await user.click(dropdown); + expect( + await screen.findByRole("option", { name: "saved workspace" }), + ).toBeInTheDocument(); + + const saved_workspace = screen.getByRole("option", { name: "saved workspace" }); + await user.click(saved_workspace); + const loadButton = screen.getByRole('button', { name: /Load/i }); + await user.click(loadButton); + + + + + +}) + + + + + +test("refreshing a node label sends update/refresheNodeLabels dispatch", async () => { + let user = userEvent.setup(); + const mockStore = configureStore(); + let store = setupStore(); + const mockedAxios = axios as jest.Mocked; + store.dispatch = jest.fn(); + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + render( + + { }} /> + + ); + + const settingsTab = screen.getByRole('tab', { name: 'Settings' }); + await user.click(settingsTab); + const labelTextField = screen.getByTestId(`label-field-0`); + expect(labelTextField).toBeInTheDocument(); + await user.click(labelTextField); + // await user.clear(labelTextField) + await user.type(labelTextField, '{backspace}{backspace}{backspace}{backspace}'); + await user.type(labelTextField, 'age'); + const button = screen.getByRole('button', { name: /Refresh/i }); + await user.click(button); + + await waitFor(() => { + expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ + type: 'options/editNodeLabel', + payload: expect.anything() + })); + expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ + type: 'graph/refreshNodeLabels', + payload: expect.anything() + })); + }) +}); + + +test("change host, port, nodelimit, layout sends dispatches", async () => { + let user = userEvent.setup(); + const mockStore = configureStore(); + let store = setupStore(); + const mockedAxios = axios as jest.Mocked; + jest.spyOn(store, 'dispatch'); + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + render( + + { }} /> + + ); + + const settingsTab = screen.getByRole('tab', { name: 'Settings' }); + await user.click(settingsTab); + + const hostTextField = screen.getByLabelText('host'); + await user.clear(hostTextField); + await user.type(hostTextField, "host_garbage"); + expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ + type: 'gremlin/setHost', + payload: "host_garbage" + })); + + const portTextField = screen.getByLabelText('port'); + await user.clear(portTextField); + await user.type(portTextField, "port_garbage"); + expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ + type: 'gremlin/setPort', + payload: "port_garbage" + })); + + const nodeLimitTextField = screen.getByLabelText('Node Limit'); + await user.clear(nodeLimitTextField); + await user.type(nodeLimitTextField, "10"); + expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ + type: 'options/setNodeLimit', + payload: "10" + })); + + + + + + + + +}); + + + + + diff --git a/test/components/Details/TableComponent.test.tsx b/test/components/Details/TableComponent.test.tsx new file mode 100644 index 0000000..be464ca --- /dev/null +++ b/test/components/Details/TableComponent.test.tsx @@ -0,0 +1,364 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import { render, screen, waitFor, fireEvent, act, within } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { SidebarComponent } from '../../../src/components/Details/SidebarComponent'; +import userEvent from '@testing-library/user-event'; +import { defaultNodeLabel, EdgeData, NodeData } from "../../../src/logics/utils"; +import { setupStore } from "../../../src/app/store"; +import axios from 'axios'; +import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; +import { setNodePositions } from '../../../src/logics/graph'; +import { addNodes, addEdges } from '../../../src/reducers/graphReducer' + + +jest.mock('../../../src/logics/graph', () => ({ + applyLayout: jest.fn(), + getNodePositions: jest.fn(), + setNodePositions: jest.fn(), + layoutOptions: ['force-directed', 'hierarchical'] +})); + +jest.mock("axios", () => ({ + ...jest.requireActual("axios"), + post: jest.fn(), +})); + +const nodes = [ + { + "id": 2, + "label": "Ava", + "properties": { + "name": "Ava", + "age": "21" + }, + "edges": [ + { + "id": "1", + "from": 2, + "to": 3, + "label": "knows", + "properties": { + "length": "2" + } + } + ], + "type": "person" + }, + { + "id": 3, + "label": "Bob", + "properties": { + "name": "Bob", + "age": "18" + }, + "edges": [], + "type": "person" + }, { + "id": 4, + "label": "Caitlyn", + "properties": { + "name": "Caitlyn", + "age": "18" + }, + "edges": [], + "type": "person" + }, { + "id": 5, + "label": "Dominic", + "properties": { + "name": "Dominic", + "age": "18" + }, + "edges": [], + "type": "person" + }, { + "id": 6, + "label": "Eddy", + "properties": { + "name": "Eddy", + "age": "18" + }, + "edges": [], + "type": "person" + }, { + "id": 7, + "label": "Frank", + "properties": { + "name": "Frank", + "age": "18" + }, + "edges": [], + "type": "person" + }, { + "id": 8, + "label": "George", + "properties": { + "name": "George", + "age": "18" + }, + "edges": [], + "type": "person" + }, { + "id": 9, + "label": "Hank", + "properties": { + "name": "Hank", + "age": "18" + }, + "edges": [], + "type": "person" + }, { + "id": 10, + "label": "Ike", + "properties": { + "name": "Ike", + "age": "18" + }, + "edges": [], + "type": "person" + }, { + "id": 11, + "label": "Jonathan", + "properties": { + "name": "Jonathan", + "age": "18" + }, + "edges": [], + "type": "person" + }, { + "id": 12, + "label": "Kyle", + "properties": { + "name": "Kyle", + "age": "18" + }, + "edges": [], + "type": "person" + } +] +const edges = [ + { + "id": "1", + "from": 2, + "to": 3, + "label": "knows", + "properties": { + "length": "2" + }, + "type": "knows" + } +] +const nodesDummy: NodeData[] = [{ id: 1, label: 'Bob', properties: { name: "Bob", age: "21" }, edges: [], type: 'person', x: 0, y: 0 }]; +const edgesDummy: EdgeData[] = [{ id: 1, from: 2, to: 3, label: 'created', properties: { name: "dummy edge", age: "0" }, type: 'created' }]; +// Replace SAVED_QUERIES import in SavedQueries component with customQueries for testing +jest.mock('../../../src/constants', () => ({ + INITIAL_LABEL_MAPPINGS: { + person: 'name' + }, + GRAPH_IMPL: "vis" + +})); + +type State = { + gremlin: { + host: string; + port: string; + query: string; + }; + options: { + nodeLabels: string[]; + nodeLimit: number; + queryHistory: string[]; + }; + graph: { + selectedNode: NodeData | null; + selectedEdge: EdgeData | null; + nodes: NodeData[], + edges: NodeData[], + }; + + +}; + +const initialState: State = { + gremlin: { + host: 'localhost', + port: '8182', + query: 'g.V()' + }, + options: { + nodeLabels: [], + nodeLimit: 50, + queryHistory: [] + }, + + graph: { + selectedNode: null, + selectedEdge: null, + nodes: [], + edges: [], + } +}; + + +test("dispatch nodes and edges and confirm Bob and Max appears in table", async () => { + let user = userEvent.setup(); + const mockStore = configureStore(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + store.dispatch(addNodes(nodes)); + store.dispatch(addEdges(edges)); + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + + render( + + { }} /> + + ); + + + const tableTab = screen.getByRole('tab', { name: 'Table View' }); + await user.click(tableTab); + + expect(screen.getByText('Ava')).toBeInTheDocument(); + expect(screen.getByText('Bob')).toBeInTheDocument(); + +}); + + +test("test that expand row works and shows age", async () => { + let user = userEvent.setup(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + store.dispatch(addNodes(nodes)); + store.dispatch(addEdges(edges)); + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + + render( + + { }} /> + + ); + + + const tableTab = screen.getByRole('tab', { name: 'Table View' }); + await user.click(tableTab); + + const expandButtons = screen.getAllByRole('button', { name: /expand row/i }); + await user.click(expandButtons[0]); + expect(screen.getByText('age')).toBeInTheDocument(); + expect(screen.getByText('21')).toBeInTheDocument(); +}); + + +test("test click sort button twice should sort descending then ascending by name", async () => { + let user = userEvent.setup(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + store.dispatch(addNodes(nodes)); + store.dispatch(addEdges(edges)); + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + + render( + + { }} /> + + ); + + + const tableTab = screen.getByRole('tab', { name: 'Table View' }); + await user.click(tableTab); + + const sortButton = screen.getByRole('button', { name: 'Label' }); + + // Click the sort label + await user.click(sortButton); + + const headersDescending = screen.getAllByRole('rowheader'); + + const headerTextsDescending = headersDescending.map(header => header.textContent); + expect(headerTextsDescending).toEqual(['Kyle', 'Jonathan', 'Ike', 'Hank', 'George', 'Frank', 'Eddy', 'Dominic', 'Caitlyn', 'Bob']); + + await user.click(sortButton); + + const headersAscending = screen.getAllByRole('rowheader'); + + const headerTextsAscending = headersAscending.map(header => header.textContent); + expect(headerTextsAscending).toEqual(['Ava', 'Bob', 'Caitlyn', 'Dominic', 'Eddy', 'Frank', 'George', 'Hank', 'Ike', 'Jonathan']); +}); + + +test("test that rows per page can be set from default(10) to 5", async () => { + let user = userEvent.setup(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + store.dispatch(addNodes(nodes)); + store.dispatch(addEdges(edges)); + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + + render( + + { }} /> + + ); + + + const tableTab = screen.getByRole('tab', { name: 'Table View' }); + await user.click(tableTab); + + const rowsPerPageButton = screen.getByRole('combobox'); + await user.click(rowsPerPageButton); + + const fiveOption = screen.getByRole("option", { name: "5" }) + await user.click(fiveOption); + + const headers = screen.getAllByRole('rowheader'); + expect(headers).toHaveLength(5); +}); + +test("dechecking node selects edge and shows 1 edge", async () => { + let user = userEvent.setup(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + store.dispatch(addNodes(nodes)); + store.dispatch(addEdges(edges)); + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); + + render( + + { }} /> + + ); + + + const tableTab = screen.getByRole('tab', { name: 'Table View' }); + await user.click(tableTab); + + const checkboxes = screen.getAllByRole('checkbox'); + await user.click(checkboxes[0]); + const allKnowsTexts = screen.getAllByText('knows'); + expect(allKnowsTexts[0]).toBeInTheDocument(); +}); + + + + + + + + + + + + + + + + diff --git a/test/components/ModalDialog/ModalDialogComponent.test.tsx b/test/components/ModalDialog/ModalDialogComponent.test.tsx new file mode 100644 index 0000000..c3e208e --- /dev/null +++ b/test/components/ModalDialog/ModalDialogComponent.test.tsx @@ -0,0 +1,199 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import { render, screen, waitFor, fireEvent, act, within } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { App } from '../../../src/App'; +import userEvent from '@testing-library/user-event'; +import { defaultNodeLabel, EdgeData, NodeData, extractEdgesAndNodes, storeSuggestions } from "../../../src/logics/utils"; +import { setupStore } from "../../../src/app/store"; +import axios from 'axios'; +import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; +import { setNodePositions } from '../../../src/logics/graph'; +import { addNodes, addEdges } from '../../../src/reducers/graphReducer' +import { openNodeDialog, setSuggestions } from '../../../src/reducers/dialogReducer'; +import { setNodeLabels } from '../../../src/reducers/optionReducer'; +import { ModalDialogComponent } from '../../../src/components/ModalDialog/ModalDialogComponent'; +// jest.mock('../../../src/logics/graph', () => ({ +// applyLayout: jest.fn(), +// getNodePositions: jest.fn(), +// setNodePositions: jest.fn(), +// layoutOptions: ['force-directed', 'hierarchical'] +// })); +jest.mock('../../../src/logics/graphImpl/sigmaImpl', () => ({ + __esModule: true, // Ensure it's treated as a module + +})); + +jest.mock("axios", () => ({ + ...jest.requireActual("axios"), + post: jest.fn(), +})); + +jest.mock('../../../src/constants', () => ({ + INITIAL_LABEL_MAPPINGS: { + person: 'name' + }, + GRAPH_IMPL: "vis" + +})); + +type State = { + gremlin: { + host: string; + port: string; + query: string; + }; + options: { + nodeLabels: string[]; + nodeLimit: number; + queryHistory: string[]; + }; + graph: { + selectedNode: NodeData | null; + selectedEdge: EdgeData | null; + nodes: NodeData[], + edges: NodeData[], + }; + + +}; + +const initialState: State = { + gremlin: { + host: 'localhost', + port: '8182', + query: 'g.V()' + }, + options: { + nodeLabels: [], + nodeLimit: 50, + queryHistory: [] + }, + + graph: { + selectedNode: null, + selectedEdge: null, + nodes: [], + edges: [], + } +}; + +test("test modalDialog renders", async () => { + let user = userEvent.setup(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + store.dispatch(openNodeDialog({ x: 200, y: 200 })); + render( + + + + ); + expect(screen.queryByRole('dialog')).toBeInTheDocument(); +}) + +test("test modalDialog renders with suggested", async () => { + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data:{ + "data": [ + { + "id": 32, + "label": "person", + "properties": { + "name": "Bob", + "age": "21" + }, + "edges": [] + } + ], + "status": 200, + "statusText": "OK", + "headers": { + "content-length": "78", + "content-type": "application/json; charset=utf-8" + }, + "config": { + "transitional": { + "silentJSONParsing": true, + "forcedJSONParsing": true, + "clarifyTimeoutError": false + }, + "adapter": [ + "xhr", + "http", + "fetch" + ], + "transformRequest": [ + null + ], + "transformResponse": [ + null + ], + "timeout": 0, + "xsrfCookieName": "XSRF-TOKEN", + "xsrfHeaderName": "X-XSRF-TOKEN", + "maxContentLength": -1, + "maxBodyLength": -1, + "env": {}, + "headers": { + "Accept": "application/json, text/plain, */*", + "Content-Type": "application/json" + }, + "method": "post", + "url": "http://localhost:3001/query", + "data": "{\"host\":\"localhost\",\"port\":\"8182\",\"query\":\"g.addV('person').property('name', 'Bob').property('age', '21')\",\"nodeLimit\":100}" + }, + "request": {} + }}); + + // const argument0: NodeData[] = [ + // { + // "id": 1, + // "label": "person", + // "properties": { + // "name": "Bob", + // "age": "21" + // }, + // "edges": [ + // { + // "id": "0", + // "from": 1, + // "to": 2, + // "label": "knows", + // "properties": { + // "length": "2" + // } + // } + // ] + // }, + // { + // "id": 2, + // "label": "person", + // "properties": { + // "name": "Max", + // "age": "18" + // }, + // "edges": [] + // }, + // ] as NodeData[] + // const argument1 = []; + let user = userEvent.setup(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + + // store.dispatch(setNodeLabels(nodeLabels)); + store.dispatch(openNodeDialog({ x: 200, y: 200 })); + + console.log(store.getState()); + render( + + + + ); + + expect(screen.queryByRole('dialog')).toBeInTheDocument(); + const input = screen.getByDisplayValue('name'); + + // Additional checks can be performed to ensure the right element is selected, if needed + expect(input).toBeInTheDocument(); +}) diff --git a/test/components/logics/utils.test.ts b/test/components/logics/utils.test.ts new file mode 100644 index 0000000..2550544 --- /dev/null +++ b/test/components/logics/utils.test.ts @@ -0,0 +1,107 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import { render, screen, waitFor, fireEvent, act, within } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { App } from '../../../src/App'; +import userEvent from '@testing-library/user-event'; +import { defaultNodeLabel, EdgeData, extractEdgesAndNodes, NodeData } from "../../../src/logics/utils"; +import { setupStore } from "../../../src/app/store"; +import axios from 'axios'; +import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; +import { setNodePositions } from '../../../src/logics/graph'; +import { addNodes, addEdges } from '../../../src/reducers/graphReducer' +import { openNodeDialog } from '../../../src/reducers/dialogReducer'; +import { ModalDialogComponent } from '../../../src/components/ModalDialog/ModalDialogComponent'; +import { DIALOG_TYPES } from '../../../src/components/ModalDialog/ModalDialogComponent'; +test("extractEdgesAndNodes with custom arguments returns correct nodes, edges, nodeLabels", async () => { + const argument0 : NodeData[] = [ + { + "id": 1, + "label": "person", + "properties": { + "name": "Bob", + "age": "21" + }, + "edges": [ + { + "id": "0", + "from": 1, + "to": 2, + "label": "knows", + "properties": { + "length": "2" + } + } + ] + }, + { + "id": 2, + "label": "person", + "properties": { + "name": "Max", + "age": "18" + }, + "edges": [] + }, + ] as NodeData[] + const argument1 = []; + + + + const expectedNodes = [ + { + "id": 1, + "label": "Bob", + "properties": { + "name": "Bob", + "age": "21" + }, + "edges": [ + { + "id": "0", + "from": 1, + "to": 2, + "label": "knows", + "properties": { + "length": "2" + } + } + ], + "type": "person" + }, + { + "id": 2, + "label": "Max", + "properties": { + "name": "Max", + "age": "18" + }, + "edges": [], + "type": "person" + } + ] + const expectedEdges = [ + { + "id": "0", + "from": 1, + "to": 2, + "label": "knows", + "properties": { + "length": "2" + }, + "type": "knows" + } + ] + const expectedNodeLabels = [ + { + "type": "person", + "field": "name" + } + ] + + const {edges, nodes, nodeLabels} = extractEdgesAndNodes(argument0, argument1); + expect(edges).toEqual(expectedEdges); + expect(nodes).toEqual(expectedNodes); + expect(nodeLabels).toEqual(expectedNodeLabels); +}) \ No newline at end of file From 671a8f66a803c1cfb9fc561176d95369eafee8b2 Mon Sep 17 00:00:00 2001 From: joon-at-sri Date: Fri, 19 Jul 2024 12:41:10 -0700 Subject: [PATCH 2/4] 80%, before major cleanups --- .../ModalDialog/ModalDialogComponent.tsx | 1 - src/logics/actionHelper.ts | 2 - src/logics/utils.ts | 7 +- src/reducers/dialogReducer.ts | 1 - src/reducers/graphReducer.ts | 8 +- .../ModalDialog/ModalDialogComponent.test.tsx | 281 +++++++++++------- test/reducers/graphReducer.test.ts | 34 +++ 7 files changed, 211 insertions(+), 123 deletions(-) create mode 100644 test/reducers/graphReducer.test.ts diff --git a/src/components/ModalDialog/ModalDialogComponent.tsx b/src/components/ModalDialog/ModalDialogComponent.tsx index f5e4e90..2840ae9 100644 --- a/src/components/ModalDialog/ModalDialogComponent.tsx +++ b/src/components/ModalDialog/ModalDialogComponent.tsx @@ -180,7 +180,6 @@ export const ModalDialogComponent = () => { { headers: { 'Content-Type': 'application/json' } } ) .then((response) => { - console.log(JSON.stringify(response, null, 2)); const addedElement = isNodeDialog ? [{ ...response.data[0], x, y }] : response.data manualAddElement(isNodeDialog, addedElement, nodeLabels, dispatch); }) diff --git a/src/logics/actionHelper.ts b/src/logics/actionHelper.ts index 57e282c..2adcdc6 100644 --- a/src/logics/actionHelper.ts +++ b/src/logics/actionHelper.ts @@ -5,8 +5,6 @@ import store, { AppDispatch } from '../app/store'; import { IdType } from "vis-network"; export const onFetchQuery = (result: any, query: string, oldNodeLabels: NodeLabel[], dispatch: AppDispatch) => { - console.log(JSON.stringify(result.data, null, 2)); - console.log(JSON.stringify(oldNodeLabels, null, 2)); const { nodes, edges, nodeLabels } = extractEdgesAndNodes( result.data, oldNodeLabels diff --git a/src/logics/utils.ts b/src/logics/utils.ts index a446299..7e926df 100644 --- a/src/logics/utils.ts +++ b/src/logics/utils.ts @@ -69,7 +69,6 @@ export interface TempFieldSuggestions { } export const storeSuggestions = (nodes: Array, edges: Array) => { - console.log("storeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") const suggestions: Suggestions = { [DIALOG_TYPES.NODE]: { types: [], labels: {} }, [DIALOG_TYPES.EDGE]: { types: [], labels: {} } @@ -109,15 +108,14 @@ export const storeSuggestions = (nodes: Array, edges: Array) }); tempTypeSet = new Set(edgeSuggestions.types); suggestions[DIALOG_TYPES.EDGE].types = Array.from(tempTypeSet) - store.dispatch(setSuggestions(suggestions)); + }; export const extractEdgesAndNodes = (nodeList: Array, oldNodeLabels: NodeLabel[] = []) => { let edges: Edge[] = []; const nodes: Node[] = []; const nodeLabels: NodeLabel[] = [...oldNodeLabels]; - const nodeLabelMap = _.mapValues(_.keyBy(nodeLabels, 'type'), 'field'); _.forEach(nodeList, (node) => { @@ -140,9 +138,6 @@ export const extractEdgesAndNodes = (nodeList: Array, oldNodeLabels: N } }); storeSuggestions(nodes as NodeData[], edges as EdgeData[]); - console.log(JSON.stringify(nodes, null, 2)); - console.log(JSON.stringify(edges, null, 2)); - console.log(JSON.stringify(nodeLabels, null, 2)); return { edges, nodes, nodeLabels }; }; diff --git a/src/reducers/dialogReducer.ts b/src/reducers/dialogReducer.ts index dd48136..57ec0d3 100644 --- a/src/reducers/dialogReducer.ts +++ b/src/reducers/dialogReducer.ts @@ -57,7 +57,6 @@ const slice = createSlice({ state.dialogType = action.payload; }, setSuggestions: (state, action) => { - console.log("setSuggestions"); const suggestions = action.payload as Suggestions; Object.entries(suggestions).forEach(([dialogType, elementSuggestions]) => { diff --git a/src/reducers/graphReducer.ts b/src/reducers/graphReducer.ts index 0b073b1..67a4aef 100644 --- a/src/reducers/graphReducer.ts +++ b/src/reducers/graphReducer.ts @@ -12,7 +12,7 @@ export type Workspace = { view: { x: number, y: number } } -type GraphState = { +export type GraphState = { nodes: NodeData[]; edges: EdgeData[]; selectedNode?: NodeData; @@ -43,7 +43,6 @@ const slice = createSlice({ return state; }, addNodes: (state, action) => { - console.log('addNodes'); state = Object.assign({}, state); const newNodes = _.differenceBy(action.payload, state.nodes, (node: any) => node.id); state.nodes = [...state.nodes, ...newNodes]; @@ -94,7 +93,10 @@ const slice = createSlice({ return state; }, refreshNodeLabels: (state, action) => { + console.log(state); + console.log("ME") const nodeLabelMap = _.mapValues(_.keyBy(action.payload, 'type'), 'field'); + console.log(JSON.stringify(action.payload, null, 2)); state.nodes = state.nodes.map((node: any) => { if (node.type in nodeLabelMap) { const field = nodeLabelMap[node.type]; @@ -102,8 +104,10 @@ const slice = createSlice({ if (label === undefined) return { ...node, ...{ label: defaultNodeLabel(node) } } else + console.log(JSON.stringify(node, null, 2)); return { ...node, label }; } + return { ...node, ...{ label: defaultNodeLabel(node) } } }); }, diff --git a/test/components/ModalDialog/ModalDialogComponent.test.tsx b/test/components/ModalDialog/ModalDialogComponent.test.tsx index c3e208e..96de959 100644 --- a/test/components/ModalDialog/ModalDialogComponent.test.tsx +++ b/test/components/ModalDialog/ModalDialogComponent.test.tsx @@ -11,7 +11,7 @@ import axios from 'axios'; import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; import { setNodePositions } from '../../../src/logics/graph'; import { addNodes, addEdges } from '../../../src/reducers/graphReducer' -import { openNodeDialog, setSuggestions } from '../../../src/reducers/dialogReducer'; +import { openNodeDialog, openEdgeDialog, setSuggestions } from '../../../src/reducers/dialogReducer'; import { setNodeLabels } from '../../../src/reducers/optionReducer'; import { ModalDialogComponent } from '../../../src/components/ModalDialog/ModalDialogComponent'; // jest.mock('../../../src/logics/graph', () => ({ @@ -59,26 +59,6 @@ type State = { }; -const initialState: State = { - gremlin: { - host: 'localhost', - port: '8182', - query: 'g.V()' - }, - options: { - nodeLabels: [], - nodeLimit: 50, - queryHistory: [] - }, - - graph: { - selectedNode: null, - selectedEdge: null, - nodes: [], - edges: [], - } -}; - test("test modalDialog renders", async () => { let user = userEvent.setup(); let store = setupStore({}); @@ -92,99 +72,30 @@ test("test modalDialog renders", async () => { expect(screen.queryByRole('dialog')).toBeInTheDocument(); }) -test("test modalDialog renders with suggested", async () => { - const mockedAxios = axios as jest.Mocked; - mockedAxios.post.mockResolvedValue({ data:{ - "data": [ - { - "id": 32, - "label": "person", - "properties": { - "name": "Bob", - "age": "21" - }, - "edges": [] - } - ], - "status": 200, - "statusText": "OK", - "headers": { - "content-length": "78", - "content-type": "application/json; charset=utf-8" - }, - "config": { - "transitional": { - "silentJSONParsing": true, - "forcedJSONParsing": true, - "clarifyTimeoutError": false - }, - "adapter": [ - "xhr", - "http", - "fetch" - ], - "transformRequest": [ - null - ], - "transformResponse": [ - null - ], - "timeout": 0, - "xsrfCookieName": "XSRF-TOKEN", - "xsrfHeaderName": "X-XSRF-TOKEN", - "maxContentLength": -1, - "maxBodyLength": -1, - "env": {}, - "headers": { - "Accept": "application/json, text/plain, */*", - "Content-Type": "application/json" - }, - "method": "post", - "url": "http://localhost:3001/query", - "data": "{\"host\":\"localhost\",\"port\":\"8182\",\"query\":\"g.addV('person').property('name', 'Bob').property('age', '21')\",\"nodeLimit\":100}" - }, - "request": {} - }}); - - // const argument0: NodeData[] = [ - // { - // "id": 1, - // "label": "person", - // "properties": { - // "name": "Bob", - // "age": "21" - // }, - // "edges": [ - // { - // "id": "0", - // "from": 1, - // "to": 2, - // "label": "knows", - // "properties": { - // "length": "2" - // } - // } - // ] - // }, - // { - // "id": 2, - // "label": "person", - // "properties": { - // "name": "Max", - // "age": "18" - // }, - // "edges": [] - // }, - // ] as NodeData[] - // const argument1 = []; +test("test node modalDialog renders with suggested", async () => { let user = userEvent.setup(); let store = setupStore({}); jest.spyOn(store, 'dispatch'); // store.dispatch(setNodeLabels(nodeLabels)); store.dispatch(openNodeDialog({ x: 200, y: 200 })); - - console.log(store.getState()); + store.dispatch(setSuggestions({ + "node": { + "types": [ + "person" + ], + "labels": { + "person": [ + "name", + "age" + ] + } + }, + "edge": { + "types": [], + "labels": {} + } + })) render( @@ -192,8 +103,156 @@ test("test modalDialog renders with suggested", async () => { ); expect(screen.queryByRole('dialog')).toBeInTheDocument(); - const input = screen.getByDisplayValue('name'); + expect(screen.getByDisplayValue('name')).toBeInTheDocument(); + expect(screen.getByDisplayValue('age')).toBeInTheDocument(); + +}) + +test("test edge modalDialog renders with suggested", async () => { + let user = userEvent.setup(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + + // store.dispatch(setNodeLabels(nodeLabels)); + store.dispatch(openEdgeDialog({ edgeFrom: 0, edgeTo: 1 })); + store.dispatch(setSuggestions({ + "node": { + "types": [ + "person" + ], + "labels": { + "person": [ + "name", + "age" + ] + } + }, + "edge": { + "types": [ + "knows" + ], + "labels": { + "knows": [ + "length", + "strength" + ] + } + } + })) + + render( + + + + ); + + expect(screen.queryByRole('dialog')).toBeInTheDocument(); + expect(screen.getByDisplayValue('length')).toBeInTheDocument(); + expect(screen.getByDisplayValue('strength')).toBeInTheDocument(); - // Additional checks can be performed to ensure the right element is selected, if needed - expect(input).toBeInTheDocument(); }) + +test("submitting dispatches addNode", async () => { + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data:{ + "data": [ + { + "id": 0, + "label": "person", + "properties": { + "name": "Bob" + }, + "edges": [] + } + ] + }}); + + + let user = userEvent.setup(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + + // store.dispatch(setNodeLabels(nodeLabels)); + store.dispatch(openNodeDialog({ x: 200, y: 200 })); + + + render( + + + + ); + const inputElement = screen.getByRole('combobox', { name: /type/i }); + // await user.click(inputElement); + await user.type(inputElement, "person"); + const buttonElement = screen.getByRole('button', { name: /Add More../i }); + await user.click(buttonElement); + + const nameInput = screen.getByRole('combobox', { name: /Property Name/i }); + expect(nameInput).toBeInTheDocument(); + // await user.click(inputElement2); + await user.type(nameInput, "name"); + await waitFor(() => { + expect(nameInput).toHaveValue('name'); + }); + + // const valueInput = screen.getByText('Property Value'); + const valueInput = screen.getByRole('textbox', { name: /Property Value/i }); + // await user.click(inputElement3); + await user.type(valueInput, "Bob"); + await waitFor(() => { + expect(valueInput).toHaveValue('Bob'); + }); + + const submitButton = screen.getByRole('button', { name: /Submit/i }); + await user.click(submitButton); + + await waitFor(() => { + expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ + type: 'graph/addNodes', + payload: expect.anything() + })); +}); + + // Additional checks can be performed to ensure the right element is selected, if needed +}) + + + +test("cancel closes dialog", async () => { + const mockedAxios = axios as jest.Mocked; + mockedAxios.post.mockResolvedValue({ data:{ + "data": [ + { + "id": 0, + "label": "person", + "properties": { + "name": "Bob" + }, + "edges": [] + } + ] + }}); + + + let user = userEvent.setup(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + + // store.dispatch(setNodeLabels(nodeLabels)); + store.dispatch(openNodeDialog({ x: 200, y: 200 })); + + render( + + + + ); + const cancelButton = screen.getByRole('button', { name: /Cancel/i }); + await user.click(cancelButton); + + await waitFor(() => { + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + }) + // Additional checks can be performed to ensure the right element is selected, if needed +}) + + diff --git a/test/reducers/graphReducer.test.ts b/test/reducers/graphReducer.test.ts new file mode 100644 index 0000000..c9f5f70 --- /dev/null +++ b/test/reducers/graphReducer.test.ts @@ -0,0 +1,34 @@ +import reducer, { refreshNodeLabels, GraphState} from '../../src/reducers/graphReducer'; + +test('test refreshNodeLabel changes label in state to age', () => { + const previousState = {nodes: [{ + "id": 0, + "label": "Bob", + "properties": { + "name": "Bob", + "age": "21" + }, + "edges": [], + "type": "person" + }], + edges: [], + selectedNode: undefined, + selectedEdge: undefined, + nodeColorMap: {}, + workspaces: [] + } as any + + const action = refreshNodeLabels([ + { + type: "person", + field: "age" + } + ]); + + const newState = reducer(previousState, action); + expect(newState.nodes[0]).toEqual(expect.objectContaining({ + label: "21" + })); + + + }) \ No newline at end of file From 6b1f543930852c705a8f02e244ed300e9d4e02c7 Mon Sep 17 00:00:00 2001 From: joon-at-sri Date: Fri, 19 Jul 2024 13:08:08 -0700 Subject: [PATCH 3/4] cleaned --- jest.config.js | 2 +- package-lock.json | 2203 +---------------- package.json | 4 - .../Details/DetailsComponent.test.tsx | 43 +- .../Details/QueryComponent.test.tsx | 53 +- .../Details/SavedQueriesComponent.test.tsx | 12 +- .../Details/SettingsComponent.test.tsx | 115 +- .../Details/TableComponent.test.tsx | 61 +- .../ModalDialog/ModalDialogComponent.test.tsx | 195 +- test/components/logics/utils.test.ts | 168 +- tsconfig.json | 3 +- 11 files changed, 265 insertions(+), 2594 deletions(-) diff --git a/jest.config.js b/jest.config.js index b73d5e7..50577de 100644 --- a/jest.config.js +++ b/jest.config.js @@ -110,7 +110,7 @@ const config = { // notifyMode: "failure-change", // A preset that is used as a base for Jest's configuration - preset: "jest-puppeteer", + // preset: "jest-puppeteer", // Run tests from one or more projects // projects: undefined, diff --git a/package-lock.json b/package-lock.json index 3b6fc94..219de32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,6 @@ "graphology-types": "^0.24.7", "gremlin": "^3.4.4", "lodash": "^4.17.15", - "puppeteer": "^22.12.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-edit-text": "^5.1.1", @@ -73,9 +72,6 @@ "jest-axe": "^9.0.0", "jest-canvas-mock": "^2.5.2", "jest-environment-jsdom": "^29.7.0", - "jest-puppeteer": "^10.0.1", - "jest-webgl-canvas-mock": "^2.5.3", - "msw": "^2.3.1", "npm-run-all": "^4.1.5", "react-scripts": "^5.0.1", "react-test-renderer": "^18.3.1", @@ -2367,33 +2363,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@bundled-es-modules/cookie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", - "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", - "dev": true, - "dependencies": { - "cookie": "^0.5.0" - } - }, - "node_modules/@bundled-es-modules/cookie/node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@bundled-es-modules/statuses": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", - "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", - "dev": true, - "dependencies": { - "statuses": "^2.0.1" - } - }, "node_modules/@csstools/normalize.css": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", @@ -3002,21 +2971,6 @@ "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==", "license": "MIT" }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "dev": true - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dev": true, - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -3053,143 +3007,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/@inquirer/confirm": { - "version": "3.1.13", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.13.tgz", - "integrity": "sha512-N9knAyc9w4E57vimoYWDalDwuLfDNrgJFJzfdiIIsB1jT4id/GGpwexXtM4fehkxqv8ob3YYWzGANt4cA+5hRw==", - "dev": true, - "dependencies": { - "@inquirer/core": "^9.0.1", - "@inquirer/type": "^1.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.1.tgz", - "integrity": "sha512-Kd3uUrkAoADpcocgk3Vk5DN2N6Yid+QlO8ceZWluFiLtVzUBdv/wK1GgZOq30OMDQNOZVTBsbSO3f/E0sOTk8w==", - "dev": true, - "dependencies": { - "@inquirer/figures": "^1.0.3", - "@inquirer/type": "^1.4.0", - "@types/mute-stream": "^0.0.4", - "@types/node": "^20.14.9", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "cli-spinners": "^2.9.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core/node_modules/@types/node": { - "version": "20.14.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", - "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@inquirer/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@inquirer/core/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@inquirer/core/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@inquirer/core/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/figures": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.3.tgz", - "integrity": "sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.4.0.tgz", - "integrity": "sha512-AjOqykVyjdJQvtfkNDGUyMYGF8xN50VUxftCQWsOyIo4DFRLr6VQhW0VItGI1JIyQGCGgIpKa7hMMwNhZb4OIw==", - "dev": true, - "dependencies": { - "mute-stream": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -5003,32 +4820,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@mswjs/cookies": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.1.1.tgz", - "integrity": "sha512-W68qOHEjx1iD+4VjQudlx26CPIoxmIAtK4ZCexU0/UJBG6jYhcuyzKJx+Iw8uhBIGd9eba64XgWVgo20it1qwA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@mswjs/interceptors": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.29.1.tgz", - "integrity": "sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==", - "dev": true, - "dependencies": { - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/logger": "^0.3.0", - "@open-draft/until": "^2.0.0", - "is-node-process": "^1.2.0", - "outvariant": "^1.2.1", - "strict-event-emitter": "^0.5.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@mui/base": { "version": "5.0.0-beta.40", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", @@ -5355,28 +5146,6 @@ "node": ">= 8" } }, - "node_modules/@open-draft/deferred-promise": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", - "dev": true - }, - "node_modules/@open-draft/logger": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", - "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", - "dev": true, - "dependencies": { - "is-node-process": "^1.2.0", - "outvariant": "^1.4.0" - } - }, - "node_modules/@open-draft/until": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", - "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", - "dev": true - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -5457,268 +5226,99 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@puppeteer/browsers": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", - "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", - "dependencies": { - "debug": "4.3.4", - "extract-zip": "2.0.1", - "progress": "2.0.3", - "proxy-agent": "6.4.0", - "semver": "7.6.0", - "tar-fs": "3.0.5", - "unbzip2-stream": "1.4.3", - "yargs": "17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@puppeteer/browsers/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@reduxjs/toolkit": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", + "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } } }, - "node_modules/@puppeteer/browsers/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dev": true, + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" }, "engines": { - "node": ">=12" - } - }, - "node_modules/@puppeteer/browsers/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" + "node": ">= 10.0.0" }, - "engines": { - "node": ">=6.0" + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" }, "peerDependenciesMeta": { - "supports-color": { + "@types/babel__core": { "optional": true } } }, - "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/@puppeteer/browsers/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" } }, - "node_modules/@puppeteer/browsers/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "dev": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" } }, - "node_modules/@puppeteer/browsers/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@puppeteer/browsers/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@puppeteer/browsers/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@puppeteer/browsers/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@puppeteer/browsers/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/@reduxjs/toolkit": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", - "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==", - "license": "MIT", - "dependencies": { - "immer": "^9.0.21", - "redux": "^4.2.1", - "redux-thunk": "^2.4.2", - "reselect": "^4.1.8" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.0.2" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-babel": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", - "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "@types/babel__core": "^7.1.9", - "rollup": "^1.20.0||^2.0.0" - }, - "peerDependenciesMeta": { - "@types/babel__core": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/plugin-replace": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", - "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - }, - "peerDependencies": { - "rollup": "^1.20.0 || ^2.0.0" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" }, "engines": { "node": ">= 8.0.0" @@ -5741,27 +5341,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "dev": true, - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "dev": true - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "dev": true - }, "node_modules/@sigma/edge-curve": { "version": "3.0.0-beta.9", "resolved": "https://registry.npmjs.org/@sigma/edge-curve/-/edge-curve-3.0.0-beta.9.tgz", @@ -6279,11 +5858,6 @@ "node": ">= 10" } }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" - }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -6387,12 +5961,6 @@ "@types/node": "*" } }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true - }, "node_modules/@types/cytoscape": { "version": "3.21.4", "resolved": "https://registry.npmjs.org/@types/cytoscape/-/cytoscape-3.21.4.tgz", @@ -6787,20 +6355,11 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mute-stream": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", - "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/node": { "version": "18.19.34", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.34.tgz", "integrity": "sha512-eXF4pfBNV5DAMKGbI02NnDtWrQ40hAN558/2vvS4gMpMIxaf6JmD7YjnZbq0Q9TDSSkKBamime8ewRoomHdt4g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -7000,12 +6559,6 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "node_modules/@types/statuses": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", - "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", - "dev": true - }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -7025,12 +6578,6 @@ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", "license": "MIT" }, - "node_modules/@types/wrap-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", - "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", - "dev": true - }, "node_modules/@types/ws": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", @@ -7058,15 +6605,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", @@ -7767,6 +7305,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8061,17 +7600,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -8187,11 +7715,6 @@ "dequal": "^2.0.3" } }, - "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" - }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -8648,47 +8171,6 @@ "dev": true, "license": "MIT" }, - "node_modules/bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", - "optional": true - }, - "node_modules/bare-fs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", - "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", - "optional": true, - "dependencies": { - "bare-events": "^2.0.0", - "bare-path": "^2.0.0", - "bare-stream": "^2.0.0" - } - }, - "node_modules/bare-os": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", - "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", - "optional": true - }, - "node_modules/bare-path": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", - "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", - "optional": true, - "dependencies": { - "bare-os": "^2.1.0" - } - }, - "node_modules/bare-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", - "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", - "optional": true, - "dependencies": { - "streamx": "^2.18.0" - } - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -8709,14 +8191,6 @@ ], "license": "MIT" }, - "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -8925,14 +8399,6 @@ "ieee754": "^1.2.1" } }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "engines": { - "node": "*" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -9155,19 +8621,6 @@ "node": ">=6.0" } }, - "node_modules/chromium-bidi": { - "version": "0.5.24", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.24.tgz", - "integrity": "sha512-5xQNN2SVBdZv4TxeMLaI+PelrnZsHDhn8h2JtyriLr+0qHcZS8BMuo93qN6J1VmtmrgYP+rmcLHcbpnA8QJh+w==", - "dependencies": { - "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0", - "zod": "3.23.8" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -9218,27 +8671,6 @@ "node": ">=0.10.0" } }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, "node_modules/cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -10097,19 +9529,6 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, - "node_modules/cwd": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/cwd/-/cwd-0.10.0.tgz", - "integrity": "sha512-YGZxdTTL9lmLkCUTpg4j0zQ7IhRB5ZmqNBbGCl3Tg6MP/d5/6sY7L5mmTjzbc6JKgVZYiqTQTNhPFsbXNGlRaA==", - "dev": true, - "dependencies": { - "find-pkg": "^0.1.2", - "fs-exists-sync": "^0.1.0" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/cytoscape": { "version": "3.29.2", "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.29.2.tgz", @@ -10215,14 +9634,6 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "engines": { - "node": ">= 14" - } - }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -10428,19 +9839,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -10530,11 +9928,6 @@ "dev": true, "license": "MIT" }, - "node_modules/devtools-protocol": { - "version": "0.0.1299070", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz", - "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==" - }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -10804,14 +10197,6 @@ "node": ">= 0.8" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/enhanced-resolve": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", @@ -10836,14 +10221,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "engines": { - "node": ">=6" - } - }, "node_modules/errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -11059,6 +10436,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -11086,6 +10464,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -11106,6 +10485,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "optional": true, "engines": { "node": ">=0.10.0" @@ -11842,6 +11222,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -11881,6 +11262,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -11897,6 +11279,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -12032,18 +11415,6 @@ "node": ">= 0.8.0" } }, - "node_modules/expand-tilde": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "integrity": "sha512-rtmc+cjLZqnu9dSYosX9EWmSJhTwpACgJQTfj4hgg2JjOD/6SIQalZrt4a3aQeh++oNxkazcaxrhPUj6+g5G/Q==", - "dev": true, - "dependencies": { - "os-homedir": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/expect": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", @@ -12060,15 +11431,6 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/expect-puppeteer": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/expect-puppeteer/-/expect-puppeteer-10.0.0.tgz", - "integrity": "sha512-E7sE6nVdEbrnpDOBMmcLgyqLJKt876AlBg1A+gsu5R8cWx+SLafreOgJAgzXg5Qko7Tk0cW5oZdRbHQLU738dg==", - "dev": true, - "engines": { - "node": ">=16" - } - }, "node_modules/expect/node_modules/@jest/expect-utils": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", @@ -12326,39 +11688,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -12366,11 +11695,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" - }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -12448,14 +11772,6 @@ "bser": "2.1.1" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -12621,106 +11937,6 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "node_modules/find-file-up": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-0.1.3.tgz", - "integrity": "sha512-mBxmNbVyjg1LQIIpgO8hN+ybWBgDQK8qjht+EbrTCGmmPV/sc7RF1i9stPTD6bpvXZywBdrwRYxhSdJv867L6A==", - "dev": true, - "dependencies": { - "fs-exists-sync": "^0.1.0", - "resolve-dir": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/find-pkg": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-0.1.2.tgz", - "integrity": "sha512-0rnQWcFwZr7eO0513HahrWafsc3CTFioEB7DRiEYCUM/70QXSY8f3mCST17HXLcPvEhzH/Ty/Bxd72ZZsr/yvw==", - "dev": true, - "dependencies": { - "find-file-up": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/find-process": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/find-process/-/find-process-1.4.7.tgz", - "integrity": "sha512-/U4CYp1214Xrp3u3Fqr9yNynUrr5Le4y0SsJh2lMDDSbpwYSz3M2SMWQC+wqcx79cN8PQtHQIL8KnuY9M66fdg==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "commander": "^5.1.0", - "debug": "^4.1.1" - }, - "bin": { - "find-process": "bin/find-process.js" - } - }, - "node_modules/find-process/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/find-process/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/find-process/node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/find-process/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-process/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -13104,15 +12320,6 @@ "node": ">= 0.6" } }, - "node_modules/fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -13209,6 +12416,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -13281,33 +12489,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-uri": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", - "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/get-uri/node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -13441,6 +12622,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -13512,15 +12694,6 @@ "graphology-types": ">=0.23.0" } }, - "node_modules/graphql": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", - "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" - } - }, "node_modules/gremlin": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/gremlin/-/gremlin-3.7.2.tgz", @@ -13660,12 +12833,6 @@ "he": "bin/he" } }, - "node_modules/headers-polyfill": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", - "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", - "dev": true - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -13681,18 +12848,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -14186,23 +13341,6 @@ "node": ">= 0.4" } }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -14471,12 +13609,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-node-process": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", - "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", - "dev": true - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -14726,15 +13858,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -16059,76 +15182,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/jest-dev-server": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/jest-dev-server/-/jest-dev-server-10.0.0.tgz", - "integrity": "sha512-FtyBBDxrAIfTX3hyKSOwj5KU6Z7fFLew5pQYOFpwyf+qpPpULL8aYxtsFkbkAwcs+Mb7qhcNbVLeiWsLOd7CKw==", - "dev": true, - "dependencies": { - "chalk": "^4.1.2", - "cwd": "^0.10.0", - "find-process": "^1.4.7", - "prompts": "^2.4.2", - "spawnd": "^10.0.0", - "tree-kill": "^1.2.2", - "wait-on": "^7.2.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/jest-dev-server/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-dev-server/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-dev-server/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-dev-server/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-diff": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", @@ -16753,294 +15806,12 @@ "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-puppeteer": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-puppeteer/-/jest-environment-puppeteer-10.0.1.tgz", - "integrity": "sha512-FxMzVRyqieQqSy5CPWiwdK5t9dkRHid5eoRTVa8RtYeXLlpW6lU0dAmxEfPkdnDVCiPUhC2APeKOXq0J72bgag==", - "dev": true, - "dependencies": { - "chalk": "^4.1.2", - "cosmiconfig": "^8.3.6", - "deepmerge": "^4.3.1", - "jest-dev-server": "^10.0.0", - "jest-environment-node": "^29.7.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/jest-environment-puppeteer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dev": true, - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/jest-environment-puppeteer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-puppeteer/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-environment-puppeteer/node_modules/supports-color": { + "node_modules/jest-environment-node/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -17899,22 +16670,6 @@ } } }, - "node_modules/jest-puppeteer": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/jest-puppeteer/-/jest-puppeteer-10.0.1.tgz", - "integrity": "sha512-FzC35XbqeuQEt1smXh1EOqhJaRkWqJkyWDMfGkcZ8C59QHXeJ7F/iOmiNqYi6l/OsycUuOPCk+IkjfGfS9YbrQ==", - "dev": true, - "dependencies": { - "expect-puppeteer": "^10.0.0", - "jest-environment-puppeteer": "^10.0.1" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "puppeteer": ">=19" - } - }, "node_modules/jest-regex-util": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", @@ -19488,16 +18243,6 @@ "node": ">=8" } }, - "node_modules/jest-webgl-canvas-mock": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/jest-webgl-canvas-mock/-/jest-webgl-canvas-mock-2.5.3.tgz", - "integrity": "sha512-aE5ym/VV8hpJgsu/zzmvJ/bhD4vnfAM9TY6TRQxQAy9lo3hxtNfa2rbLFt3Qx31spQd5fhlQHO58Aey6oFDxAA==", - "dev": true, - "dependencies": { - "cssfontparser": "^1.2.1", - "moo-color": "^1.0.2" - } - }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -19645,19 +18390,6 @@ "jiti": "bin/jiti.js" } }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "dev": true, - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -19678,11 +18410,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" - }, "node_modules/jsdom": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", @@ -19798,6 +18525,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -20461,11 +19189,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" - }, "node_modules/mnemonist": { "version": "0.39.8", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", @@ -20490,215 +19213,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "license": "MIT" }, - "node_modules/msw": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.3.1.tgz", - "integrity": "sha512-ocgvBCLn/5l3jpl1lssIb3cniuACJLoOfZu01e3n5dbJrpA5PeeWn28jCLgQDNt6d7QT8tF2fYRzm9JoEHtiig==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@bundled-es-modules/cookie": "^2.0.0", - "@bundled-es-modules/statuses": "^1.0.1", - "@inquirer/confirm": "^3.0.0", - "@mswjs/cookies": "^1.1.0", - "@mswjs/interceptors": "^0.29.0", - "@open-draft/until": "^2.1.0", - "@types/cookie": "^0.6.0", - "@types/statuses": "^2.0.4", - "chalk": "^4.1.2", - "graphql": "^16.8.1", - "headers-polyfill": "^4.0.2", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.2", - "path-to-regexp": "^6.2.0", - "strict-event-emitter": "^0.5.1", - "type-fest": "^4.9.0", - "yargs": "^17.7.2" - }, - "bin": { - "msw": "cli/index.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mswjs" - }, - "peerDependencies": { - "typescript": ">= 4.7.x" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/msw/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/msw/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/msw/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/msw/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/msw/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/msw/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/msw/node_modules/path-to-regexp": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", - "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", - "dev": true - }, - "node_modules/msw/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/msw/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/msw/node_modules/type-fest": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.21.0.tgz", - "integrity": "sha512-ADn2w7hVPcK6w1I0uWnM//y1rLXZhzB9mr0a3OirzclKF1Wp6VzevUmzz/NRAWunOT6E8HrnpGY7xOfc6K57fA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/msw/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/msw/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/msw/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/msw/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/multicast-dns": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", @@ -20713,15 +19227,6 @@ "multicast-dns": "cli.js" } }, - "node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -20815,14 +19320,6 @@ "dev": true, "license": "MIT" }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -21210,6 +19707,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -21267,21 +19765,6 @@ "node": ">= 0.8.0" } }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/outvariant": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", - "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==", - "dev": true - }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -21328,76 +19811,11 @@ "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pac-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", - "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.5", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 14" + "node": ">=6" } }, "node_modules/pandemonium": { @@ -21460,15 +19878,6 @@ "node": ">= 0.10" } }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -21591,11 +20000,6 @@ "node": ">=8" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" - }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -23194,14 +21598,6 @@ "dev": true, "license": "MIT" }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/promise": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", @@ -23256,67 +21652,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "engines": { - "node": ">=12" - } - }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -23337,15 +21672,6 @@ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -23356,80 +21682,6 @@ "node": ">=6" } }, - "node_modules/puppeteer": { - "version": "22.12.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.12.1.tgz", - "integrity": "sha512-1GxY8dnEnHr1SLzdSDr0FCjM6JQfAh2E2I/EqzeF8a58DbGVk9oVjj4lFdqNoVbpgFSpAbz7VER9St7S1wDpNg==", - "hasInstallScript": true, - "dependencies": { - "@puppeteer/browsers": "2.2.3", - "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1299070", - "puppeteer-core": "22.12.1" - }, - "bin": { - "puppeteer": "lib/esm/puppeteer/node/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core": { - "version": "22.12.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.12.1.tgz", - "integrity": "sha512-XmqeDPVdC5/3nGJys1jbgeoZ02wP0WV1GBlPtr/ULRbGXJFuqgXMcKQ3eeNtFpBzGRbpeoCGWHge1ZWKWl0Exw==", - "dependencies": { - "@puppeteer/browsers": "2.2.3", - "chromium-bidi": "0.5.24", - "debug": "^4.3.5", - "devtools-protocol": "0.0.1299070", - "ws": "^8.17.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/puppeteer/node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/puppeteer/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -23483,11 +21735,6 @@ ], "license": "MIT" }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" - }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -25476,6 +23723,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -25556,47 +23804,6 @@ "node": ">=8" } }, - "node_modules/resolve-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "integrity": "sha512-QxMPqI6le2u0dCLyiGzgy92kjkkL6zO0XyvHzjdTNH3zM6e5Hz3BwG6+aEyNgiQ5Xz6PwTwgQEj3U50dByPKIA==", - "dev": true, - "dependencies": { - "expand-tilde": "^1.2.2", - "global-modules": "^0.2.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/global-modules": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", - "integrity": "sha512-JeXuCbvYzYXcwE6acL9V2bAOeSIGl4dD+iwLY9iUx2VBJJ80R18HCn+JCwHM9Oegdfya3lEkGCdaRkSyc10hDA==", - "dev": true, - "dependencies": { - "global-prefix": "^0.1.4", - "is-windows": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/global-prefix": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", - "integrity": "sha512-gOPiyxcD9dJGCEArAhF4Hd0BAqvAe/JzERP7tYumE4yIkmIedPUVXcJFWbV3/p/ovIIvKjkrTk+f1UVkq7vvbw==", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.0", - "ini": "^1.3.4", - "is-windows": "^0.2.0", - "which": "^1.2.12" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -26373,15 +24580,6 @@ "node": ">=8" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -26404,43 +24602,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", - "dependencies": { - "agent-base": "^7.1.1", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -26538,31 +24699,6 @@ "dev": true, "license": "MIT" }, - "node_modules/spawnd": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-10.0.0.tgz", - "integrity": "sha512-6GKcakMTryb5b1SWCvdubCDHEsR2k+5VZUD5G19umZRarkvj1RyCGyizcqhjewI7cqZo8fTVD8HpnDZbVOLMtg==", - "dev": true, - "dependencies": { - "signal-exit": "^4.1.0", - "tree-kill": "^1.2.2" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/spawnd/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -26806,25 +24942,6 @@ "node": ">= 0.8" } }, - "node_modules/streamx": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", - "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", - "dependencies": { - "fast-fifo": "^1.3.2", - "queue-tick": "^1.0.1", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, - "node_modules/strict-event-emitter": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", - "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", - "dev": true - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -27049,6 +25166,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -27506,29 +25624,6 @@ "node": ">=6" } }, - "node_modules/tar-fs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.5.tgz", - "integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" - } - }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -27682,14 +25777,6 @@ "node": ">=8" } }, - "node_modules/text-decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", - "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", - "dependencies": { - "b4a": "^1.6.4" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -27726,11 +25813,6 @@ "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", "dev": true }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -27876,6 +25958,7 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, "license": "0BSD" }, "node_modules/tsutils": { @@ -28040,7 +26123,7 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -28162,38 +26245,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "node_modules/unbzip2-stream/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/underscore": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", @@ -28213,7 +26264,7 @@ "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -28277,6 +26328,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -28360,11 +26412,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==" - }, "node_modules/use-sync-external-store": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", @@ -28537,34 +26584,6 @@ "node": ">=14" } }, - "node_modules/wait-on": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", - "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==", - "dev": true, - "dependencies": { - "axios": "^1.6.1", - "joi": "^17.11.0", - "lodash": "^4.17.21", - "minimist": "^1.2.8", - "rxjs": "^7.8.1" - }, - "bin": { - "wait-on": "bin/wait-on" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/wait-on/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -29506,6 +27525,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { @@ -29669,15 +27689,6 @@ "node": ">=4" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -29690,26 +27701,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } } } diff --git a/package.json b/package.json index 1f59822..c2e51e5 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "graphology-types": "^0.24.7", "gremlin": "^3.4.4", "lodash": "^4.17.15", - "puppeteer": "^22.12.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-edit-text": "^5.1.1", @@ -78,9 +77,6 @@ "jest-axe": "^9.0.0", "jest-canvas-mock": "^2.5.2", "jest-environment-jsdom": "^29.7.0", - "jest-puppeteer": "^10.0.1", - "jest-webgl-canvas-mock": "^2.5.3", - "msw": "^2.3.1", "npm-run-all": "^4.1.5", "react-scripts": "^5.0.1", "react-test-renderer": "^18.3.1", diff --git a/test/components/Details/DetailsComponent.test.tsx b/test/components/Details/DetailsComponent.test.tsx index 5f84abf..671e4e2 100644 --- a/test/components/Details/DetailsComponent.test.tsx +++ b/test/components/Details/DetailsComponent.test.tsx @@ -5,13 +5,10 @@ import { render, screen, waitFor, fireEvent, act } from '@testing-library/react' import '@testing-library/jest-dom'; import { SidebarComponent } from '../../../src/components/Details/SidebarComponent'; import userEvent from '@testing-library/user-event'; -import { defaultNodeLabel, EdgeData, NodeData } from "../../../src/logics/utils"; -import { setupStore } from "../../../src/app/store"; +import { EdgeData, NodeData } from "../../../src/logics/utils"; import axios from 'axios'; import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; -import { Store, AnyAction } from 'redux'; -import { updateNode } from '../../../src/reducers/graphReducer'; -import { onFetchQuery } from '../../../src/logics/actionHelper'; + jest.mock('../../../src/logics/graph', () => ({ applyLayout: jest.fn(), getNodePositions: jest.fn(), @@ -23,11 +20,6 @@ jest.mock("axios", () => ({ ...jest.requireActual("axios"), post: jest.fn(), })); -// jest.mock('../../logics/actionHelper', () => ({ -// onFetchQuery: jest.fn(), -// })); - - const selectedNodeDummy: NodeData = { id: 1, label: 'Bob', properties: { name: "Bob", age: "21" }, edges: [], type: 'person', x: 0, y: 0 }; const selectedEdgeDummy: EdgeData = { id: 1, from: 2, to: 3, label: 'created', properties: { name: "dummy edge", age: "0" }, type: 'created' }; @@ -48,10 +40,7 @@ type State = { nodes: NodeData[], edges: NodeData[], }; - - }; - const initialState: State = { gremlin: { host: 'localhost', @@ -72,10 +61,7 @@ const initialState: State = { } }; - - describe('node tests', () => { - test('renders node details correctly', async () => { const mockStore = configureStore(); let store = mockStore(initialState); @@ -94,8 +80,6 @@ describe('node tests', () => { expect(screen.getByText('21')).toBeInTheDocument(); }); - - test("sends axios post to delete a node property", async () => { let user = userEvent.setup(); const mockStore = configureStore(); @@ -131,7 +115,6 @@ describe('node tests', () => { }); }) - test(`clicking "Traverse Out Edges" sends addNodes`, async () => { let user = userEvent.setup(); const mockStore = configureStore(); @@ -159,7 +142,6 @@ describe('node tests', () => { }) - test(`clicking "Traverse In Edges" sends addNodes`, async () => { let user = userEvent.setup(); const mockStore = configureStore(); @@ -178,7 +160,6 @@ describe('node tests', () => { const detailsTab = screen.getByRole('tab', { name: 'Details' }); await user.click(detailsTab); - const button = screen.getByRole('button', { name: /Traverse In Edges/i }); await user.click(button); await waitFor(() => { @@ -187,10 +168,8 @@ describe('node tests', () => { payload: expect.anything() })); }); - }) - test('node clicking add property and confirming calls axios post with right arguments', async () => { let user = userEvent.setup(); const mockStore = configureStore(); @@ -235,20 +214,10 @@ describe('node tests', () => { }) ); }); - - - }); - - }) - - - - describe("edge tests", () => { - test('renders edge details correctly', async () => { const mockStore = configureStore(); let store = mockStore({ ...initialState, graph: { selectedNode: null, selectedEdge: selectedEdgeDummy } }); @@ -304,8 +273,6 @@ describe("edge tests", () => { }); }) - - test('edge clicking add property and confirming calls axios post with right arguments', async () => { let user = userEvent.setup(); const mockStore = configureStore(); @@ -350,9 +317,5 @@ describe("edge tests", () => { }) ); }); - - - }); - -}) +}) \ No newline at end of file diff --git a/test/components/Details/QueryComponent.test.tsx b/test/components/Details/QueryComponent.test.tsx index d70afb2..ce769cf 100644 --- a/test/components/Details/QueryComponent.test.tsx +++ b/test/components/Details/QueryComponent.test.tsx @@ -1,13 +1,11 @@ import React from 'react'; import { Provider } from 'react-redux'; import configureStore from 'redux-mock-store'; -import { render, screen, waitFor, fireEvent, act } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import Query from '../../../src/components/Details/QueryComponent'; import userEvent from '@testing-library/user-event'; import axios from 'axios'; -import { COMMON_GREMLIN_ERROR, QUERY_ENDPOINT } from "../../../src/constants"; -import { selectGremlin, setError, setQuery, } from '../../../src/reducers/gremlinReducer'; import { setupStore } from '../../../src/app/store'; import { SidebarComponent } from '../../../src/components/Details/SidebarComponent'; @@ -19,10 +17,9 @@ jest.mock('../../../src/logics/graph', () => ({ applyLayout: jest.fn(), getNodePositions: jest.fn(), setNodePositions: jest.fn(), - layoutOptions: ['random', 'hierarchical'] + layoutOptions: ['random', 'hierarchical'] })); - const initialState = { gremlin: { host: 'localhost', @@ -53,7 +50,6 @@ const initialState = { } }; - test('queryComponent renders with g.V()', () => { const mockStore = configureStore(); let store = mockStore(initialState); @@ -75,7 +71,7 @@ test('adding one node query sends axios post and dispatches addNode', async () = const mockStore = configureStore(); let store = mockStore(initialState); store.dispatch = jest.fn(); - jest.spyOn(store, 'dispatch'); + jest.spyOn(store, 'dispatch'); render( @@ -89,54 +85,37 @@ test('adding one node query sends axios post and dispatches addNode', async () = const button = screen.getByText('Execute'); await userEvent.click(button); - await waitFor(() => { - expect(axios.post).toHaveBeenCalledTimes(1); + expect(axios.post).toHaveBeenCalled(); }); - await waitFor(() => { expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ type: 'graph/addNodes', - payload: expect.anything() - })); + payload: expect.anything() + })); }); - }); test('executed query is added into query history list', async () => { - // const mockStore = configureStore(); - // let store = mockStore(initialState); - // store.dispatch = jest.fn(); const mockedAxios = axios as jest.Mocked; mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); let user = userEvent.setup(); let store = setupStore(); - // jest.spyOn(store, 'dispatch'); render( { }} /> ); - // const detailsTab = screen.getByRole('tab', { name: 'Details' }); - // await act(async () => { - // fireEvent.click(detailsTab); - // }) const textField = screen.getByLabelText('gremlin query'); const newQuery = 'g.addV("person").property("name", "Alice")'; - // await act(async () => { - // fireEvent.change(textField, { target: { value: newQuery } }); - // }) await user.clear(textField); await user.type(textField, newQuery); const button = screen.getByText('Execute'); await userEvent.click(button); - // expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ - // type: 'gremlin/setQuery', - // payload: expect.anything() - // })); + expect(store.getState().gremlin.query).toBe(newQuery); await waitFor(() => { const matchingElements = screen.getAllByText(newQuery); @@ -144,41 +123,23 @@ test('executed query is added into query history list', async () => { }); }); - - - test('execute then clear', async () => { - // const mockStore = configureStore(); - // let store = mockStore(initialState); - // store.dispatch = jest.fn(); const mockedAxios = axios as jest.Mocked; mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); - let user = userEvent.setup(); let store = setupStore(); - // jest.spyOn(store, 'dispatch'); render( { }} /> ); - // const detailsTab = screen.getByRole('tab', { name: 'Details' }); - // await act(async () => { - // fireEvent.click(detailsTab); - // }) - const button = screen.getByText('Execute'); await userEvent.click(button); - // expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ - // type: 'gremlin/setQuery', - // payload: expect.anything() - // })); const clearButton = screen.getByText('Clear Graph'); await userEvent.click(clearButton); await waitFor(() => { const matchingElements = screen.getAllByText("g.V()"); expect(matchingElements.length).toBe(1); }); - }); \ No newline at end of file diff --git a/test/components/Details/SavedQueriesComponent.test.tsx b/test/components/Details/SavedQueriesComponent.test.tsx index 5a045c4..7a31307 100644 --- a/test/components/Details/SavedQueriesComponent.test.tsx +++ b/test/components/Details/SavedQueriesComponent.test.tsx @@ -26,10 +26,9 @@ jest.mock("axios", () => ({ })); const customQueries = { - "get node with name marko" : "g.V().has('name', 'marko')", - "get person nodes that marko has outgoing edges to" : "g.V().has('name', 'marko').out().hasLabel('person')" - } -// Replace SAVED_QUERIES import in SavedQueries component with customQueries for testing + "get node with name marko": "g.V().has('name', 'marko')", + "get person nodes that marko has outgoing edges to": "g.V().has('name', 'marko').out().hasLabel('person')" +} jest.mock('../../../src/constants', () => ({ SAVED_QUERIES: customQueries, INITIAL_LABEL_MAPPINGS: [] @@ -52,8 +51,6 @@ type State = { nodes: NodeData[], edges: NodeData[], }; - - }; const initialState: State = { @@ -76,7 +73,6 @@ const initialState: State = { } }; - test("clicking play button for first saved query sends axios", async () => { let user = userEvent.setup(); const mockStore = configureStore(); @@ -97,7 +93,7 @@ test("clicking play button for first saved query sends axios", async () => { await user.click(firstQueryButton); - const query = "g.V().has('name', 'marko')"; + const query = "g.V().has('name', 'marko')"; await waitFor(() => { expect(mockedAxios.post).toHaveBeenCalledWith( QUERY_ENDPOINT, diff --git a/test/components/Details/SettingsComponent.test.tsx b/test/components/Details/SettingsComponent.test.tsx index f6b9331..bd27542 100644 --- a/test/components/Details/SettingsComponent.test.tsx +++ b/test/components/Details/SettingsComponent.test.tsx @@ -1,16 +1,12 @@ import React from 'react'; import { Provider } from 'react-redux'; import configureStore from 'redux-mock-store'; -import { render, screen, waitFor, fireEvent, act, within } from '@testing-library/react'; +import { render, screen, waitFor, within } from '@testing-library/react'; import '@testing-library/jest-dom'; import { SidebarComponent } from '../../../src/components/Details/SidebarComponent'; import userEvent from '@testing-library/user-event'; -import { defaultNodeLabel, EdgeData, NodeData } from "../../../src/logics/utils"; import { setupStore } from "../../../src/app/store"; import axios from 'axios'; -import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; -import { setNodePositions } from '../../../src/logics/graph'; - jest.mock('../../../src/logics/graph', () => ({ applyLayout: jest.fn(), @@ -28,7 +24,6 @@ const customQueries = { "get node with name marko": "g.V().has('name', 'marko')", "get person nodes that marko has outgoing edges to": "g.V().has('name', 'marko').out().hasLabel('person')" } -// Replace SAVED_QUERIES import in SavedQueries component with customQueries for testing jest.mock('../../../src/constants', () => ({ SAVED_QUERIES: customQueries, INITIAL_LABEL_MAPPINGS: { @@ -38,48 +33,6 @@ jest.mock('../../../src/constants', () => ({ })); -type State = { - gremlin: { - host: string; - port: string; - query: string; - }; - options: { - nodeLabels: string[]; - nodeLimit: number; - queryHistory: string[]; - }; - graph: { - selectedNode: NodeData | null; - selectedEdge: EdgeData | null; - nodes: NodeData[], - edges: NodeData[], - }; - - -}; - -const initialState: State = { - gremlin: { - host: 'localhost', - port: '8182', - query: 'g.V()' - }, - options: { - nodeLabels: [], - nodeLimit: 50, - queryHistory: [] - }, - - graph: { - selectedNode: null, - selectedEdge: null, - nodes: [], - edges: [], - } -}; - - test("refreshing a node label sends update/refresheNodeLabels dispatch", async () => { let user = userEvent.setup(); const mockStore = configureStore(); @@ -94,29 +47,18 @@ test("refreshing a node label sends update/refresheNodeLabels dispatch", async ( ); - const settingsTab = screen.getByRole('tab', { name: 'Settings' }); await user.click(settingsTab); - // expect(screen.getByText('name')).toBeInTheDocument(); - - const labelTextField = screen.getByTestId(`label-field-0`); - expect(labelTextField).toBeInTheDocument(); - - - await user.click(labelTextField); - - // await user.clear(labelTextField) await user.type(labelTextField, '{backspace}{backspace}{backspace}{backspace}'); await user.type(labelTextField, 'age'); const button = screen.getByRole('button', { name: /Refresh/i }); await user.click(button); - await waitFor(() => { expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ type: 'options/editNodeLabel', @@ -126,63 +68,37 @@ test("refreshing a node label sends update/refresheNodeLabels dispatch", async ( type: 'graph/refreshNodeLabels', payload: expect.anything() })); - - }) - }); - - - - - test("save workspace as 'saved workspace' and confirm it appears as one of the options in load workspace", async () => { let user = userEvent.setup(); - const mockStore = configureStore(); let store = setupStore(); const mockedAxios = axios as jest.Mocked; - // store.dispatch = jest.fn(); mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); jest.spyOn(store, 'dispatch'); - render( { }} /> ); - - const settingsTab = screen.getByRole('tab', { name: 'Settings' }); await user.click(settingsTab); - // expect(screen.getByText('name')).toBeInTheDocument(); - - - const labelTextField = screen.getByTestId(`label-field-0`); - - expect(labelTextField).toBeInTheDocument(); - - - - await user.click(labelTextField); - - - const saveWorkspaceButton = screen.getByRole('button', { name: /Save Workspace/i }); await user.click(saveWorkspaceButton); - const workspaceNameInput = screen.getByRole('textbox', { name: 'Workspace Name' }); await user.click(workspaceNameInput); await user.type(workspaceNameInput, 'saved workspace'); const saveButton = screen.getByRole('button', { name: /Save/i }); await user.click(saveButton); + await waitFor(() => { expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); }); + const loadWorkspaceButton = screen.getByRole('button', { name: /Load Workspace/i }); await user.click(loadWorkspaceButton); - const dropdown = within(await screen.findByTestId("workspace-select")).getByRole( "combobox", ); @@ -190,25 +106,14 @@ test("save workspace as 'saved workspace' and confirm it appears as one of the o expect( await screen.findByRole("option", { name: "saved workspace" }), ).toBeInTheDocument(); - const saved_workspace = screen.getByRole("option", { name: "saved workspace" }); await user.click(saved_workspace); const loadButton = screen.getByRole('button', { name: /Load/i }); await user.click(loadButton); - - - - - }) - - - - -test("refreshing a node label sends update/refresheNodeLabels dispatch", async () => { +test("refreshing a node label sends editNodeLabel/refresheNodeLabels dispatch", async () => { let user = userEvent.setup(); - const mockStore = configureStore(); let store = setupStore(); const mockedAxios = axios as jest.Mocked; store.dispatch = jest.fn(); @@ -221,10 +126,10 @@ test("refreshing a node label sends update/refresheNodeLabels dispatch", async ( const settingsTab = screen.getByRole('tab', { name: 'Settings' }); await user.click(settingsTab); + const labelTextField = screen.getByTestId(`label-field-0`); expect(labelTextField).toBeInTheDocument(); await user.click(labelTextField); - // await user.clear(labelTextField) await user.type(labelTextField, '{backspace}{backspace}{backspace}{backspace}'); await user.type(labelTextField, 'age'); const button = screen.getByRole('button', { name: /Refresh/i }); @@ -242,10 +147,8 @@ test("refreshing a node label sends update/refresheNodeLabels dispatch", async ( }) }); - test("change host, port, nodelimit, layout sends dispatches", async () => { let user = userEvent.setup(); - const mockStore = configureStore(); let store = setupStore(); const mockedAxios = axios as jest.Mocked; jest.spyOn(store, 'dispatch'); @@ -282,14 +185,6 @@ test("change host, port, nodelimit, layout sends dispatches", async () => { type: 'options/setNodeLimit', payload: "10" })); - - - - - - - - }); diff --git a/test/components/Details/TableComponent.test.tsx b/test/components/Details/TableComponent.test.tsx index be464ca..682fc21 100644 --- a/test/components/Details/TableComponent.test.tsx +++ b/test/components/Details/TableComponent.test.tsx @@ -1,15 +1,13 @@ import React from 'react'; import { Provider } from 'react-redux'; import configureStore from 'redux-mock-store'; -import { render, screen, waitFor, fireEvent, act, within } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import { SidebarComponent } from '../../../src/components/Details/SidebarComponent'; import userEvent from '@testing-library/user-event'; -import { defaultNodeLabel, EdgeData, NodeData } from "../../../src/logics/utils"; +import { EdgeData, NodeData } from "../../../src/logics/utils"; import { setupStore } from "../../../src/app/store"; import axios from 'axios'; -import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; -import { setNodePositions } from '../../../src/logics/graph'; import { addNodes, addEdges } from '../../../src/reducers/graphReducer' @@ -150,9 +148,6 @@ const edges = [ "type": "knows" } ] -const nodesDummy: NodeData[] = [{ id: 1, label: 'Bob', properties: { name: "Bob", age: "21" }, edges: [], type: 'person', x: 0, y: 0 }]; -const edgesDummy: EdgeData[] = [{ id: 1, from: 2, to: 3, label: 'created', properties: { name: "dummy edge", age: "0" }, type: 'created' }]; -// Replace SAVED_QUERIES import in SavedQueries component with customQueries for testing jest.mock('../../../src/constants', () => ({ INITIAL_LABEL_MAPPINGS: { person: 'name' @@ -161,74 +156,27 @@ jest.mock('../../../src/constants', () => ({ })); -type State = { - gremlin: { - host: string; - port: string; - query: string; - }; - options: { - nodeLabels: string[]; - nodeLimit: number; - queryHistory: string[]; - }; - graph: { - selectedNode: NodeData | null; - selectedEdge: EdgeData | null; - nodes: NodeData[], - edges: NodeData[], - }; - - -}; - -const initialState: State = { - gremlin: { - host: 'localhost', - port: '8182', - query: 'g.V()' - }, - options: { - nodeLabels: [], - nodeLimit: 50, - queryHistory: [] - }, - - graph: { - selectedNode: null, - selectedEdge: null, - nodes: [], - edges: [], - } -}; - - test("dispatch nodes and edges and confirm Bob and Max appears in table", async () => { let user = userEvent.setup(); - const mockStore = configureStore(); let store = setupStore({}); jest.spyOn(store, 'dispatch'); store.dispatch(addNodes(nodes)); store.dispatch(addEdges(edges)); const mockedAxios = axios as jest.Mocked; mockedAxios.post.mockResolvedValue({ data: 'Mocked success' }); - render( { }} /> ); - const tableTab = screen.getByRole('tab', { name: 'Table View' }); await user.click(tableTab); expect(screen.getByText('Ava')).toBeInTheDocument(); expect(screen.getByText('Bob')).toBeInTheDocument(); - }); - test("test that expand row works and shows age", async () => { let user = userEvent.setup(); let store = setupStore({}); @@ -244,7 +192,6 @@ test("test that expand row works and shows age", async () => { ); - const tableTab = screen.getByRole('tab', { name: 'Table View' }); await user.click(tableTab); @@ -270,13 +217,11 @@ test("test click sort button twice should sort descending then ascending by name ); - const tableTab = screen.getByRole('tab', { name: 'Table View' }); await user.click(tableTab); const sortButton = screen.getByRole('button', { name: 'Label' }); - // Click the sort label await user.click(sortButton); const headersDescending = screen.getAllByRole('rowheader'); @@ -308,7 +253,6 @@ test("test that rows per page can be set from default(10) to 5", async () => { ); - const tableTab = screen.getByRole('tab', { name: 'Table View' }); await user.click(tableTab); @@ -337,7 +281,6 @@ test("dechecking node selects edge and shows 1 edge", async () => { ); - const tableTab = screen.getByRole('tab', { name: 'Table View' }); await user.click(tableTab); diff --git a/test/components/ModalDialog/ModalDialogComponent.test.tsx b/test/components/ModalDialog/ModalDialogComponent.test.tsx index 96de959..d8c9512 100644 --- a/test/components/ModalDialog/ModalDialogComponent.test.tsx +++ b/test/components/ModalDialog/ModalDialogComponent.test.tsx @@ -1,119 +1,83 @@ import React from 'react'; import { Provider } from 'react-redux'; -import configureStore from 'redux-mock-store'; -import { render, screen, waitFor, fireEvent, act, within } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; -import { App } from '../../../src/App'; import userEvent from '@testing-library/user-event'; -import { defaultNodeLabel, EdgeData, NodeData, extractEdgesAndNodes, storeSuggestions } from "../../../src/logics/utils"; +import { EdgeData, NodeData } from "../../../src/logics/utils"; import { setupStore } from "../../../src/app/store"; import axios from 'axios'; -import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; -import { setNodePositions } from '../../../src/logics/graph'; -import { addNodes, addEdges } from '../../../src/reducers/graphReducer' import { openNodeDialog, openEdgeDialog, setSuggestions } from '../../../src/reducers/dialogReducer'; -import { setNodeLabels } from '../../../src/reducers/optionReducer'; import { ModalDialogComponent } from '../../../src/components/ModalDialog/ModalDialogComponent'; -// jest.mock('../../../src/logics/graph', () => ({ -// applyLayout: jest.fn(), -// getNodePositions: jest.fn(), -// setNodePositions: jest.fn(), -// layoutOptions: ['force-directed', 'hierarchical'] -// })); jest.mock('../../../src/logics/graphImpl/sigmaImpl', () => ({ - __esModule: true, // Ensure it's treated as a module + __esModule: true, // Ensure it's treated as a module })); jest.mock("axios", () => ({ - ...jest.requireActual("axios"), - post: jest.fn(), + ...jest.requireActual("axios"), + post: jest.fn(), })); jest.mock('../../../src/constants', () => ({ - INITIAL_LABEL_MAPPINGS: { - person: 'name' - }, - GRAPH_IMPL: "vis" + INITIAL_LABEL_MAPPINGS: { + person: 'name' + }, + GRAPH_IMPL: "vis" })); -type State = { - gremlin: { - host: string; - port: string; - query: string; - }; - options: { - nodeLabels: string[]; - nodeLimit: number; - queryHistory: string[]; - }; - graph: { - selectedNode: NodeData | null; - selectedEdge: EdgeData | null; - nodes: NodeData[], - edges: NodeData[], - }; - - -}; - test("test modalDialog renders", async () => { - let user = userEvent.setup(); - let store = setupStore({}); - jest.spyOn(store, 'dispatch'); - store.dispatch(openNodeDialog({ x: 200, y: 200 })); - render( - - - - ); - expect(screen.queryByRole('dialog')).toBeInTheDocument(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); + store.dispatch(openNodeDialog({ x: 200, y: 200 })); + render( + + + + ); + expect(screen.queryByRole('dialog')).toBeInTheDocument(); }) test("test node modalDialog renders with suggested", async () => { - let user = userEvent.setup(); - let store = setupStore({}); - jest.spyOn(store, 'dispatch'); + let user = userEvent.setup(); + let store = setupStore({}); + jest.spyOn(store, 'dispatch'); - // store.dispatch(setNodeLabels(nodeLabels)); - store.dispatch(openNodeDialog({ x: 200, y: 200 })); - store.dispatch(setSuggestions({ - "node": { - "types": [ - "person" - ], - "labels": { - "person": [ - "name", - "age" - ] - } - }, - "edge": { - "types": [], - "labels": {} + // store.dispatch(setNodeLabels(nodeLabels)); + store.dispatch(openNodeDialog({ x: 200, y: 200 })); + store.dispatch(setSuggestions({ + "node": { + "types": [ + "person" + ], + "labels": { + "person": [ + "name", + "age" + ] } - })) - render( - - - - ); + }, + "edge": { + "types": [], + "labels": {} + } + })) + render( + + + + ); + + expect(screen.queryByRole('dialog')).toBeInTheDocument(); + expect(screen.getByDisplayValue('name')).toBeInTheDocument(); + expect(screen.getByDisplayValue('age')).toBeInTheDocument(); - expect(screen.queryByRole('dialog')).toBeInTheDocument(); - expect(screen.getByDisplayValue('name')).toBeInTheDocument(); - expect(screen.getByDisplayValue('age')).toBeInTheDocument(); - }) test("test edge modalDialog renders with suggested", async () => { let user = userEvent.setup(); let store = setupStore({}); jest.spyOn(store, 'dispatch'); - - // store.dispatch(setNodeLabels(nodeLabels)); store.dispatch(openEdgeDialog({ edgeFrom: 0, edgeTo: 1 })); store.dispatch(setSuggestions({ "node": { @@ -141,20 +105,19 @@ test("test edge modalDialog renders with suggested", async () => { })) render( - - - + + + ); - expect(screen.queryByRole('dialog')).toBeInTheDocument(); expect(screen.getByDisplayValue('length')).toBeInTheDocument(); expect(screen.getByDisplayValue('strength')).toBeInTheDocument(); - }) -test("submitting dispatches addNode", async () => { +test("submitting dialog dispatches addNode", async () => { const mockedAxios = axios as jest.Mocked; - mockedAxios.post.mockResolvedValue({ data:{ + mockedAxios.post.mockResolvedValue({ + data: { "data": [ { "id": 0, @@ -165,62 +128,47 @@ test("submitting dispatches addNode", async () => { "edges": [] } ] - }}); - - + } + }); let user = userEvent.setup(); let store = setupStore({}); jest.spyOn(store, 'dispatch'); - - // store.dispatch(setNodeLabels(nodeLabels)); store.dispatch(openNodeDialog({ x: 200, y: 200 })); - render( - - - + + + ); const inputElement = screen.getByRole('combobox', { name: /type/i }); - // await user.click(inputElement); await user.type(inputElement, "person"); const buttonElement = screen.getByRole('button', { name: /Add More../i }); await user.click(buttonElement); - const nameInput = screen.getByRole('combobox', { name: /Property Name/i }); expect(nameInput).toBeInTheDocument(); - // await user.click(inputElement2); await user.type(nameInput, "name"); await waitFor(() => { expect(nameInput).toHaveValue('name'); }); - - // const valueInput = screen.getByText('Property Value'); const valueInput = screen.getByRole('textbox', { name: /Property Value/i }); - // await user.click(inputElement3); await user.type(valueInput, "Bob"); await waitFor(() => { expect(valueInput).toHaveValue('Bob'); }); - const submitButton = screen.getByRole('button', { name: /Submit/i }); await user.click(submitButton); - await waitFor(() => { expect(store.dispatch).toHaveBeenCalledWith(expect.objectContaining({ - type: 'graph/addNodes', - payload: expect.anything() + type: 'graph/addNodes', + payload: expect.anything() })); -}); - - // Additional checks can be performed to ensure the right element is selected, if needed + }); }) - - test("cancel closes dialog", async () => { const mockedAxios = axios as jest.Mocked; - mockedAxios.post.mockResolvedValue({ data:{ + mockedAxios.post.mockResolvedValue({ + data: { "data": [ { "id": 0, @@ -231,28 +179,23 @@ test("cancel closes dialog", async () => { "edges": [] } ] - }}); - + } + }); let user = userEvent.setup(); let store = setupStore({}); jest.spyOn(store, 'dispatch'); - - // store.dispatch(setNodeLabels(nodeLabels)); store.dispatch(openNodeDialog({ x: 200, y: 200 })); - render( - - - + + + ); const cancelButton = screen.getByRole('button', { name: /Cancel/i }); await user.click(cancelButton); - await waitFor(() => { expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); }) - // Additional checks can be performed to ensure the right element is selected, if needed }) diff --git a/test/components/logics/utils.test.ts b/test/components/logics/utils.test.ts index 2550544..d20e70a 100644 --- a/test/components/logics/utils.test.ts +++ b/test/components/logics/utils.test.ts @@ -1,87 +1,47 @@ -import React from 'react'; -import { Provider } from 'react-redux'; -import configureStore from 'redux-mock-store'; -import { render, screen, waitFor, fireEvent, act, within } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import { App } from '../../../src/App'; -import userEvent from '@testing-library/user-event'; -import { defaultNodeLabel, EdgeData, extractEdgesAndNodes, NodeData } from "../../../src/logics/utils"; -import { setupStore } from "../../../src/app/store"; -import axios from 'axios'; -import { EDGE_ID_APPEND, QUERY_ENDPOINT, QUERY_RAW_ENDPOINT } from '../../../src/constants'; -import { setNodePositions } from '../../../src/logics/graph'; -import { addNodes, addEdges } from '../../../src/reducers/graphReducer' -import { openNodeDialog } from '../../../src/reducers/dialogReducer'; -import { ModalDialogComponent } from '../../../src/components/ModalDialog/ModalDialogComponent'; -import { DIALOG_TYPES } from '../../../src/components/ModalDialog/ModalDialogComponent'; -test("extractEdgesAndNodes with custom arguments returns correct nodes, edges, nodeLabels", async () => { - const argument0 : NodeData[] = [ - { - "id": 1, - "label": "person", - "properties": { - "name": "Bob", - "age": "21" - }, - "edges": [ - { - "id": "0", - "from": 1, - "to": 2, - "label": "knows", - "properties": { - "length": "2" - } - } - ] - }, - { - "id": 2, - "label": "person", - "properties": { - "name": "Max", - "age": "18" - }, - "edges": [] - }, - ] as NodeData[] - const argument1 = []; +import { extractEdgesAndNodes, NodeData } from "../../../src/logics/utils"; - - - const expectedNodes = [ - { - "id": 1, - "label": "Bob", - "properties": { - "name": "Bob", - "age": "21" - }, - "edges": [ - { - "id": "0", - "from": 1, - "to": 2, - "label": "knows", - "properties": { - "length": "2" - } - } - ], - "type": "person" - }, +test("extractEdgesAndNodes with custom arguments returns correct nodes, edges, nodeLabels", async () => { + const argument0: NodeData[] = [ + { + "id": 1, + "label": "person", + "properties": { + "name": "Bob", + "age": "21" + }, + "edges": [ { - "id": 2, - "label": "Max", + "id": "0", + "from": 1, + "to": 2, + "label": "knows", "properties": { - "name": "Max", - "age": "18" - }, - "edges": [], - "type": "person" + "length": "2" + } } ] - const expectedEdges = [ + }, + { + "id": 2, + "label": "person", + "properties": { + "name": "Max", + "age": "18" + }, + "edges": [] + }, + ] as NodeData[] + const argument1 = []; + + const expectedNodes = [ + { + "id": 1, + "label": "Bob", + "properties": { + "name": "Bob", + "age": "21" + }, + "edges": [ { "id": "0", "from": 1, @@ -89,19 +49,43 @@ test("extractEdgesAndNodes with custom arguments returns correct nodes, edges, n "label": "knows", "properties": { "length": "2" - }, - "type": "knows" + } } - ] - const expectedNodeLabels = [ - { - "type": "person", - "field": "name" - } - ] + ], + "type": "person" + }, + { + "id": 2, + "label": "Max", + "properties": { + "name": "Max", + "age": "18" + }, + "edges": [], + "type": "person" + } + ] + const expectedEdges = [ + { + "id": "0", + "from": 1, + "to": 2, + "label": "knows", + "properties": { + "length": "2" + }, + "type": "knows" + } + ] + const expectedNodeLabels = [ + { + "type": "person", + "field": "name" + } + ] - const {edges, nodes, nodeLabels} = extractEdgesAndNodes(argument0, argument1); - expect(edges).toEqual(expectedEdges); - expect(nodes).toEqual(expectedNodes); - expect(nodeLabels).toEqual(expectedNodeLabels); + const { edges, nodes, nodeLabels } = extractEdgesAndNodes(argument0, argument1); + expect(edges).toEqual(expectedEdges); + expect(nodes).toEqual(expectedNodes); + expect(nodeLabels).toEqual(expectedNodeLabels); }) \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 699ed87..a925747 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,6 +28,5 @@ }, "include": [ "./src/", - "./types/" -, "test/components/QueryComponent.test.tsx", "test/components/DetailsComponent.test.tsx" ] + "./types/" ] } From 7eaba0cc95bc03128e5201fc9cd86012de995c40 Mon Sep 17 00:00:00 2001 From: joon-at-sri Date: Fri, 19 Jul 2024 13:09:09 -0700 Subject: [PATCH 4/4] removed consolelogs --- src/reducers/graphReducer.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/reducers/graphReducer.ts b/src/reducers/graphReducer.ts index 67a4aef..d958c9b 100644 --- a/src/reducers/graphReducer.ts +++ b/src/reducers/graphReducer.ts @@ -93,10 +93,7 @@ const slice = createSlice({ return state; }, refreshNodeLabels: (state, action) => { - console.log(state); - console.log("ME") const nodeLabelMap = _.mapValues(_.keyBy(action.payload, 'type'), 'field'); - console.log(JSON.stringify(action.payload, null, 2)); state.nodes = state.nodes.map((node: any) => { if (node.type in nodeLabelMap) { const field = nodeLabelMap[node.type]; @@ -104,7 +101,6 @@ const slice = createSlice({ if (label === undefined) return { ...node, ...{ label: defaultNodeLabel(node) } } else - console.log(JSON.stringify(node, null, 2)); return { ...node, label }; }