Files
paseo/packages/app/src/components/question-form-card-core.test.ts
T
paseo-bot[bot]andGabriel Giordano 019e5279e9 Show Claude option previews in the question card, readable on Android (#6171)
* Show Claude option previews in the question card

* Wrap long lines in question option previews

* Check option previews under the option that owns them

* Show the whole option preview on native so every line is reachable

On Android, a slow drag inside a preview capped at 240px did not scroll
it, so lines past the cap could not be read. The preview's nested
scroll sits inside the option's Pressable; the same drag scrolls it
once the preview is outside the Pressable. Native now renders the
preview at full height so the chat scroll reaches every line, and a tap
on it still selects the option. Web keeps the 240px cap.

The mock agent's rollout preview is now taller than the cap so the
Android replay can check the last line.

---------

Co-authored-by: Gabriel Giordano <hello@gabrielgiordano.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-10-14 07:13:06 +02:00

176 lines
5.7 KiB
TypeScript

import { describe, expect, test } from "vitest";
import {
areQuestionsAnswered,
buildQuestionFormAnswers,
parseQuestionFormQuestions,
questionShowsTextInput,
resolveDismissLabel,
shouldSubmitEmptyOnDismiss,
} from "./question-form-card-core";
describe("question form card core", () => {
test("treats optional input prompts as skippable empty answers", () => {
const questions = parseQuestionFormQuestions({
questions: [
{
question: "Optional comment?",
header: "Response",
options: [],
multiSelect: false,
placeholder: "Optional comment (press Enter to skip)...",
allowEmpty: true,
dismissLabel: "Skip",
},
],
});
if (!questions) throw new Error("questions did not parse");
expect(areQuestionsAnswered(questions, {}, {})).toBe(true);
expect(buildQuestionFormAnswers(questions, {}, {})).toEqual({ Response: "" });
expect(shouldSubmitEmptyOnDismiss(questions)).toBe(true);
expect(resolveDismissLabel(questions)).toBe("Skip");
});
test("requires a selection for option-only questions", () => {
const questions = parseQuestionFormQuestions({
questions: [
{
question: "Pick one",
header: "Response",
options: [{ label: "A" }, { label: "B" }],
multiSelect: false,
},
],
});
if (!questions) throw new Error("questions did not parse");
const [question] = questions;
if (!question) throw new Error("question missing");
expect(questionShowsTextInput(question)).toBe(false);
expect(areQuestionsAnswered(questions, {}, { 0: "freeform" })).toBe(false);
expect(areQuestionsAnswered(questions, { 0: new Set([1]) }, {})).toBe(true);
expect(buildQuestionFormAnswers(questions, { 0: new Set([1]) }, {})).toEqual({
Response: "B",
});
});
test("keeps checked options and appends the other answer for multi-select", () => {
const questions = parseQuestionFormQuestions({
questions: [
{
question: "Which fruits do you like?",
header: "Fruits",
options: [{ label: "Apple" }, { label: "Banana" }, { label: "Cherry" }],
multiSelect: true,
allowOther: true,
},
],
});
if (!questions) throw new Error("questions did not parse");
expect(buildQuestionFormAnswers(questions, { 0: new Set([0, 2]) }, { 0: " durian " })).toEqual({
Fruits: "Apple, Cherry, durian",
});
expect(buildQuestionFormAnswers(questions, { 0: new Set([0, 2]) }, {})).toEqual({
Fruits: "Apple, Cherry",
});
expect(buildQuestionFormAnswers(questions, { 0: new Set() }, { 0: "durian" })).toEqual({
Fruits: "durian",
});
expect(buildQuestionFormAnswers(questions, {}, { 0: "durian" })).toEqual({ Fruits: "durian" });
});
test("replaces the selected option with the other answer for single-select", () => {
const questions = parseQuestionFormQuestions({
questions: [
{
question: "Which provider?",
header: "Provider",
options: [{ label: "Claude Code" }, { label: "Codex" }],
multiSelect: false,
allowOther: true,
},
],
});
if (!questions) throw new Error("questions did not parse");
expect(buildQuestionFormAnswers(questions, { 0: new Set([1]) }, { 0: "OpenCode" })).toEqual({
Provider: "OpenCode",
});
expect(buildQuestionFormAnswers(questions, { 0: new Set([1]) }, {})).toEqual({
Provider: "Codex",
});
});
test("shows text input for explicit other questions", () => {
const questions = parseQuestionFormQuestions({
questions: [
{
question: "Pick or type",
header: "Response",
options: [{ label: "A" }],
isOther: true,
multiSelect: false,
},
],
});
if (!questions) throw new Error("questions did not parse");
const [question] = questions;
if (!question) throw new Error("question missing");
expect(questionShowsTextInput(question)).toBe(true);
expect(areQuestionsAnswered(questions, {}, { 0: "custom" })).toBe(true);
expect(buildQuestionFormAnswers(questions, {}, { 0: "custom" })).toEqual({
Response: "custom",
});
});
test("shows text input for questions that allow other answers", () => {
const questions = parseQuestionFormQuestions({
questions: [
{
question: "Pick or type",
header: "Response",
options: [{ label: "A" }],
allowOther: true,
multiSelect: false,
},
],
});
if (!questions) throw new Error("questions did not parse");
const [question] = questions;
if (!question) throw new Error("question missing");
expect(questionShowsTextInput(question)).toBe(true);
expect(areQuestionsAnswered(questions, {}, { 0: "custom" })).toBe(true);
expect(buildQuestionFormAnswers(questions, {}, { 0: "custom" })).toEqual({
Response: "custom",
});
});
test("keeps option previews so options can be compared", () => {
const questions = parseQuestionFormQuestions({
questions: [
{
question: "Which layout?",
header: "Layout",
options: [
{
label: "Tabs",
description: "One tab per section",
preview: "<Tabs>\n <Tab />\n</Tabs>",
},
{ label: "Single page" },
],
multiSelect: false,
},
],
});
expect(questions?.[0]?.options).toEqual([
{ label: "Tabs", description: "One tab per section", preview: "<Tabs>\n <Tab />\n</Tabs>" },
{ label: "Single page", description: undefined, preview: undefined },
]);
});
});