my_gradio / js /core /src /init.test.ts
xray918's picture
Upload folder using huggingface_hub
0ad74ed verified
import { describe, test, expect, vi } from "vitest";
import { spy } from "tinyspy";
import { setupServer } from "msw/node";
import { http, HttpResponse } from "msw";
import type { client_return } from "@gradio/client";
import { Dependency, TargetMap } from "./types";
import {
process_frontend_fn,
create_target_meta,
determine_interactivity,
process_server_fn,
get_component
} from "./init";
describe("process_frontend_fn", () => {
test("empty source code returns null", () => {
const source = "";
const fn = process_frontend_fn(source, false, 1, 1);
expect(fn).toBe(null);
});
test("falsey source code returns null: false", () => {
const source = false;
const fn = process_frontend_fn(source, false, 1, 1);
expect(fn).toBe(null);
});
test("falsey source code returns null: undefined", () => {
const source = undefined;
const fn = process_frontend_fn(source, false, 1, 1);
expect(fn).toBe(null);
});
test("falsey source code returns null: null", () => {
const source = null;
const fn = process_frontend_fn(source, false, 1, 1);
expect(fn).toBe(null);
});
test("source code returns a function", () => {
const source = "(arg) => arg";
const fn = process_frontend_fn(source, false, 1, 1);
expect(typeof fn).toBe("function");
});
test("arrays of values can be passed to the generated function", async () => {
const source = "(arg) => arg";
const fn = process_frontend_fn(source, false, 1, 1);
if (fn) {
await expect(fn([1])).resolves.toEqual([1]);
}
});
test("arrays of many values can be passed", async () => {
const source = "(...args) => args";
const fn = process_frontend_fn(source, false, 1, 1);
if (fn) {
await expect(fn([1, 2, 3, 4, 5, 6])).resolves.toEqual([1, 2, 3, 4, 5, 6]);
}
});
test("The generated function returns a promise", () => {
const source = "(arg) => arg";
const fn = process_frontend_fn(source, false, 1, 1);
if (fn) {
expect(fn([1])).toBeInstanceOf(Promise);
}
});
test("The generated function is callable and returns the expected value", async () => {
const source = "(arg) => arg";
const fn = process_frontend_fn(source, false, 1, 1);
if (fn) {
await expect(fn([1])).resolves.toEqual([1]);
}
});
test("The return value of the function is wrapped in an array if there is no backend function and the input length is 1", async () => {
const source = "(arg) => arg";
const fn = process_frontend_fn(source, false, 1, 1);
if (fn) {
await expect(fn([1])).resolves.toEqual([1]);
}
});
test("The return value of the function is not wrapped in an array if there is no backend function and the input length is greater than 1", async () => {
const source = "(arg) => arg";
const fn = process_frontend_fn(source, false, 2, 2);
if (fn) {
await expect(fn([1])).resolves.toEqual(1);
}
});
test("The return value of the function is wrapped in an array if there is a backend function and the input length is 1", async () => {
const source = "(arg) => arg";
const fn = process_frontend_fn(source, true, 1, 1);
if (fn) {
await expect(fn([1])).resolves.toEqual([1]);
}
});
test("The return value of the function is not wrapped in an array if there is a backend function and the input length is greater than 1", async () => {
const source = "(arg) => arg";
const fn = process_frontend_fn(source, true, 2, 2);
if (fn) {
await expect(fn([1])).resolves.toEqual(1);
}
});
});
describe("create_target_meta", () => {
test("creates a target map", () => {
const targets: Dependency["targets"] = [
[1, "change"],
[2, "input"],
[3, "load"]
];
const fn_index = 0;
const target_map = {};
const result = create_target_meta(targets, fn_index, target_map);
expect(result).toEqual({
1: { change: [0] },
2: { input: [0] },
3: { load: [0] }
});
});
test("if the target already exists, it adds the new trigger to the list", () => {
const targets: Dependency["targets"] = [
[1, "change"],
[1, "input"],
[1, "load"]
];
const fn_index = 1;
const target_map: TargetMap = {
1: { change: [0] }
};
const result = create_target_meta(targets, fn_index, target_map);
expect(result).toEqual({
1: { change: [0, 1], input: [1], load: [1] }
});
});
test("if the trigger already exists, it adds the new function to the list", () => {
const targets: Dependency["targets"] = [
[1, "change"],
[2, "change"],
[3, "change"]
];
const fn_index = 1;
const target_map: TargetMap = {
1: { change: [0] },
2: { change: [0] },
3: { change: [0] }
};
const result = create_target_meta(targets, fn_index, target_map);
expect(result).toEqual({
1: { change: [0, 1] },
2: { change: [0, 1] },
3: { change: [0, 1] }
});
});
test("if the target and trigger already exist, it adds the new function to the list", () => {
const targets: Dependency["targets"] = [[1, "change"]];
const fn_index = 1;
const target_map: TargetMap = {
1: { change: [0] }
};
const result = create_target_meta(targets, fn_index, target_map);
expect(result).toEqual({
1: { change: [0, 1] }
});
});
test("if the target, trigger and function id already exist, it does not add duplicates", () => {
const targets: Dependency["targets"] = [[1, "change"]];
const fn_index = 0;
const target_map: TargetMap = {
1: { change: [0] }
};
const result = create_target_meta(targets, fn_index, target_map);
expect(result).toEqual({
1: { change: [0] }
});
});
});
describe("determine_interactivity", () => {
test("returns true if the prop is interactive = true", () => {
const result = determine_interactivity(
0,
true,
"hi",
new Set([0]),
new Set([2])
);
expect(result).toBe(true);
});
test("returns false if the prop is interactive = false", () => {
const result = determine_interactivity(
0,
false,
"hi",
new Set([0]),
new Set([2])
);
expect(result).toBe(false);
});
test("returns true if the component is an input", () => {
const result = determine_interactivity(
0,
undefined,
"hi",
new Set([0]),
new Set([2])
);
expect(result).toBe(true);
});
test("returns true if the component is not an input or output and the component has no default value: empty string", () => {
const result = determine_interactivity(
2,
undefined,
"",
new Set([0]),
new Set([1])
);
expect(result).toBe(true);
});
test("returns true if the component is not an input or output and the component has no default value: empty array", () => {
const result = determine_interactivity(
2,
undefined,
[],
new Set([0]),
new Set([1])
);
expect(result).toBe(true);
});
test("returns true if the component is not an input or output and the component has no default value: boolean", () => {
const result = determine_interactivity(
2,
undefined,
false,
new Set([0]),
new Set([1])
);
expect(result).toBe(true);
});
test("returns true if the component is not an input or output and the component has no default value: undefined", () => {
const result = determine_interactivity(
2,
undefined,
undefined,
new Set([0]),
new Set([1])
);
expect(result).toBe(true);
});
test("returns true if the component is not an input or output and the component has no default value: null", () => {
const result = determine_interactivity(
2,
undefined,
null,
new Set([0]),
new Set([1])
);
expect(result).toBe(true);
});
test("returns true if the component is not an input or output and the component has no default value: 0", () => {
const result = determine_interactivity(
2,
undefined,
0,
new Set([0]),
new Set([1])
);
expect(result).toBe(true);
});
test("returns false if the component is not an input or output and the component has a default value", () => {
const result = determine_interactivity(
2,
undefined,
"hello",
new Set([0]),
new Set([1])
);
expect(result).toBe(false);
});
});
describe("process_server_fn", () => {
test("returns an object", () => {
const result = process_server_fn(1, ["fn1", "fn2"], {} as any);
expect(result).toBeTypeOf("object");
});
test("returns an object with the correct keys", () => {
const result = process_server_fn(1, ["fn1", "fn2"], {} as any);
expect(Object.keys(result)).toEqual(["fn1", "fn2"]);
});
test("returns an object with the correct keys and values", () => {
const app = {
component_server: async (id: number, fn: string, args: any) => {
return args;
}
} as client_return;
const result = process_server_fn(1, ["fn1", "fn2"], app);
expect(Object.keys(result)).toEqual(["fn1", "fn2"]);
expect(result.fn1).toBeInstanceOf(Function);
expect(result.fn2).toBeInstanceOf(Function);
});
test("returned server functions should resolve to a promise", async () => {
const app = {
component_server: async (id: number, fn: string, args: any) => {
return args;
}
} as client_return;
const result = process_server_fn(1, ["fn1", "fn2"], app);
const response = result.fn1("hello");
expect(response).toBeInstanceOf(Promise);
});
test("the functions call the clients component_server function with the correct arguments ", async () => {
const mock = spy(async (id: number, fn: string, args: any) => {
return args;
});
const app = {
component_server: mock as any
} as client_return;
const result = process_server_fn(1, ["fn1", "fn2"], app as client_return);
const response = await result.fn1("hello");
expect(response).toBe("hello");
expect(mock.calls).toEqual([[1, "fn1", "hello"]]);
});
test("if there are no server functions, it returns an empty object", () => {
const result = process_server_fn(1, undefined, {} as any);
expect(result).toEqual({});
});
});
describe("get_component", () => {
test("returns an object", () => {
const result = get_component("test-component-one", "class_id", "root", []);
expect(result.component).toBeTypeOf("object");
});
test("returns an object with the correct keys", () => {
const result = get_component("test-component-one", "class_id", "root", []);
expect(Object.keys(result)).toEqual([
"component",
"name",
"example_components"
]);
});
test("the component key is a promise", () => {
const result = get_component("test-component-one", "class_id", "root", []);
expect(result.component).toBeInstanceOf(Promise);
});
test("the resolved component key is an object", async () => {
const result = get_component("test-component-one", "class_id", "root", []);
const o = await result.component;
expect(o).toBeTypeOf("object");
});
test("getting the same component twice should return the same promise", () => {
const result = get_component("test-component-one", "class_id", "root", []);
const result_two = get_component(
"test-component-one",
"class_id",
"root",
[]
);
expect(result.component).toBe(result_two.component);
});
test("if example components are not provided, the example_components key is undefined", async () => {
const result = get_component("dataset", "class_id", "root", []);
expect(result.example_components).toBe(undefined);
});
test("if the type is not a dataset, the example_components key is undefined", async () => {
const result = get_component("test-component-one", "class_id", "root", []);
expect(result.example_components).toBe(undefined);
});
test("when the type is a dataset, returns an object with the correct keys and values and example components", () => {
const result = get_component(
"dataset",
"class_id",
"root",
[
{
type: "test-component-one",
component_class_id: "example_class_id",
id: 1,
props: {
value: "hi",
interactive: false
},
has_modes: false,
instance: {} as any,
component: {} as any
}
],
["test-component-one"]
);
expect(result.component).toBeTypeOf("object");
expect(result.example_components).toBeInstanceOf(Map);
});
test("when example components are returned, returns an object with the correct keys and values and example components", () => {
const result = get_component(
"dataset",
"class_id",
"root",
[
{
type: "test-component-one",
component_class_id: "example_class_id",
id: 1,
props: {
value: "hi",
interactive: false
},
has_modes: false,
instance: {} as any,
component: {} as any
}
],
["test-component-one"]
);
expect(result.example_components?.get("test-component-one")).toBeTypeOf(
"object"
);
expect(result.example_components?.get("test-component-one")).toBeInstanceOf(
Promise
);
});
test("if the component is not found then it should request the component from the server", async () => {
const api_url = "example.com";
const id = "test-random";
const variant = "component";
const handlers = [
http.get(
`${api_url}/custom_component/${id}/client/${variant}/style.css`,
() => {
return new HttpResponse('console.log("boo")', {
status: 200,
headers: {
"Content-Type": "text/css"
}
});
}
)
];
// vi.mock calls are always hoisted out of the test function to the top of the file
// so we need to use vi.hoisted to hoist the mock function above the vi.mock call
const { mock } = vi.hoisted(() => {
return { mock: vi.fn() };
});
vi.mock(
`example.com/custom_component/test-random/client/component/index.js`,
async () => {
mock();
return {
default: {
default: "HELLO"
}
};
}
);
const server = setupServer(...handlers);
server.listen();
await get_component("test-random", id, api_url, []).component;
expect(mock).toHaveBeenCalled();
server.close();
});
});