#![cfg(unix)] mod common; use common::run_pikl; use pikl_test_macros::pikl_tests; pikl_tests! { headless mod basic_selection { items: ["alpha", "beta", "charlie"]; test confirm_first { actions: [confirm] stdout: "alpha" exit: 0 } test move_down_and_confirm { actions: [move-down, confirm] stdout: "beta" exit: 0 } test move_to_third { actions: [move-down, move-down, confirm] stdout: "charlie" exit: 0 } test cancel_exits_1 { actions: [cancel] stdout: "" exit: 1 } test empty_script_cancels { actions: [] exit: 1 } } headless mod filtering { items: ["alpha", "beta", "banana"]; test filter_then_confirm { actions: [filter "ban", confirm] stdout: "banana" exit: 0 } } headless mod errors { items: ["one", "two"]; test invalid_action { actions: [raw "bogus"] stderr contains: "unknown action" exit: 2 } test actions_after_show_ui { actions: [raw "show-ui", raw "confirm"] stderr contains: "actions after show-ui" exit: 2 } } // ── Demo scenario tests ────────────────────────────── // These cover the scenarios from examples/demo.sh so // we catch regressions in the demo workflows. headless mod demo_plain_text { items: [ "apple", "banana", "cherry", "date", "elderberry", "fig", "grape", "honeydew" ]; test confirm_first { actions: [confirm] stdout: "apple" exit: 0 } test navigate_to_last { actions: [move-to-bottom, confirm] stdout: "honeydew" exit: 0 } test filter_then_confirm { actions: [filter "cher", confirm] stdout: "cherry" exit: 0 } test filter_narrows_to_one { actions: [filter "elder", confirm] stdout: "elderberry" exit: 0 } test cancel { actions: [cancel] exit: 1 } } headless mod demo_big_list { // Bare numbers parse as JSON numbers with empty labels, // so we prefix with "item-" to keep them as plain text. items: [ "item-1", "item-2", "item-3", "item-4", "item-5", "item-6", "item-7", "item-8", "item-9", "item-10", "item-11", "item-12", "item-13", "item-14", "item-15", "item-16", "item-17", "item-18", "item-19", "item-20", "item-50", "item-100", "item-200", "item-499", "item-500" ]; test confirm_first { actions: [confirm] stdout: "item-1" exit: 0 } test move_to_bottom { actions: [move-to-bottom, confirm] stdout: "item-500" exit: 0 } test filter_exact { actions: [filter "item-499", confirm] stdout: "item-499" exit: 0 } test page_down_then_confirm { actions: [page-down, confirm] exit: 0 } } headless mod demo_json_objects { items: [ "{\"label\": \"Arch Linux\", \"category\": \"rolling\", \"init\": \"systemd\"}", "{\"label\": \"NixOS\", \"category\": \"rolling\", \"init\": \"systemd\"}", "{\"label\": \"Void Linux\", \"category\": \"rolling\", \"init\": \"runit\"}", "{\"label\": \"Debian\", \"category\": \"stable\", \"init\": \"systemd\"}", "{\"label\": \"Alpine\", \"category\": \"stable\", \"init\": \"openrc\"}", "{\"label\": \"Fedora\", \"category\": \"semi-rolling\", \"init\": \"systemd\"}", "{\"label\": \"Gentoo\", \"category\": \"rolling\", \"init\": \"openrc\"}" ]; test confirm_first { actions: [confirm] stdout: "Arch Linux" exit: 0 } test preserves_json_fields { actions: [confirm] stdout: "rolling" exit: 0 } test filter_by_label { actions: [filter "Void", confirm] stdout: "Void Linux" exit: 0 } test navigate_to_debian { actions: [move-down, move-down, move-down, confirm] stdout: "Debian" exit: 0 } test filter_and_navigate { actions: [filter "Linux", move-down, confirm] stdout: "Void Linux" exit: 0 } test cancel { actions: [cancel] exit: 1 } } headless mod demo_custom_label_key { items: [ "{\"name\": \"Neovim\", \"type\": \"editor\", \"lang\": \"C/Lua\"}", "{\"name\": \"Helix\", \"type\": \"editor\", \"lang\": \"Rust\"}", "{\"name\": \"Kakoune\", \"type\": \"editor\", \"lang\": \"C++\"}", "{\"name\": \"Emacs\", \"type\": \"editor\", \"lang\": \"Lisp\"}", "{\"name\": \"Vim\", \"type\": \"editor\", \"lang\": \"C\"}" ]; label_key: "name"; test confirm_first { actions: [confirm] stdout: "Neovim" exit: 0 } test filter_by_name { actions: [filter "Hel", confirm] stdout: "Helix" exit: 0 } test preserves_lang_field { actions: [move-down, confirm] stdout: "Rust" exit: 0 } test navigate_to_last { actions: [move-to-bottom, confirm] stdout: "Vim" exit: 0 } } headless mod demo_mixed_input { items: [ "just a plain string", "{\"label\": \"a json object\", \"extra\": 42}", "another plain string", "{\"label\": \"second object\", \"extra\": 99}" ]; test confirm_plain_text { actions: [confirm] stdout: "just a plain string" exit: 0 } test confirm_json_object { actions: [move-down, confirm] stdout: "a json object" exit: 0 } test json_preserves_extra { actions: [move-down, confirm] stdout: "42" exit: 0 } test navigate_to_second_plain { actions: [move-down, move-down, confirm] stdout: "another plain string" exit: 0 } test navigate_to_second_json { actions: [move-down, move-down, move-down, confirm] stdout: "second object" exit: 0 } test filter_across_types { actions: [filter "plain", confirm] stdout: "just a plain string" exit: 0 } } headless mod new_actions { items: ["a","b","c","d","e","f","g","h","i","j"]; test half_page_down_confirm { actions: [raw "half-page-down", confirm] // headless viewport=50, half=25, clamps to last (idx 9) -> "j" stdout: "j" exit: 0 } test set_mode_round_trip { actions: [raw "set-mode normal", raw "set-mode insert", confirm] stdout: "a" exit: 0 } } headless mod multi_select_headless { items: ["alpha", "bravo", "charlie"]; multi: true; test toggle_and_confirm { actions: [toggle-select, confirm] stdout: "alpha" exit: 0 } test toggle_two_and_confirm { actions: [toggle-select, move-down, move-down, toggle-select, confirm] stdout: "alpha" exit: 0 } test select_all_and_confirm { actions: [select-all, confirm] stdout: "alpha" exit: 0 } test deselect_all_clears { actions: [toggle-select, deselect-all, confirm] // Falls back to cursor item stdout: "alpha" exit: 0 } test without_multi_toggle_is_noop { // This test does NOT have multi: true (uses the shared fixture) // but toggle-select still just selects cursor on confirm actions: [toggle-select, confirm] stdout: "alpha" exit: 0 } } headless mod pipeline_headless { items: ["error_log", "warning_temp", "info_log"]; test pipeline_filter { actions: [filter "'log | !error", confirm] stdout: "info_log" exit: 0 } } headless mod modifier_prefix { items: ["alpha", "beta", "gamma"]; test shift_confirm_plain_output_unchanged { // Modifier prefixes don't affect plain text output. actions: [raw "+shift confirm"] stdout: "alpha" exit: 0 } test unknown_modifier_errors { actions: [raw "+meta confirm"] stderr contains: "unknown modifier" exit: 2 } test modifier_with_movement { // Modifiers on navigation don't change behavior, // but they should parse and not break anything. actions: [raw "+shift move-down", confirm] stdout: "beta" exit: 0 } } }