diff --git a/internal/appui/api_views.go b/internal/appui/api_views.go index f400e05..4a53aa7 100644 --- a/internal/appui/api_views.go +++ b/internal/appui/api_views.go @@ -836,8 +836,8 @@ func (u *ui) auditQuickFilterButton(gtx layout.Context, click *widget.Clickable, func (u *ui) apiTokenDetailPanel(gtx layout.Context) layout.Dimensions { token, ok := u.selectedAPIToken() - editClicks := u.ensureAPIPolicyEditClickables(0) - removeClicks := u.ensureAPIPolicyRemoveClickables(0) + var editClicks []widget.Clickable + var removeClicks []widget.Clickable if ok { editClicks = u.ensureAPIPolicyEditClickables(len(token.Policies)) removeClicks = u.ensureAPIPolicyRemoveClickables(len(token.Policies)) diff --git a/internal/appui/frame.go b/internal/appui/frame.go index cee50f6..c70eb5e 100644 --- a/internal/appui/frame.go +++ b/internal/appui/frame.go @@ -1007,14 +1007,16 @@ func (u *ui) handleAPIPolicyClicks(gtx layout.Context) { for u.clearAPIPolicyTarget.Clicked(gtx) { u.runAction("clear API policy target", u.clearAPIPolicyTargetAction) } - for i := range u.apiPolicyEdits { - for u.apiPolicyEdits[i].Clicked(gtx) { + editClicks := u.apiPolicyEdits + for i := range editClicks { + for editClicks[i].Clicked(gtx) { index := i u.runAction("edit API policy rule", func() error { return u.editAPIPolicyRuleAction(index) }) } } - for i := range u.apiPolicyRemoves { - for u.apiPolicyRemoves[i].Clicked(gtx) { + removeClicks := u.apiPolicyRemoves + for i := range removeClicks { + for removeClicks[i].Clicked(gtx) { index := i u.runAction("remove API policy rule", func() error { return u.removeAPIPolicyRuleAction(index) }) } diff --git a/internal/appui/main_test.go b/internal/appui/main_test.go index 75c0c42..8201ad8 100644 --- a/internal/appui/main_test.go +++ b/internal/appui/main_test.go @@ -861,6 +861,72 @@ func TestUIAPITokenPolicyRulesCanBeCreatedUpdatedAndRemoved(t *testing.T) { } } +func TestUIAPITokenPolicyButtonsRemainClickableAfterPanelLayout(t *testing.T) { + t.Parallel() + + u := newUIWithSession("desktop", &session.Manager{}, statePaths{ + DefaultSaveAsPath: filepath.Join(t.TempDir(), "vault.kdbx"), + RecentVaultsPath: filepath.Join(t.TempDir(), "recent-vaults.json"), + RecentRemotesPath: filepath.Join(t.TempDir(), "recent-remotes.json"), + UIPreferencesPath: filepath.Join(t.TempDir(), "ui-prefs.json"), + }) + u.masterPassword.SetText("correct horse battery staple") + if err := u.createVaultAction(); err != nil { + t.Fatalf("createVaultAction() error = %v", err) + } + + u.showAPITokensSection() + u.apiTokenName.SetText("CLI") + u.apiTokenClientName.SetText("grpc-cli") + if err := u.issueAPITokenAction(); err != nil { + t.Fatalf("issueAPITokenAction() error = %v", err) + } + + u.apiPolicyOperation.SetText(string(apitokens.OperationListEntries)) + u.apiPolicyPath.SetText("Root / Internet") + u.apiPolicyAllow.Value = true + u.apiPolicyGroupScopeW.Value = true + if err := u.addAPIPolicyRuleAction(); err != nil { + t.Fatalf("addAPIPolicyRuleAction() error = %v", err) + } + token, ok := u.selectedAPIToken() + if !ok || len(token.Policies) != 1 { + t.Fatalf("selectedAPIToken().Policies before layout = %#v, want 1 rule", token.Policies) + } + if len(u.apiPolicyEdits) != 1 { + t.Fatalf("len(apiPolicyEdits) before layout = %d, want 1", len(u.apiPolicyEdits)) + } + + gtx := layout.Context{ + Ops: new(op.Ops), + Constraints: layout.Exact(image.Pt(800, 600)), + } + _ = u.apiTokenDetailPanel(gtx) + + if len(u.apiPolicyEdits) != 1 { + t.Fatalf("len(apiPolicyEdits) after layout = %d, want 1", len(u.apiPolicyEdits)) + } + u.apiPolicyEdits[0].Click() + u.handleAPIPolicyClicks(layout.Context{}) + if u.selectedAPIPolicyIndex != 0 { + t.Fatalf("selectedAPIPolicyIndex after rendered edit click = %d, want 0", u.selectedAPIPolicyIndex) + } + + u.selectedAPIPolicyIndex = -1 + u.loadSelectedAPITokenIntoEditor() + _ = u.apiTokenDetailPanel(gtx) + if len(u.apiPolicyRemoves) != 1 { + t.Fatalf("len(apiPolicyRemoves) after layout = %d, want 1", len(u.apiPolicyRemoves)) + } + u.apiPolicyRemoves[0].Click() + u.handleAPIPolicyClicks(layout.Context{}) + + token, ok = u.selectedAPIToken() + if !ok || len(token.Policies) != 0 { + t.Fatalf("selectedAPIToken().Policies after rendered remove click = %#v, want empty", token.Policies) + } +} + func TestAPITokenStatusSummary(t *testing.T) { t.Parallel()