mirror of
https://github.com/boolean-maybe/tiki
synced 2026-04-21 13:37:20 +00:00
fix action firing on printable character
This commit is contained in:
parent
8838c29149
commit
48e796877e
2 changed files with 222 additions and 4 deletions
|
|
@ -113,11 +113,12 @@ func (ir *InputRouter) HandleInput(event *tcell.EventKey, currentView *ViewEntry
|
|||
}
|
||||
}
|
||||
|
||||
// pre-gate: ActionOpenPalette (*) and ActionToggleHeader (F10) must fire before
|
||||
// task-edit Prepare and before search/fullscreen/editor gates, so they stay truly
|
||||
// global without triggering edit-session setup or focus churn.
|
||||
// pre-gate: global actions that must fire before task-edit Prepare() and before
|
||||
// search/fullscreen/editor gates. ActionOpenPalette is suppressed in TaskEditView
|
||||
// because '*' is a typeable rune that should be inserted into fields.
|
||||
if action := ir.globalActions.Match(event); action != nil {
|
||||
if action.ID == ActionOpenPalette || action.ID == ActionToggleHeader {
|
||||
isTaskEdit := currentView != nil && currentView.ViewID == model.TaskEditViewID
|
||||
if action.ID == ActionToggleHeader || (action.ID == ActionOpenPalette && !isTaskEdit) {
|
||||
return ir.handleGlobalAction(action.ID)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/boolean-maybe/tiki/model"
|
||||
taskpkg "github.com/boolean-maybe/tiki/task"
|
||||
"github.com/boolean-maybe/tiki/testutil"
|
||||
"github.com/boolean-maybe/tiki/view/taskdetail"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
|
@ -93,3 +97,216 @@ func TestActionPalette_AsteriskFiltersInPalette(t *testing.T) {
|
|||
|
||||
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
||||
}
|
||||
|
||||
// getEditView type-asserts the active view to *taskdetail.TaskEditView.
|
||||
func getEditView(t *testing.T, ta *testutil.TestApp) *taskdetail.TaskEditView {
|
||||
t.Helper()
|
||||
v := ta.NavController.GetActiveView()
|
||||
ev, ok := v.(*taskdetail.TaskEditView)
|
||||
if !ok {
|
||||
t.Fatalf("expected *taskdetail.TaskEditView, got %T", v)
|
||||
}
|
||||
return ev
|
||||
}
|
||||
|
||||
func TestActionPalette_BlockedInTaskEdit_DraftFirstKey(t *testing.T) {
|
||||
ta := testutil.NewTestApp(t)
|
||||
defer ta.Cleanup()
|
||||
|
||||
ta.NavController.PushView(model.MakePluginViewID("Kanban"), nil)
|
||||
ta.Draw()
|
||||
|
||||
// press 'n' to create new task (draft path, title focused)
|
||||
ta.SendKey(tcell.KeyRune, 'n', tcell.ModNone)
|
||||
|
||||
// very first key in edit view is '*'
|
||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
||||
|
||||
if ta.GetPaletteConfig().IsVisible() {
|
||||
t.Fatal("palette should NOT open when '*' is pressed in task edit (draft, first key)")
|
||||
}
|
||||
ev := getEditView(t, ta)
|
||||
if got := ev.GetEditedTitle(); got != "*" {
|
||||
t.Fatalf("title = %q, want %q", got, "*")
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionPalette_BlockedInTaskEdit_ExistingFirstKey(t *testing.T) {
|
||||
ta := testutil.NewTestApp(t)
|
||||
defer ta.Cleanup()
|
||||
|
||||
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
||||
t.Fatalf("create task: %v", err)
|
||||
}
|
||||
if err := ta.TaskStore.Reload(); err != nil {
|
||||
t.Fatalf("reload: %v", err)
|
||||
}
|
||||
|
||||
// push task edit directly (non-draft path)
|
||||
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
||||
TaskID: "TIKI-1",
|
||||
Focus: model.EditFieldTitle,
|
||||
}))
|
||||
ta.Draw()
|
||||
|
||||
// first key is '*'
|
||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
||||
|
||||
if ta.GetPaletteConfig().IsVisible() {
|
||||
t.Fatal("palette should NOT open when '*' is pressed in task edit (existing, first key)")
|
||||
}
|
||||
ev := getEditView(t, ta)
|
||||
if got := ev.GetEditedTitle(); got != "Test*" {
|
||||
t.Fatalf("title = %q, want %q", got, "Test*")
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionPalette_BlockedInTaskEdit_TitleMidText(t *testing.T) {
|
||||
ta := testutil.NewTestApp(t)
|
||||
defer ta.Cleanup()
|
||||
|
||||
ta.NavController.PushView(model.MakePluginViewID("Kanban"), nil)
|
||||
ta.Draw()
|
||||
|
||||
ta.SendKey(tcell.KeyRune, 'n', tcell.ModNone)
|
||||
ta.SendText("ab*cd")
|
||||
|
||||
if ta.GetPaletteConfig().IsVisible() {
|
||||
t.Fatal("palette should NOT open when '*' is typed mid-title")
|
||||
}
|
||||
ev := getEditView(t, ta)
|
||||
if got := ev.GetEditedTitle(); got != "ab*cd" {
|
||||
t.Fatalf("title = %q, want %q", got, "ab*cd")
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionPalette_BlockedInTaskEdit_Description(t *testing.T) {
|
||||
ta := testutil.NewTestApp(t)
|
||||
defer ta.Cleanup()
|
||||
|
||||
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
||||
t.Fatalf("create task: %v", err)
|
||||
}
|
||||
if err := ta.TaskStore.Reload(); err != nil {
|
||||
t.Fatalf("reload: %v", err)
|
||||
}
|
||||
|
||||
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
||||
TaskID: "TIKI-1",
|
||||
Focus: model.EditFieldDescription,
|
||||
DescOnly: true,
|
||||
}))
|
||||
ta.Draw()
|
||||
|
||||
ta.SendText("x*y")
|
||||
|
||||
if ta.GetPaletteConfig().IsVisible() {
|
||||
t.Fatal("palette should NOT open when '*' is typed in description")
|
||||
}
|
||||
ev := getEditView(t, ta)
|
||||
desc := ev.GetEditedDescription()
|
||||
if !strings.Contains(desc, "x*y") {
|
||||
t.Fatalf("description = %q, should contain %q", desc, "x*y")
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionPalette_BlockedInTaskEdit_Tags(t *testing.T) {
|
||||
ta := testutil.NewTestApp(t)
|
||||
defer ta.Cleanup()
|
||||
|
||||
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
||||
t.Fatalf("create task: %v", err)
|
||||
}
|
||||
if err := ta.TaskStore.Reload(); err != nil {
|
||||
t.Fatalf("reload: %v", err)
|
||||
}
|
||||
|
||||
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
||||
TaskID: "TIKI-1",
|
||||
TagsOnly: true,
|
||||
}))
|
||||
ta.Draw()
|
||||
|
||||
ta.SendText("tag*val")
|
||||
|
||||
if ta.GetPaletteConfig().IsVisible() {
|
||||
t.Fatal("palette should NOT open when '*' is typed in tags")
|
||||
}
|
||||
ev := getEditView(t, ta)
|
||||
tags := ev.GetEditedTags()
|
||||
if len(tags) != 1 || tags[0] != "tag*val" {
|
||||
t.Fatalf("tags = %v, want [tag*val]", tags)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionPalette_BlockedInTaskEdit_Assignee(t *testing.T) {
|
||||
ta := testutil.NewTestApp(t)
|
||||
defer ta.Cleanup()
|
||||
|
||||
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
||||
t.Fatalf("create task: %v", err)
|
||||
}
|
||||
if err := ta.TaskStore.Reload(); err != nil {
|
||||
t.Fatalf("reload: %v", err)
|
||||
}
|
||||
|
||||
// push task edit (default title focus)
|
||||
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
||||
TaskID: "TIKI-1",
|
||||
Focus: model.EditFieldTitle,
|
||||
}))
|
||||
ta.Draw()
|
||||
|
||||
// tab 5× to Assignee: Title→Status→Type→Priority→Points→Assignee
|
||||
for i := 0; i < 5; i++ {
|
||||
ta.SendKey(tcell.KeyTab, 0, tcell.ModNone)
|
||||
}
|
||||
|
||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
||||
|
||||
if ta.GetPaletteConfig().IsVisible() {
|
||||
t.Fatal("palette should NOT open when '*' is pressed on Assignee field")
|
||||
}
|
||||
editTask := ta.EditingTask()
|
||||
if editTask == nil {
|
||||
t.Fatal("expected editing task to be non-nil")
|
||||
}
|
||||
if editTask.Assignee != "Unassigned*" {
|
||||
t.Fatalf("assignee = %q, want %q ('*' appended to default value)", editTask.Assignee, "Unassigned*")
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionPalette_BlockedInTaskEdit_NonTypeableField(t *testing.T) {
|
||||
ta := testutil.NewTestApp(t)
|
||||
defer ta.Cleanup()
|
||||
|
||||
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
||||
t.Fatalf("create task: %v", err)
|
||||
}
|
||||
if err := ta.TaskStore.Reload(); err != nil {
|
||||
t.Fatalf("reload: %v", err)
|
||||
}
|
||||
|
||||
// push task edit (default title focus)
|
||||
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
||||
TaskID: "TIKI-1",
|
||||
Focus: model.EditFieldTitle,
|
||||
}))
|
||||
ta.Draw()
|
||||
|
||||
// tab 1× to Status (non-typeable field)
|
||||
ta.SendKey(tcell.KeyTab, 0, tcell.ModNone)
|
||||
|
||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
||||
|
||||
if ta.GetPaletteConfig().IsVisible() {
|
||||
t.Fatal("palette should NOT open when '*' is pressed on Status field")
|
||||
}
|
||||
editTask := ta.EditingTask()
|
||||
if editTask == nil {
|
||||
t.Fatal("expected editing task to be non-nil")
|
||||
}
|
||||
if editTask.Status != taskpkg.StatusReady {
|
||||
t.Fatalf("status = %v, want %v (rune should be silently ignored)", editTask.Status, taskpkg.StatusReady)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue