mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 16:34:36 +00:00
* 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>
176 lines
5.7 KiB
TypeScript
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 },
|
|
]);
|
|
});
|
|
});
|