import { describe, expect, it } from "vitest";
import { createAction } from "./action.svelte";

describe("createAction", () => {
	it("derives ergonomic projections from one state", async () => {
		let resolve!: (value: string) => void;
		const action = createAction(() => new Promise<string>((done) => (resolve = done)));

		expect(action.loading).toBe(false);
		expect(action.data).toBeUndefined();
		expect(action.error).toBeUndefined();

		const running = action.run();
		expect(action.loading).toBe(true);

		resolve("done");
		await running;
		expect(action.data).toBe("done");
	});

	it("converts failures", async () => {
		const action = createAction(
			async () => {
				throw new Error("nope");
			},
			(cause) => (cause instanceof Error ? cause.message : "unknown")
		);

		await action.run();
		expect(action.error).toBe("nope");
	});

	it("ignores an older result after a newer run starts", async () => {
		const resolvers: Array<(value: string) => void> = [];
		const names: string[] = [];
		const action = createAction(
			(name: string) =>
				new Promise<string>((resolve) => {
					names.push(name);
					resolvers.push(resolve);
				})
		);
		const first = action.run("first");
		const second = action.run("second");
		expect(names).toEqual(["first", "second"]);
		expect(action.args).toEqual(["second"]);

		resolvers[0]("stale");
		await first;
		expect(action.loading).toBe(true);

		resolvers[1]("current");
		await second;
		expect(action.data).toBe("current");
		expect(action.args).toBeUndefined();
	});
});
