Skip to content
This repository has been archived by the owner on Nov 12, 2024. It is now read-only.

Commit

Permalink
Repair broken openapi-fetch types
Browse files Browse the repository at this point in the history
  • Loading branch information
htunnicliff committed Sep 10, 2024
1 parent 5a8ce45 commit 5fbda5a
Show file tree
Hide file tree
Showing 14 changed files with 668 additions and 194 deletions.
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
lint-staged
vitest --run --changed
vitest --run --changed --typecheck
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@ Wrapper hooks are provided 1:1 for each hook exported by SWR.
```ts
import createClient from "openapi-fetch";

import { createQueryHook } from "swr-openapi";
import { createImmutableHook } from "swr-openapi/immutable";
import { createInfiniteHook } from "swr-openapi/infinite";
import { createMutateHook } from "swr-openapi/mutate";
import {
createQueryHook,
createImmutableHook,
createInfiniteHook,
createMutateHook,
} from "swr-openapi";

import { paths as SomeApiPaths } from "./some-api";

Expand Down
29 changes: 28 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import vitest from "@vitest/eslint-plugin";
import prettier from "eslint-config-prettier";

export default tseslint.config(
Expand All @@ -14,7 +15,6 @@ export default tseslint.config(
},
},
},
prettier,
{
files: ["**/*.{js,ts}"],
rules: {
Expand All @@ -33,4 +33,31 @@ export default tseslint.config(
"@typescript-eslint/only-throw-error": "off",
},
},
{
files: ["**/__test__/**"],
plugins: {
vitest,
},
settings: {
vitest: {
typecheck: true,
},
},
languageOptions: {
globals: {
...vitest.environments.env.globals,
},
},
rules: {
...vitest.configs.all.rules,
"vitest/no-hooks": "off",
"vitest/padding-around-expect-groups": "off",
"vitest/prefer-lowercase-title": "off",
"vitest/prefer-expect-assertions": "off",
"vitest/prefer-to-be-falsy": "off",
"vitest/prefer-to-be-truthy": "off",
"vitest/require-hook": "off",
},
},
prettier,
);
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
"prepare": "husky",
"prepublishOnly": "npm run build && npm run exports:check && npm run types:check && npm run format:check && npm test",
"build": "del ./dist && tsc --project tsconfig.build.json",
"test": "vitest run",
"dev": "vitest",
"test": "vitest run --typecheck",
"dev": "vitest --typecheck",
"lint": "eslint './src/**/*'",
"types:check": "tsc --noEmit",
"format": "prettier --write .",
Expand All @@ -57,6 +57,7 @@
"@types/eslint-config-prettier": "6.11.3",
"@types/lodash": "4.17.7",
"@types/react": "18.3.5",
"@vitest/eslint-plugin": "1.1.0",
"del-cli": "5.1.0",
"eslint": "9.10.0",
"eslint-config-prettier": "9.1.0",
Expand Down
26 changes: 19 additions & 7 deletions src/__test__/fixtures/petstore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,9 +495,13 @@ export interface operations {
/** @description Status values that need to be considered for filter */
status?: "available" | "pending" | "sold";
};
header?: never;
header?: {
"X-Example": string;
};
path?: never;
cookie?: never;
cookie?: {
"some-cookie-key": string;
};
};
requestBody?: never;
responses: {
Expand All @@ -516,15 +520,19 @@ export interface operations {
headers: {
[name: string]: unknown;
};
content?: never;
content: {
"application/json": {
message: "Invalid status";
}
};
};
};
};
findPetsByTags: {
parameters: {
query?: {
query: {
/** @description Tags to filter by */
tags?: string[];
tags: string[];
};
header?: never;
path?: never;
Expand Down Expand Up @@ -578,14 +586,18 @@ export interface operations {
headers: {
[name: string]: unknown;
};
content?: never;
content: {
"application/json": {
message: "Invalid pet configuration";
};
};
};
/** @description Pet not found */
404: {
headers: {
[name: string]: unknown;
};
content?: never;
content?: never
};
};
};
Expand Down
7 changes: 6 additions & 1 deletion src/__test__/infinite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ describe("createInfiniteHook", () => {

// Invokes `useSWRInfinite`
expect(useSWRInfinite).toHaveBeenCalledTimes(1);

let getKey = useSWRInfinite.mock.lastCall![0];

// Calling `getKey` should invoke `getInit`
const key = getKey(0, null);

expect(getInit).toHaveBeenCalledTimes(1);

// `getInit` should be called with key loader arguments
Expand All @@ -44,6 +46,7 @@ describe("createInfiniteHook", () => {
getInit.mockReturnValueOnce(null);
useInfinite("/pet/{petId}", getInit);
getKey = useSWRInfinite.mock.lastCall![0];

expect(getKey(0, null)).toBeNull();
});

Expand All @@ -70,18 +73,20 @@ describe("createInfiniteHook", () => {

await expect(() =>
fetcher!(["some-key", "any-path", { some: "init" }]),
).rejects.toThrowError(new Error("Yikes"));
).rejects.toThrow(new Error("Yikes"));
});

it("passes correct config to useSWRInfinite", () => {
useInfinite("/pet/{petId}", vi.fn(), { initialSize: 5000 });

expect(useSWRInfinite).toHaveBeenLastCalledWith(
expect.any(Function),
expect.any(Function),
{ initialSize: 5000 },
);

useInfinite("/pet/{petId}", vi.fn());

expect(useSWRInfinite).toHaveBeenLastCalledWith(
expect.any(Function),
expect.any(Function),
Expand Down
21 changes: 9 additions & 12 deletions src/__test__/mutate.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { isMatch } from "lodash";
import createClient from "openapi-fetch";
import * as React from "react";
import * as SWR from "swr";
import type { ScopedMutator } from "swr/_internal";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createMutateHook } from "../mutate.js";
import type { paths } from "./fixtures/petstore.js";
import { isMatch } from "lodash";

// Mock `useCallback` (return given function as-is)
vi.mock("react");
Expand Down Expand Up @@ -44,10 +44,7 @@ describe("createMutateHook", () => {
it("returns callback that invokes swr `mutate` with fn, data and options", async () => {
expect(swrMutate).not.toHaveBeenCalled();

const data = {
name: "doggie",
photoUrls: ["https://example.com"],
};
const data = [{ name: "doggie", photoUrls: ["https://example.com"] }];
const config = { throwOnError: false };

await mutate(["/pet/findByStatus"], data, config);
Expand All @@ -65,7 +62,7 @@ describe("createMutateHook", () => {

describe("useMutate -> mutate -> key matcher", () => {
it("returns false for non-array keys", async () => {
await mutate(["/pet/findByTags"]);
await mutate(["/pet/findByStatus"]);
const keyMatcher = getKeyMatcher();

expect(keyMatcher(null)).toBe(false);
Expand All @@ -75,7 +72,7 @@ describe("createMutateHook", () => {
});

it("returns false for arrays with length !== 3", async () => {
await mutate(["/pet/findByTags"]);
await mutate(["/pet/findByStatus"]);
const keyMatcher = getKeyMatcher();

expect(keyMatcher(Array(0))).toBe(false);
Expand All @@ -86,19 +83,19 @@ describe("createMutateHook", () => {
});

it("matches when prefix and path are equal and init isn't given", async () => {
await mutate(["/pet/findByTags"]);
await mutate(["/pet/findByStatus"]);
const keyMatcher = getKeyMatcher();

// Same path, no init
expect(keyMatcher(["<unique-key>", "/pet/findByTags"])).toBe(true);
expect(keyMatcher(["<unique-key>", "/pet/findByStatus"])).toBe(true);

// Same path, init ignored
expect(
keyMatcher(["<unique-key>", "/pet/findByTags", { some: "init" }]),
keyMatcher(["<unique-key>", "/pet/findByStatus", { some: "init" }]),
).toBe(true);

// Same path, undefined init ignored
expect(keyMatcher(["<unique-key>", "/pet/findByTags", undefined])).toBe(
expect(keyMatcher(["<unique-key>", "/pet/findByStatus", undefined])).toBe(
true,
);
});
Expand All @@ -107,7 +104,7 @@ describe("createMutateHook", () => {
const psudeoCompare = vi.fn().mockReturnValue("booleanPlaceholder");

const prefix = "<unique-key>";
const path = "/pet/findByTags";
const path = "/pet/findByStatus";
const givenInit = {};

const useMutate = createMutateHook(client, prefix, psudeoCompare);
Expand Down
15 changes: 10 additions & 5 deletions src/__test__/query-base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ describe("configureBaseQueryHook", () => {

it("passes correct key to useSWR", () => {
useQuery("/store/inventory", {});

expect(useSWR).toHaveBeenLastCalledWith(
["<unique-key>", "/store/inventory", {}],
expect.any(Function),
undefined,
);

useQuery("/pet/findByTags", {
Expand All @@ -42,6 +44,7 @@ describe("configureBaseQueryHook", () => {
},
},
});

expect(useSWR).toHaveBeenLastCalledWith(
[
"<unique-key>",
Expand All @@ -55,19 +58,21 @@ describe("configureBaseQueryHook", () => {
},
],
expect.any(Function),
undefined,
);

// @ts-expect-error - TODO: Support undefined init when not required
useQuery("/store/inventory");

expect(useSWR).toHaveBeenLastCalledWith(
["<unique-key>", "/store/inventory", undefined],
expect.any(Function),
undefined,
);
});

it("passes correct fetcher to useSWR", async () => {
// Note: useQuery input doesn't matter here, since we test the fetcher in isolation
useQuery("/pet/findByTags", {});
useQuery("/pet/findByStatus");

const fetcher = useSWR.mock.lastCall![1];

Expand All @@ -89,14 +94,14 @@ describe("configureBaseQueryHook", () => {

await expect(() =>
fetcher!(["some-key", "any-path", { some: "init" }]),
).rejects.toThrowError(new Error("Yikes"));
).rejects.toThrow(new Error("Yikes"));
});

it("passes correct config to useSWR", () => {
useQuery("/pet/findByTags", {}, { errorRetryCount: 56 });
useQuery("/pet/findByStatus", {}, { errorRetryCount: 56 });

expect(useSWR).toHaveBeenLastCalledWith(
["<unique-key>", "/pet/findByTags", {}],
["<unique-key>", "/pet/findByStatus", {}],
expect.any(Function),
{ errorRetryCount: 56 },
);
Expand Down
Loading

0 comments on commit 5fbda5a

Please sign in to comment.