State reads three ways on every card -- a stripe, a shaped-and-labelled pill, and the copy -- so it survives a colourblind reader and a glance at arm's length. Colour alone would fail on the one screen where getting it wrong costs money. The expired card leads with what still works, because that is the first thing a worried customer wants to know and the backend really does keep servers, monitors and agent keys running. The awaiting-link card is deliberately loud: a customer who has paid and not linked has paid for nothing yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
78 lines
2.9 KiB
TypeScript
78 lines
2.9 KiB
TypeScript
import { render, screen } from "@testing-library/react";
|
|
import { describe, expect, it } from "vitest";
|
|
import { InstanceCard } from "./InstanceCard";
|
|
import type { Instance, License } from "@/lib/api";
|
|
|
|
const base: Instance = {
|
|
instance_id: "6a0fe3f0-49d2-4aa1-967c-a3094b200b5d",
|
|
account_id: "a1",
|
|
name: "Acme Production",
|
|
slug: "acme",
|
|
deployment: "cloud",
|
|
tier: "professional",
|
|
status: "active",
|
|
current_license: "l1",
|
|
relink_count: 0,
|
|
created_at: "2026-01-01T00:00:00Z",
|
|
};
|
|
|
|
function licence(daysFromNow: number): License {
|
|
return {
|
|
license_id: "l1",
|
|
instance_id: base.instance_id,
|
|
account_id: "a1",
|
|
tier: "professional",
|
|
deployment: "cloud",
|
|
limits: { max_servers: -1, max_secret_groups: -1, max_channels: -1 },
|
|
features: ["console", "oidc"],
|
|
issued_at: "2026-01-01T00:00:00Z",
|
|
expires_at: new Date(Date.now() + daysFromNow * 86_400_000).toISOString(),
|
|
issued_by: "staff@example.com",
|
|
reason: "new",
|
|
};
|
|
}
|
|
|
|
describe("InstanceCard", () => {
|
|
it("shows a valid licence with days remaining", () => {
|
|
render(<InstanceCard instance={base} license={licence(367)} />);
|
|
expect(screen.getByText("Valid")).toBeInTheDocument();
|
|
expect(screen.getByText(/367 days remaining/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("warns inside fourteen days", () => {
|
|
render(<InstanceCard instance={base} license={licence(9)} />);
|
|
expect(screen.getByText("Expiring")).toBeInTheDocument();
|
|
expect(screen.getByText(/9 days remaining/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("names what still works when expired", () => {
|
|
render(<InstanceCard instance={base} license={licence(-3)} />);
|
|
expect(screen.getByText("Expired")).toBeInTheDocument();
|
|
// The reassurance is the point: this is the first thing a worried
|
|
// customer needs, and the backend really does keep these running.
|
|
expect(screen.getByText(/servers and monitors are still running/i)).toBeInTheDocument();
|
|
expect(screen.getByText(/changes are disabled/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it("prompts to link when paid but never linked", () => {
|
|
render(
|
|
<InstanceCard
|
|
instance={{
|
|
...base,
|
|
status: "awaiting_link",
|
|
deployment: "self_hosted",
|
|
current_license: undefined,
|
|
}}
|
|
/>,
|
|
);
|
|
expect(screen.getByText("Awaiting link")).toBeInTheDocument();
|
|
expect(screen.getByText(/not attached to an install yet/i)).toBeInTheDocument();
|
|
expect(screen.getByRole("link", { name: /link an install/i })).toBeInTheDocument();
|
|
});
|
|
|
|
it("links to the instance's own subdomain for cloud", () => {
|
|
render(<InstanceCard instance={base} license={licence(30)} />);
|
|
expect(screen.getByRole("link", { name: /open/i })).toBeInTheDocument();
|
|
});
|
|
});
|