Agent role cards always auto-sent a hardcoded "Take a look at the current document" on pick. Make it configurable per role: - autoStart (bool, default true): whether picking the role auto-sends a message. - launchMessage (nullable text): the text sent on auto-start; empty -> the built-in default. autoStart=false -> bind the role and send nothing (the user types the first message, which still carries the roleId). Existing roles default to autoStart=true / launchMessage=null => identical old behavior. Full-stack: - migration 20260624T120000 adds `auto_start boolean NOT NULL DEFAULT true` + `launch_message text` (additive; down drops both); db.d.ts updated by hand. - DTO: autoStart (@IsBoolean) + launchMessage (trim @Transform, @MaxLength 2000). - repo/service: thread + normalize (undefined=unchanged, ""=>null, autoStart??true). Both fields exposed in the picker-view for ordinary members (they decide whether/what to auto-send); instructions/modelConfig stay ADMIN-ONLY. - client: IAiRole types, role form (Switch + Textarea, re-hydrated on edit), handleRolePick branches on autoStart; i18n en-US + ru-RU. Review follow-ups folded in: reset the `rolePickedNoSend` flag when the thread returns to an empty role-less state (the "New chat after autoStart=false pick" stuck-UI bug — render-phase one-shot reset); made create/update launchMessage normalization symmetric (raw value, server normalizes ""→null). Server: 68 role tests pass, tsc clean. Client: tsc clean, role tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
import { describe, it, expect, vi } from "vitest";
|
|
import { render, screen, fireEvent } from "@testing-library/react";
|
|
import { MantineProvider } from "@mantine/core";
|
|
import RoleCards from "./role-cards";
|
|
import { IAiRole } from "@/features/ai-chat/types/ai-chat.types.ts";
|
|
|
|
// matchMedia (read by MantineProvider) is stubbed globally in vitest.setup.ts.
|
|
|
|
const roles: IAiRole[] = [
|
|
{
|
|
id: "r1",
|
|
name: "Pirate",
|
|
emoji: "🏴☠️",
|
|
description: "Talks like a pirate",
|
|
enabled: true,
|
|
autoStart: true,
|
|
launchMessage: null,
|
|
},
|
|
{
|
|
id: "r2",
|
|
name: "Grandpa",
|
|
emoji: null,
|
|
description: null,
|
|
enabled: true,
|
|
autoStart: true,
|
|
launchMessage: null,
|
|
},
|
|
];
|
|
|
|
function renderCards(onPick = vi.fn()) {
|
|
render(
|
|
<MantineProvider>
|
|
<RoleCards roles={roles} onPick={onPick} />
|
|
</MantineProvider>,
|
|
);
|
|
return onPick;
|
|
}
|
|
|
|
describe("RoleCards", () => {
|
|
it("renders one card per role with name, emoji, and description", () => {
|
|
renderCards();
|
|
expect(screen.getByText("Pirate")).toBeDefined();
|
|
expect(screen.getByText("Talks like a pirate")).toBeDefined();
|
|
expect(screen.getByText("Grandpa")).toBeDefined();
|
|
// The emoji is shown for the role that has one.
|
|
expect(screen.getByText("🏴☠️")).toBeDefined();
|
|
});
|
|
|
|
it("does NOT render a Universal assistant card", () => {
|
|
renderCards();
|
|
expect(screen.queryByText("Universal assistant")).toBeNull();
|
|
});
|
|
|
|
it("calls onPick with the role object when a card is clicked", () => {
|
|
const onPick = renderCards();
|
|
fireEvent.click(screen.getByText("Pirate"));
|
|
expect(onPick).toHaveBeenCalledWith(roles[0]);
|
|
});
|
|
});
|