refactor(frontend): fetch to axios (#22)
* refactor: replace fetch with axios * build(frontend): update test command to allow cli args passthrough
This commit is contained in:
parent
6965ce6ec4
commit
b77574e596
11 changed files with 154 additions and 122 deletions
|
@ -18,7 +18,7 @@ tasks:
|
||||||
test:
|
test:
|
||||||
desc: "Runs the frontend test suite."
|
desc: "Runs the frontend test suite."
|
||||||
deps: [bootstrap]
|
deps: [bootstrap]
|
||||||
cmd: yarn test
|
cmd: yarn test {{ .CLI_ARGS }}
|
||||||
dir: frontend
|
dir: frontend
|
||||||
lint:
|
lint:
|
||||||
desc: "Checks lint and formatting."
|
desc: "Checks lint and formatting."
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
preset: "ts-jest",
|
preset: "ts-jest",
|
||||||
testEnvironment: "jsdom",
|
testEnvironment: "jsdom",
|
||||||
|
setupFilesAfterEnv: ["./tests/testSetup.ts"],
|
||||||
transform: {
|
transform: {
|
||||||
"^.+\\.(ts|tsx)$": "ts-jest",
|
"^.+\\.(ts|tsx)$": "ts-jest",
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
"@mui/icons-material": "^5.14.1",
|
"@mui/icons-material": "^5.14.1",
|
||||||
"@mui/material": "^5.14.2",
|
"@mui/material": "^5.14.2",
|
||||||
"@tanstack/react-query": "^4.32.6",
|
"@tanstack/react-query": "^4.32.6",
|
||||||
|
"axios": "^1.4.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
},
|
},
|
||||||
|
@ -26,6 +27,8 @@
|
||||||
"@types/jest": "^29.5.3",
|
"@types/jest": "^29.5.3",
|
||||||
"@types/react": "^18.2.18",
|
"@types/react": "^18.2.18",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
|
"axios-mock-adapter": "^1.21.5",
|
||||||
|
"buffer": "^5.5.0||^6.0.0",
|
||||||
"jest": "^29.6.2",
|
"jest": "^29.6.2",
|
||||||
"jest-environment-jsdom": "^29.6.2",
|
"jest-environment-jsdom": "^29.6.2",
|
||||||
"parcel": "^2.9.3",
|
"parcel": "^2.9.3",
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query"
|
import { useQuery, useQueryClient } from "@tanstack/react-query"
|
||||||
|
|
||||||
import makeRequest from "./requestUtils"
|
import axios from "axios"
|
||||||
|
|
||||||
|
export const axiosWithDefaults = axios.create({
|
||||||
|
baseURL: "http://localhost:8000",
|
||||||
|
})
|
||||||
|
|
||||||
interface FileData {
|
interface FileData {
|
||||||
/* Displayed title of the item. */
|
/* Displayed title of the item. */
|
||||||
|
@ -15,21 +19,17 @@ interface FileData {
|
||||||
|
|
||||||
function useOwnFileList() {
|
function useOwnFileList() {
|
||||||
return useQuery(["file-list"], async () => {
|
return useQuery(["file-list"], async () => {
|
||||||
const response = await makeRequest<Array<FileData>>(
|
const response = await axiosWithDefaults.get<Array<FileData>>("/files/")
|
||||||
"http://localhost:8000/files/",
|
|
||||||
)
|
|
||||||
|
|
||||||
return response.json
|
return response.data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function useFileDetails(fileId: string) {
|
function useFileDetails(fileId: string) {
|
||||||
return useQuery(["file-details", fileId], async () => {
|
return useQuery(["file-details", fileId], async () => {
|
||||||
const response = await makeRequest<FileData>(
|
const response = await axiosWithDefaults.get<FileData>(`/files/${fileId}/`)
|
||||||
`http://localhost:8000/files/${fileId}/`,
|
|
||||||
)
|
|
||||||
|
|
||||||
return response.json
|
return response.data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,13 +43,12 @@ function useFileMutations(): {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
const deleteFile = async (fileId: string): Promise<FileData> => {
|
const deleteFile = async (fileId: string): Promise<FileData> => {
|
||||||
const response = await makeRequest<FileData>(
|
const response = await axiosWithDefaults.delete<FileData>(
|
||||||
`http://localhost:8000/files/${fileId}`,
|
`/files/${fileId}/`,
|
||||||
{ method: "DELETE" },
|
|
||||||
)
|
)
|
||||||
|
|
||||||
queryClient.invalidateQueries({ queryKey: ["file-list"] })
|
queryClient.invalidateQueries({ queryKey: ["file-list"] })
|
||||||
return response.json
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
return { deleteFile }
|
return { deleteFile }
|
||||||
|
@ -62,12 +61,12 @@ async function uploadFile(file: File) {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append("file", file)
|
formData.append("file", file)
|
||||||
|
|
||||||
const response = await makeRequest<FileData>("http://localhost:8000/files/", {
|
const response = await axiosWithDefaults.postForm<FileData>(
|
||||||
method: "POST",
|
"/files/",
|
||||||
body: formData,
|
formData,
|
||||||
})
|
)
|
||||||
|
|
||||||
return response.json
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export { useOwnFileList, useFileDetails, useFileMutations, uploadFile }
|
export { useOwnFileList, useFileDetails, useFileMutations, uploadFile }
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Request utilities.
|
|
||||||
*
|
|
||||||
* To avoid relying too much on the `fetch` global directly and to facilitate
|
|
||||||
* testing, this utility abstracts the logic that makes network requests.
|
|
||||||
*/
|
|
||||||
|
|
||||||
interface RequestOptions {
|
|
||||||
method: string
|
|
||||||
body?: FormData | string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Response<ResponseSchema> {
|
|
||||||
status: number
|
|
||||||
json: ResponseSchema
|
|
||||||
}
|
|
||||||
|
|
||||||
type MakeRequestFn<Schema> = (
|
|
||||||
url: string,
|
|
||||||
opts?: RequestOptions,
|
|
||||||
) => Promise<Response<Schema>>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Wrapper for the logic that makes network requests.
|
|
||||||
*
|
|
||||||
* This is primarily done to make testing a bit easier, but also to centralize
|
|
||||||
* shared concerns across requests (i.e. common headers and such).
|
|
||||||
*/
|
|
||||||
async function makeRequest<ResponseSchema>(
|
|
||||||
url: string,
|
|
||||||
options?: RequestOptions,
|
|
||||||
): Promise<Response<ResponseSchema>> {
|
|
||||||
const response = await fetch(url, { ...(options ?? {}) })
|
|
||||||
|
|
||||||
const json = await response.json()
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: response.status,
|
|
||||||
json: json,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { RequestOptions, Response, MakeRequestFn }
|
|
||||||
|
|
||||||
export default makeRequest
|
|
|
@ -3,9 +3,8 @@ import { within } from "@testing-library/dom"
|
||||||
import userEvent from "@testing-library/user-event"
|
import userEvent from "@testing-library/user-event"
|
||||||
import { type UseQueryResult } from "@tanstack/react-query"
|
import { type UseQueryResult } from "@tanstack/react-query"
|
||||||
|
|
||||||
import { renderWithContexts as render, applyMakeRequestMock } from "./helpers"
|
import { renderWithContexts as render, getAxiosMockAdapter } from "./helpers"
|
||||||
import FileDetails from "../src/components/FileDetails"
|
import FileDetails from "../src/components/FileDetails"
|
||||||
import * as requestUtil from "../src/queries/requestUtils"
|
|
||||||
import { type FileData } from "../src/queries/files"
|
import { type FileData } from "../src/queries/files"
|
||||||
import * as fileQueries from "../src/queries/files"
|
import * as fileQueries from "../src/queries/files"
|
||||||
import * as locationContextUtils from "../src/contexts/LocationContext"
|
import * as locationContextUtils from "../src/contexts/LocationContext"
|
||||||
|
@ -18,12 +17,11 @@ describe("FileDetails", () => {
|
||||||
id: "b61bf93d-a9db-473e-822e-a65003b1b7e3",
|
id: "b61bf93d-a9db-473e-822e-a65003b1b7e3",
|
||||||
}
|
}
|
||||||
test("Clicking the delete button fires request to delete file", async () => {
|
test("Clicking the delete button fires request to delete file", async () => {
|
||||||
const spy = applyMakeRequestMock<FileData>(
|
const expectedUrlPattern = new RegExp(`/files/${mockItem.id}/$`)
|
||||||
async (url: string, opts?: requestUtil.RequestOptions) => ({
|
|
||||||
status: 200,
|
const axiosMock = getAxiosMockAdapter()
|
||||||
json: mockItem,
|
|
||||||
}),
|
axiosMock.onDelete(expectedUrlPattern).reply(200, mockItem)
|
||||||
)
|
|
||||||
|
|
||||||
jest
|
jest
|
||||||
.spyOn(fileQueries, "useFileDetails")
|
.spyOn(fileQueries, "useFileDetails")
|
||||||
|
@ -41,21 +39,21 @@ describe("FileDetails", () => {
|
||||||
|
|
||||||
await user.click(deleteButton)
|
await user.click(deleteButton)
|
||||||
|
|
||||||
expect(spy.mock.calls.length).toEqual(1)
|
const deleteRequests = axiosMock.history.delete
|
||||||
|
|
||||||
const deleteRequest = spy.mock.calls[0]
|
expect(deleteRequests.length).toEqual(1)
|
||||||
|
|
||||||
expect(deleteRequest[0]).toMatch(new RegExp(`\/files\/${mockItem.id}\$`))
|
const deleteRequest = deleteRequests[0]
|
||||||
expect(deleteRequest[1]?.method).toEqual("DELETE")
|
|
||||||
|
expect(deleteRequest.url).toMatch(expectedUrlPattern)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("Clicking the delete button redirects to the file list after success", async () => {
|
test("Clicking the delete button redirects to the file list after success", async () => {
|
||||||
const spy = applyMakeRequestMock<FileData>(
|
const expectedUrlPattern = new RegExp(`/files/${mockItem.id}/$`)
|
||||||
async (url: string, opts?: requestUtil.RequestOptions) => ({
|
|
||||||
status: 200,
|
const axiosMock = getAxiosMockAdapter()
|
||||||
json: mockItem,
|
|
||||||
}),
|
axiosMock.onDelete(expectedUrlPattern).reply(200, mockItem)
|
||||||
)
|
|
||||||
|
|
||||||
jest
|
jest
|
||||||
.spyOn(fileQueries, "useFileDetails")
|
.spyOn(fileQueries, "useFileDetails")
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { within } from "@testing-library/dom"
|
import { within } from "@testing-library/dom"
|
||||||
import userEvent from "@testing-library/user-event"
|
import userEvent from "@testing-library/user-event"
|
||||||
|
|
||||||
import { renderWithContexts as render, applyMakeRequestMock } from "./helpers"
|
import { renderWithContexts as render, getAxiosMockAdapter } from "./helpers"
|
||||||
import FileList from "../src/components/FileList"
|
import FileList from "../src/components/FileList"
|
||||||
import * as requestUtil from "../src/queries/requestUtils"
|
import AxiosMockAdapter from "axios-mock-adapter"
|
||||||
import { type FileData } from "../src/queries/files"
|
|
||||||
|
|
||||||
describe("FileList", () => {
|
describe("FileList", () => {
|
||||||
const mockItems = [
|
const mockItems = [
|
||||||
|
@ -75,12 +74,12 @@ describe("FileList", () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
test("Clicking the delete button fires request to delete file", async () => {
|
test("Clicking the delete button fires request to delete file", async () => {
|
||||||
const spy = applyMakeRequestMock<FileData>(
|
const expectedUrlPattern = new RegExp(`/files/${mockItems[0].id}/$`)
|
||||||
async (url: string, opts?: requestUtil.RequestOptions) => ({
|
|
||||||
status: 200,
|
const axiosMock = getAxiosMockAdapter()
|
||||||
json: mockItems[0],
|
|
||||||
}),
|
axiosMock.onDelete(expectedUrlPattern).reply(200, mockItems[0])
|
||||||
)
|
|
||||||
const user = userEvent.setup()
|
const user = userEvent.setup()
|
||||||
|
|
||||||
const { getByLabelText, debug } = render(
|
const { getByLabelText, debug } = render(
|
||||||
|
@ -90,14 +89,13 @@ describe("FileList", () => {
|
||||||
|
|
||||||
await user.click(deleteButton)
|
await user.click(deleteButton)
|
||||||
|
|
||||||
expect(spy.mock.calls.length).toEqual(1)
|
const deleteRequests = axiosMock.history.delete
|
||||||
|
|
||||||
const deleteRequest = spy.mock.calls[0]
|
expect(deleteRequests.length).toEqual(1)
|
||||||
|
|
||||||
expect(deleteRequest[0]).toMatch(
|
const deleteRequest = deleteRequests[0]
|
||||||
new RegExp(`\/files\/${mockItems[0].id}\$`),
|
|
||||||
)
|
expect(deleteRequest.url).toMatch(expectedUrlPattern)
|
||||||
expect(deleteRequest[1]?.method).toEqual("DELETE")
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { within } from "@testing-library/dom"
|
import { within } from "@testing-library/dom"
|
||||||
import userEvent from "@testing-library/user-event"
|
import userEvent from "@testing-library/user-event"
|
||||||
|
|
||||||
import { renderWithContexts as render, applyMakeRequestMock } from "./helpers"
|
import { renderWithContexts as render, getAxiosMockAdapter } from "./helpers"
|
||||||
import NavigationBar from "../src/components/NavigationBar"
|
import NavigationBar from "../src/components/NavigationBar"
|
||||||
import * as requestUtil from "../src/queries/requestUtils"
|
|
||||||
import { type FileData } from "../src/queries/files"
|
import { type FileData } from "../src/queries/files"
|
||||||
|
|
||||||
describe("NavigationBar", () => {
|
describe("NavigationBar", () => {
|
||||||
|
@ -14,17 +14,14 @@ describe("NavigationBar", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test("Clicking the upload button and selecting a file POSTs the file", async () => {
|
test("Clicking the upload button and selecting a file POSTs the file", async () => {
|
||||||
const spy = applyMakeRequestMock<FileData>(
|
const axiosMock = getAxiosMockAdapter()
|
||||||
async (url: string, opts?: requestUtil.RequestOptions) => ({
|
const expectedUrlPattern = new RegExp("/files/$")
|
||||||
status: 200,
|
axiosMock.onPost(expectedUrlPattern).reply(200, {
|
||||||
json: {
|
id: "b61bf93d-a9db-473e-822e-a65003b1b7e3",
|
||||||
id: "b61bf93d-a9db-473e-822e-a65003b1b7e3",
|
filename: "test.txt",
|
||||||
filename: "test.txt",
|
title: "test",
|
||||||
title: "test",
|
size: 1,
|
||||||
size: 1,
|
})
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const user = userEvent.setup()
|
const user = userEvent.setup()
|
||||||
|
|
||||||
|
@ -38,13 +35,13 @@ describe("NavigationBar", () => {
|
||||||
|
|
||||||
await user.upload(fileInput as HTMLInputElement, mockFile)
|
await user.upload(fileInput as HTMLInputElement, mockFile)
|
||||||
|
|
||||||
expect(spy.mock.calls.length).toEqual(1)
|
const postRequests = axiosMock.history.post
|
||||||
const call = spy.mock.lastCall
|
|
||||||
|
|
||||||
if (!call || call.length !== 2) fail("No last call or wrong length.")
|
expect(postRequests.length).toEqual(1)
|
||||||
|
|
||||||
expect(call[0]).toEqual("http://localhost:8000/files/")
|
const postRequest = postRequests[0]
|
||||||
expect(call[1]?.method).toEqual("POST")
|
|
||||||
|
expect(postRequest.url).toMatch(expectedUrlPattern)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { type ReactNode } from "react"
|
import { type ReactNode } from "react"
|
||||||
import { render } from "@testing-library/react"
|
import { render } from "@testing-library/react"
|
||||||
import { QueryClientProvider, QueryClient } from "@tanstack/react-query"
|
import { QueryClientProvider, QueryClient } from "@tanstack/react-query"
|
||||||
|
import { axiosWithDefaults } from "../src/queries/files"
|
||||||
|
import AxiosMockAdapter from "axios-mock-adapter"
|
||||||
|
|
||||||
import AsyncTaskContext, {
|
import AsyncTaskContext, {
|
||||||
type AsyncTask,
|
type AsyncTask,
|
||||||
} from "../src/contexts/AsyncTaskContext"
|
} from "../src/contexts/AsyncTaskContext"
|
||||||
import LocationContext from "../src/contexts/LocationContext"
|
import LocationContext from "../src/contexts/LocationContext"
|
||||||
import * as requestUtil from "../src/queries/requestUtils"
|
|
||||||
import { type FileData } from "../src/queries/files"
|
|
||||||
|
|
||||||
interface ContextInitialValues {
|
interface ContextInitialValues {
|
||||||
asyncTaskContext: Array<AsyncTask>
|
asyncTaskContext: Array<AsyncTask>
|
||||||
|
@ -19,7 +19,7 @@ const defaultContextValues = {
|
||||||
locationContext: { default: "/" },
|
locationContext: { default: "/" },
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderWithContexts(
|
function renderWithContexts(
|
||||||
component: ReactNode,
|
component: ReactNode,
|
||||||
initialValues?: Partial<ContextInitialValues>,
|
initialValues?: Partial<ContextInitialValues>,
|
||||||
) {
|
) {
|
||||||
|
@ -35,8 +35,8 @@ export function renderWithContexts(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyMakeRequestMock<Schema>(
|
function getAxiosMockAdapter() {
|
||||||
impl: typeof requestUtil.default<Schema>,
|
return new AxiosMockAdapter(axiosWithDefaults)
|
||||||
) {
|
|
||||||
return jest.spyOn(requestUtil, "default").mockImplementation(impl)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { getAxiosMockAdapter, renderWithContexts }
|
||||||
|
|
0
frontend/tests/testSetup.ts
Normal file
0
frontend/tests/testSetup.ts
Normal file
|
@ -2666,6 +2666,29 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"axios-mock-adapter@npm:^1.21.5":
|
||||||
|
version: 1.21.5
|
||||||
|
resolution: "axios-mock-adapter@npm:1.21.5"
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: ^3.1.3
|
||||||
|
is-buffer: ^2.0.5
|
||||||
|
peerDependencies:
|
||||||
|
axios: ">= 0.17.0"
|
||||||
|
checksum: e3c2ccf220a2ddd316ccdff36b65754ddaf9b7a8dc70a64e9ff94da335a0694aaba3d925e6d7d5f71dd4badfe9af581bd7e6dd2dc09414d9900298afe12a71a0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"axios@npm:^1.4.0":
|
||||||
|
version: 1.4.0
|
||||||
|
resolution: "axios@npm:1.4.0"
|
||||||
|
dependencies:
|
||||||
|
follow-redirects: ^1.15.0
|
||||||
|
form-data: ^4.0.0
|
||||||
|
proxy-from-env: ^1.1.0
|
||||||
|
checksum: 7fb6a4313bae7f45e89d62c70a800913c303df653f19eafec88e56cea2e3821066b8409bc68be1930ecca80e861c52aa787659df0ffec6ad4d451c7816b9386b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"babel-jest@npm:^29.6.2":
|
"babel-jest@npm:^29.6.2":
|
||||||
version: 29.6.2
|
version: 29.6.2
|
||||||
resolution: "babel-jest@npm:29.6.2"
|
resolution: "babel-jest@npm:29.6.2"
|
||||||
|
@ -2769,6 +2792,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"base64-js@npm:^1.3.1":
|
||||||
|
version: 1.5.1
|
||||||
|
resolution: "base64-js@npm:1.5.1"
|
||||||
|
checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"boolbase@npm:^1.0.0":
|
"boolbase@npm:^1.0.0":
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
resolution: "boolbase@npm:1.0.0"
|
resolution: "boolbase@npm:1.0.0"
|
||||||
|
@ -2843,6 +2873,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"buffer@npm:^5.5.0||^6.0.0":
|
||||||
|
version: 6.0.3
|
||||||
|
resolution: "buffer@npm:6.0.3"
|
||||||
|
dependencies:
|
||||||
|
base64-js: ^1.3.1
|
||||||
|
ieee754: ^1.2.1
|
||||||
|
checksum: 5ad23293d9a731e4318e420025800b42bf0d264004c0286c8cc010af7a270c7a0f6522e84f54b9ad65cbd6db20b8badbfd8d2ebf4f80fa03dab093b89e68c3f9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"cacache@npm:^17.0.0":
|
"cacache@npm:^17.0.0":
|
||||||
version: 17.1.3
|
version: 17.1.3
|
||||||
resolution: "cacache@npm:17.1.3"
|
resolution: "cacache@npm:17.1.3"
|
||||||
|
@ -3620,6 +3660,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"fast-deep-equal@npm:^3.1.3":
|
||||||
|
version: 3.1.3
|
||||||
|
resolution: "fast-deep-equal@npm:3.1.3"
|
||||||
|
checksum: e21a9d8d84f53493b6aa15efc9cfd53dd5b714a1f23f67fb5dc8f574af80df889b3bce25dc081887c6d25457cce704e636395333abad896ccdec03abaf1f3f9d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.1.0":
|
"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.1.0":
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
resolution: "fast-json-stable-stringify@npm:2.1.0"
|
resolution: "fast-json-stable-stringify@npm:2.1.0"
|
||||||
|
@ -3662,6 +3709,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"follow-redirects@npm:^1.15.0":
|
||||||
|
version: 1.15.2
|
||||||
|
resolution: "follow-redirects@npm:1.15.2"
|
||||||
|
peerDependenciesMeta:
|
||||||
|
debug:
|
||||||
|
optional: true
|
||||||
|
checksum: faa66059b66358ba65c234c2f2a37fcec029dc22775f35d9ad6abac56003268baf41e55f9ee645957b32c7d9f62baf1f0b906e68267276f54ec4b4c597c2b190
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"for-each@npm:^0.3.3":
|
"for-each@npm:^0.3.3":
|
||||||
version: 0.3.3
|
version: 0.3.3
|
||||||
resolution: "for-each@npm:0.3.3"
|
resolution: "for-each@npm:0.3.3"
|
||||||
|
@ -4070,6 +4127,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ieee754@npm:^1.2.1":
|
||||||
|
version: 1.2.1
|
||||||
|
resolution: "ieee754@npm:1.2.1"
|
||||||
|
checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"import-fresh@npm:^3.2.1":
|
"import-fresh@npm:^3.2.1":
|
||||||
version: 3.3.0
|
version: 3.3.0
|
||||||
resolution: "import-fresh@npm:3.3.0"
|
resolution: "import-fresh@npm:3.3.0"
|
||||||
|
@ -4188,6 +4252,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"is-buffer@npm:^2.0.5":
|
||||||
|
version: 2.0.5
|
||||||
|
resolution: "is-buffer@npm:2.0.5"
|
||||||
|
checksum: 764c9ad8b523a9f5a32af29bdf772b08eb48c04d2ad0a7240916ac2688c983bf5f8504bf25b35e66240edeb9d9085461f9b5dae1f3d2861c6b06a65fe983de42
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"is-callable@npm:^1.1.3":
|
"is-callable@npm:^1.1.3":
|
||||||
version: 1.2.7
|
version: 1.2.7
|
||||||
resolution: "is-callable@npm:1.2.7"
|
resolution: "is-callable@npm:1.2.7"
|
||||||
|
@ -5981,6 +6052,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"proxy-from-env@npm:^1.1.0":
|
||||||
|
version: 1.1.0
|
||||||
|
resolution: "proxy-from-env@npm:1.1.0"
|
||||||
|
checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"psl@npm:^1.1.33":
|
"psl@npm:^1.1.33":
|
||||||
version: 1.9.0
|
version: 1.9.0
|
||||||
resolution: "psl@npm:1.9.0"
|
resolution: "psl@npm:1.9.0"
|
||||||
|
@ -6243,6 +6321,9 @@ __metadata:
|
||||||
"@types/jest": ^29.5.3
|
"@types/jest": ^29.5.3
|
||||||
"@types/react": ^18.2.18
|
"@types/react": ^18.2.18
|
||||||
"@types/react-dom": ^18.2.7
|
"@types/react-dom": ^18.2.7
|
||||||
|
axios: ^1.4.0
|
||||||
|
axios-mock-adapter: ^1.21.5
|
||||||
|
buffer: ^5.5.0||^6.0.0
|
||||||
jest: ^29.6.2
|
jest: ^29.6.2
|
||||||
jest-environment-jsdom: ^29.6.2
|
jest-environment-jsdom: ^29.6.2
|
||||||
parcel: ^2.9.3
|
parcel: ^2.9.3
|
||||||
|
|
Reference in a new issue