mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
refactor(site): use axios in non-swr endpoints (#460)
Summary: Applies axios to login, logout and getApiKey Impact: POC of axios (#453) and testing axios Additional details: * add test:watch script resolves: #453
This commit is contained in:
@ -17,12 +17,14 @@
|
|||||||
"storybook": "start-storybook -p 6006 -s ./static",
|
"storybook": "start-storybook -p 6006 -s ./static",
|
||||||
"storybook:build": "build-storybook",
|
"storybook:build": "build-storybook",
|
||||||
"test": "jest --selectProjects test",
|
"test": "jest --selectProjects test",
|
||||||
"test:coverage": "jest --selectProjects test --collectCoverage"
|
"test:coverage": "jest --selectProjects test --collectCoverage",
|
||||||
|
"test:watch": "jest --selectProjects test --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material-ui/core": "4.9.4",
|
"@material-ui/core": "4.9.4",
|
||||||
"@material-ui/icons": "4.5.1",
|
"@material-ui/icons": "4.5.1",
|
||||||
"@material-ui/lab": "4.0.0-alpha.42",
|
"@material-ui/lab": "4.0.0-alpha.42",
|
||||||
|
"axios": "0.26.1",
|
||||||
"formik": "2.2.9",
|
"formik": "2.2.9",
|
||||||
"history": "5.3.0",
|
"history": "5.3.0",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
|
124
site/src/api.test.ts
Normal file
124
site/src/api.test.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import axios from "axios"
|
||||||
|
import { APIKeyResponse, getApiKey, login, LoginResponse, logout } from "./api"
|
||||||
|
|
||||||
|
// Mock the axios module so that no real network requests are made, but rather
|
||||||
|
// we swap in a resolved or rejected value
|
||||||
|
//
|
||||||
|
// See: https://jestjs.io/docs/mock-functions#mocking-modules
|
||||||
|
jest.mock("axios")
|
||||||
|
|
||||||
|
describe("api.ts", () => {
|
||||||
|
describe("login", () => {
|
||||||
|
it("should return LoginResponse", async () => {
|
||||||
|
// given
|
||||||
|
const loginResponse: LoginResponse = {
|
||||||
|
session_token: "abc_123_test",
|
||||||
|
}
|
||||||
|
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||||
|
return Promise.resolve({ data: loginResponse })
|
||||||
|
})
|
||||||
|
axios.post = axiosMockPost
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await login("test", "123")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(axiosMockPost).toHaveBeenCalled()
|
||||||
|
expect(result).toStrictEqual(loginResponse)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error on 401", async () => {
|
||||||
|
// given
|
||||||
|
// ..ensure that we await our expect assertion in async/await test
|
||||||
|
expect.assertions(1)
|
||||||
|
const expectedError = {
|
||||||
|
message: "Validation failed",
|
||||||
|
errors: [{ field: "email", code: "email" }],
|
||||||
|
}
|
||||||
|
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||||
|
return Promise.reject(expectedError)
|
||||||
|
})
|
||||||
|
axios.post = axiosMockPost
|
||||||
|
|
||||||
|
try {
|
||||||
|
await login("test", "123")
|
||||||
|
} catch (error) {
|
||||||
|
expect(error).toStrictEqual(expectedError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("logout", () => {
|
||||||
|
it("should return without erroring", async () => {
|
||||||
|
// given
|
||||||
|
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||||
|
return Promise.resolve()
|
||||||
|
})
|
||||||
|
axios.post = axiosMockPost
|
||||||
|
|
||||||
|
// when
|
||||||
|
await logout()
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(axiosMockPost).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error on 500", async () => {
|
||||||
|
// given
|
||||||
|
// ..ensure that we await our expect assertion in async/await test
|
||||||
|
expect.assertions(1)
|
||||||
|
const expectedError = {
|
||||||
|
message: "Failed to logout.",
|
||||||
|
}
|
||||||
|
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||||
|
return Promise.reject(expectedError)
|
||||||
|
})
|
||||||
|
axios.post = axiosMockPost
|
||||||
|
|
||||||
|
try {
|
||||||
|
await logout()
|
||||||
|
} catch (error) {
|
||||||
|
expect(error).toStrictEqual(expectedError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("getApiKey", () => {
|
||||||
|
it("should return APIKeyResponse", async () => {
|
||||||
|
// given
|
||||||
|
const apiKeyResponse: APIKeyResponse = {
|
||||||
|
key: "abc_123_test",
|
||||||
|
}
|
||||||
|
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||||
|
return Promise.resolve({ data: apiKeyResponse })
|
||||||
|
})
|
||||||
|
axios.post = axiosMockPost
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await getApiKey()
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(axiosMockPost).toHaveBeenCalled()
|
||||||
|
expect(result).toStrictEqual(apiKeyResponse)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error on 401", async () => {
|
||||||
|
// given
|
||||||
|
// ..ensure that we await our expect assertion in async/await test
|
||||||
|
expect.assertions(1)
|
||||||
|
const expectedError = {
|
||||||
|
message: "No Cookie!",
|
||||||
|
}
|
||||||
|
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
|
||||||
|
return Promise.reject(expectedError)
|
||||||
|
})
|
||||||
|
axios.post = axiosMockPost
|
||||||
|
|
||||||
|
try {
|
||||||
|
await getApiKey()
|
||||||
|
} catch (error) {
|
||||||
|
expect(error).toStrictEqual(expectedError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -1,7 +1,8 @@
|
|||||||
|
import axios, { AxiosRequestHeaders } from "axios"
|
||||||
import { mutate } from "swr"
|
import { mutate } from "swr"
|
||||||
|
|
||||||
interface LoginResponse {
|
const CONTENT_TYPE_JSON: AxiosRequestHeaders = {
|
||||||
session_token: string
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,48 +108,32 @@ export namespace Workspace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LoginResponse {
|
||||||
|
session_token: string
|
||||||
|
}
|
||||||
|
|
||||||
export const login = async (email: string, password: string): Promise<LoginResponse> => {
|
export const login = async (email: string, password: string): Promise<LoginResponse> => {
|
||||||
const response = await fetch("/api/v2/users/login", {
|
const payload = JSON.stringify({
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const body = await response.json()
|
const response = await axios.post<LoginResponse>("/api/v2/users/login", payload, {
|
||||||
if (!response.ok) {
|
headers: { ...CONTENT_TYPE_JSON },
|
||||||
throw new Error(body.message)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return body
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export const logout = async (): Promise<void> => {
|
export const logout = async (): Promise<void> => {
|
||||||
const response = await fetch("/api/v2/users/logout", {
|
await axios.post("/api/v2/users/logout")
|
||||||
method: "POST",
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const body = await response.json()
|
|
||||||
throw new Error(body.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getApiKey = async (): Promise<{ key: string }> => {
|
export interface APIKeyResponse {
|
||||||
const response = await fetch("/api/v2/users/me/keys", {
|
key: string
|
||||||
method: "POST",
|
}
|
||||||
})
|
|
||||||
|
export const getApiKey = async (): Promise<APIKeyResponse> => {
|
||||||
if (!response.ok) {
|
const response = await axios.post<APIKeyResponse>("/api/v2/users/me/keys")
|
||||||
const body = await response.json()
|
return response.data
|
||||||
throw new Error(body.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
|
||||||
}
|
}
|
||||||
|
@ -4085,6 +4085,13 @@ axe-core@^4.3.5:
|
|||||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.0.tgz#f93be7f81017eb8bedeb1859cc8092cc918d2dc8"
|
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.0.tgz#f93be7f81017eb8bedeb1859cc8092cc918d2dc8"
|
||||||
integrity sha512-btWy2rze3NnxSSxb7LtNhPYYFrRoFBfjiGzmSc/5Hu47wApO2KNXjP/w7Nv2Uz/Fyr/pfEiwOkcXhDxu0jz5FA==
|
integrity sha512-btWy2rze3NnxSSxb7LtNhPYYFrRoFBfjiGzmSc/5Hu47wApO2KNXjP/w7Nv2Uz/Fyr/pfEiwOkcXhDxu0jz5FA==
|
||||||
|
|
||||||
|
axios@0.26.1:
|
||||||
|
version "0.26.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9"
|
||||||
|
integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.14.8"
|
||||||
|
|
||||||
axobject-query@^2.2.0:
|
axobject-query@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
|
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
|
||||||
@ -6773,6 +6780,11 @@ follow-redirects@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
||||||
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
|
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
|
||||||
|
|
||||||
|
follow-redirects@^1.14.8:
|
||||||
|
version "1.14.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7"
|
||||||
|
integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==
|
||||||
|
|
||||||
for-in@^1.0.2:
|
for-in@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||||
|
Reference in New Issue
Block a user