console/src/Spinner.test.ts

288 lines
7.6 KiB
TypeScript

import { beforeEach, expect, test } from "bun:test";
import { reset, expectCalls, getWidgets } from "./test/widget-mock.ts";
const { Spinner, defaultSpinnerOptions, withSpinner } = await import("@paperclover/console/Spinner");
type Spinner<T extends Record<string, unknown> = Record<string, unknown>> = import("@paperclover/console/Spinner").Spinner<T>;
beforeEach(reset);
test("default options and rendering", () => {
const spinner = new Spinner("spin");
expect(spinner.props).toEqual({});
expect(spinner.fps).toEqual(defaultSpinnerOptions.fps);
expect(spinner.frames).toEqual(defaultSpinnerOptions.frames);
expect(defaultSpinnerOptions.frames).toHaveLength(10);
let now = 0;
for (let i = 0; i < spinner.frames.length; i++) {
expect(spinner.format(now)).toEqual(`\x1b[94m${spinner.frames[i]}\x1b[39m spin`);
now += 1000 / spinner.fps;
}
spinner.text = "something";
expectCalls(spinner, {
redraws: 1,
});
});
test("custom options apply correctly", () => {
const customFrames = ['A', 'B', 'C'];
const customFps = 24;
const spinner = new Spinner({
text: "custom spinner",
frames: customFrames,
fps: customFps
});
expect(spinner.frames).toEqual(customFrames);
expect(spinner.fps).toEqual(customFps);
expect(spinner.text).toEqual("custom spinner");
let now = 0;
for (let i = 0; i < spinner.frames.length; i++) {
expect(spinner.format(now)).toEqual(`\x1b[94m${spinner.frames[i]}\x1b[39m custom spinner`);
now += 1000 / spinner.fps;
}
});
test("custom color options", () => {
// Test with named color
const redSpinner = new Spinner({
text: "red spinner",
color: "red"
});
expect(redSpinner.format(0)).toEqual(`\x1b[31m${redSpinner.frames[0]}\x1b[39m red spinner`);
// Test with hex color
const hexSpinner = new Spinner({
text: "hex spinner",
color: "#ff00ff"
});
expect(hexSpinner.format(0)).toContain(hexSpinner.frames[0]);
expect(hexSpinner.format(0)).toContain("hex spinner");
// Test with rgb array
const rgbSpinner = new Spinner({
text: "rgb spinner",
color: [255, 0, 255]
});
expect(rgbSpinner.format(0)).toContain(rgbSpinner.frames[0]);
expect(rgbSpinner.format(0)).toContain("rgb spinner");
// Test with color disabled
const noColorSpinner = new Spinner({
text: "no color",
color: false
});
expect(noColorSpinner.format(0)).toEqual(`${noColorSpinner.frames[0]} no color`);
});
test("invalid color throws error", () => {
expect(() => {
new Spinner({
text: "invalid color",
color: "invalidColor" as any
});
}).toThrow("Invalid color: invalidColor");
});
test("text getter and setter", () => {
const spinner = new Spinner("initial text");
expect(spinner.text).toEqual("initial text");
spinner.text = "updated text";
expect(spinner.text).toEqual("updated text");
expectCalls(spinner, {
redraws: 1
});
});
test("function for text field and props getter and setter", () => {
const textFn = (props: { count: number }) => `Items: ${props.count}`;
const spinner = new Spinner<{ count: number; newProp?: string }>({
text: textFn,
props: { count: 5 }
});
expect(spinner.text).toEqual("Items: 5");
expect(spinner.props).toEqual({ count: 5 });
spinner.props = { count: 10 };
expect(spinner.text).toEqual("Items: 10");
expect(spinner.props).toEqual({ count: 10 });
expectCalls(spinner, {
redraws: 1
});
// Adding new props should keep existing ones
spinner.props = { newProp: "value" };
expect(spinner.props).toEqual({ count: 10, newProp: "value" });
expectCalls(spinner, {
redraws: 2
});
});
test("update method with string", () => {
const spinner = new Spinner("initial");
spinner.update("updated via update");
expect(spinner.text).toEqual("updated via update");
expectCalls(spinner, {
redraws: 1
});
});
test("update method with props", () => {
const textFn = (props: { count: number, name?: string }) =>
`${props.name || 'Items'}: ${props.count}`;
const spinner = new Spinner<{ count: number, name?: string }>({
text: textFn,
props: { count: 0 }
});
expect(spinner.text).toEqual("Items: 0");
spinner.update({ count: 5 });
expect(spinner.text).toEqual("Items: 5");
spinner.update({ name: "Products" });
expect(spinner.text).toEqual("Products: 5");
expectCalls(spinner, {
redraws: 2
});
});
test("success method", () => {
const spinner = new Spinner("working");
spinner.success();
expectCalls(spinner, {
successCalls: ["working"]
});
const spinner2 = new Spinner("working");
spinner2.success("completed");
expectCalls(spinner2, {
successCalls: ["completed"]
});
});
test("error method", () => {
const spinner = new Spinner("working");
spinner.error();
expectCalls(spinner, {
errorCalls: ["working"]
});
const spinner2 = new Spinner("working");
spinner2.error("failed");
expectCalls(spinner2, {
errorCalls: ["failed"]
});
const error = new Error("Something went wrong");
const spinner3 = new Spinner("working");
spinner3.error(error);
expectCalls(spinner3, {
errorCalls: [error]
});
});
test("withSpinner function", async () => {
const expectedResult = Symbol("unique");
let spin!: Spinner;
const result = await withSpinner("Loading data", async (spinner) => {
spin = spinner;
expect(spinner.text).toEqual("Loading data");
return expectedResult;
});
expect(result).toEqual(expectedResult);
expectCalls(spin, {
successCalls: ["Completed"],
});
});
test("withSpinner function with custom success text", async () => {
let spin!: Spinner;
const result = await withSpinner({
text: "Loading",
successText: "Data loaded successfully"
}, async (spinner) => {
spin = spinner;
return "data";
});
expect(result).toEqual("data");
expectCalls(spin, {
successCalls: ["Data loaded successfully"],
});
});
test("withSpinner function with success text function", async () => {
let spin!: Spinner;
const result = await withSpinner({
text: "Loading",
successText: (data: number[]) => `Loaded ${data.length} items`
}, async (spinner) => {
spin = spinner;
return [1, 2, 3];
});
expect(result).toEqual([1, 2, 3]);
expectCalls(spin, {
successCalls: ["Loaded 3 items"],
});
});
test("withSpinner function with error", async () => {
const error = new Error("Failed to load data");
let spin!: Spinner;
try {
await withSpinner({
text: "Loading",
failureText: "Could not load data"
}, async (spinner) => {
spin = spinner;
throw error;
});
expect.unreachable();
} catch (e) {
expect(e).toEqual(error);
expectCalls(spin, {
errorCalls: ["Could not load data"]
});
}
});
test("withSpinner function with error text function", async () => {
const error = new Error("Network error");
let spin!: Spinner;
try {
await withSpinner({
text: "Loading",
failureText: (err) => `Error: ${err.message}`
}, async (spinner) => {
spin = spinner;
throw error;
});
expect.unreachable();
} catch (e) {
expect(e).toEqual(error);
expectCalls(spin, {
errorCalls: ["Error: Network error"]
});
}
});