react-solid-flow
Version:
[SolidJS](https://www.solidjs.com/docs/latest/api#control-flow)-inspired basic control-flow components and everyday async state hook library for [React](https://reactjs.org/)
171 lines (156 loc) • 4.75 kB
text/typescript
import { describe, expect, it, vi } from "vitest";
import { Resource } from "../Resource";
describe("useResource", () => {
describe("Resource.constructor", () => {
it("creates an empty resource", () => {
const res = new Resource();
expect(res).toEqual({
state: "unresolved",
data: undefined,
loading: false,
error: undefined,
latest: undefined,
});
});
it("correctly sets core data", () => {
const res = new Resource({
data: "test1",
loading: true,
error: "test2",
});
expect(res.data).toBe("test1");
expect(res.loading).toBe(true);
expect(res.error).toBe("test2");
});
it("keeps latest data from previous resource", () => {
let res = new Resource({
data: "test1",
loading: false,
error: undefined,
});
res = new Resource({
data: undefined,
loading: true,
}, res);
res = new Resource({
...res,
data: undefined,
loading: false,
error: "test2",
}, res);
expect(res.latest).toBe("test1");
});
it("updates resource state name with getState function", () => {
const speGetState = vi.spyOn(Resource, "getState");
let res = new Resource<string>({
data: undefined,
loading: true,
error: undefined,
});
expect(res.state).toBe("pending");
expect(speGetState).toBeCalledTimes(1);
res = new Resource({
data: "test",
loading: false,
error: undefined,
}, res);
expect(res.state).toBe("ready");
expect(speGetState).toBeCalledTimes(2);
});
});
describe("Resource.from", () => {
it("creates a resource from sync data", () => {
const res = Resource.from("test");
expect(res).toEqual({
data: "test",
loading: false,
error: undefined,
latest: "test",
state: "ready",
});
expect(res).toBeInstanceOf(Resource);
});
it("creates a resource with async data", () => {
const res = Resource.from(Promise.resolve());
expect(res).toEqual({
data: undefined,
loading: true,
error: undefined,
latest: undefined,
state: "pending",
});
expect(res).toBeInstanceOf(Resource);
});
});
/*
* | state | data | loading | error |
* |:-----------|:-----:|:-------:|:-----:|
* | unresolved | No | No | No |
* | pending | No | Yes | No* |
* | ready | Yes | No | No |
* | refreshing | Yes | Yes | No* |
* | errored | No* | No | Yes |
*/
describe("Resource.getState", () => {
// we're using falsy values where possible, to check that they're told apart
// from undefined correctly
it("determines unresolved state", () => {
expect(Resource.getState({
data: undefined,
loading: false,
error: undefined,
})).toBe("unresolved");
});
it("determines pending state", () => {
expect(Resource.getState({
data: undefined,
loading: true,
error: undefined,
})).toBe("pending");
});
it("incorrectly reset error + loading still results in pending state", () => {
// Though this example is impossible in this code, we still have this test
// in case it was called on some 3d party implementation of Resource-like object.
expect(Resource.getState({
data: undefined,
loading: true,
error: false,
})).toBe("pending");
});
it("determines ready state", () => {
expect(Resource.getState({
data: false,
loading: false,
error: undefined,
})).toBe("ready");
});
it("determines refreshing state", () => {
expect(Resource.getState({
data: false,
loading: true,
error: undefined,
})).toBe("refreshing");
});
it("determines refreshing state with incorrectly set error", () => {
expect(Resource.getState({
data: false,
loading: true,
error: false,
})).toBe("refreshing");
});
it("determines errored state", () => {
expect(Resource.getState({
data: null,
loading: false,
error: false,
})).toBe("errored");
});
it("data + error results in errored state", () => {
expect(Resource.getState({
data: true,
loading: false,
error: false,
})).toBe("errored");
});
});
});