feat: refresh and edit browser instances
This commit is contained in:
@@ -2,6 +2,7 @@ package ui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -48,6 +49,12 @@ func TestShellInstanceRowsContainRequiredColumnsAndIndependentActionControls(t *
|
||||
if shell.deleteClickFor(shell.rows[0].ID) == shell.deleteClickFor(shell.rows[1].ID) {
|
||||
t.Fatal("rows share a delete button state")
|
||||
}
|
||||
if shell.editClickFor(shell.rows[0].ID) == shell.editClickFor(shell.rows[1].ID) {
|
||||
t.Fatal("rows share an edit button state")
|
||||
}
|
||||
if shell.rowClickFor(shell.rows[0].ID) == shell.rowClickFor(shell.rows[1].ID) {
|
||||
t.Fatal("rows share a row interaction state")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstanceListColumnWeightsPrioritizeDirectoryAndCompactAction(t *testing.T) {
|
||||
@@ -331,3 +338,108 @@ func TestShellAppliesSelectedInstanceDirectory(t *testing.T) {
|
||||
t.Fatalf("instance dir = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellRefreshesConfiguredInstancesAsynchronously(t *testing.T) {
|
||||
shell := NewShell(material.NewTheme())
|
||||
target := shell.rows[3]
|
||||
seen := make(chan []InstanceRow, 1)
|
||||
shell.OnRefreshInstances(func(_ context.Context, rows []InstanceRow) ([]InstanceRefreshResult, error) {
|
||||
seen <- rows
|
||||
return []InstanceRefreshResult{{ID: target.ID, Status: "运行中", PID: 5432, RemoteDebugPort: 9777, OccupancySource: "chub_registry"}}, nil
|
||||
}, nil)
|
||||
|
||||
shell.requestRefresh()
|
||||
if !shell.refreshClickState.running {
|
||||
t.Fatal("refresh did not enter running state")
|
||||
}
|
||||
select {
|
||||
case rows := <-seen:
|
||||
if len(rows) != len(shell.rows) || rows[0].ID != shell.rows[0].ID {
|
||||
t.Fatalf("refresh snapshot = %#v", rows)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("refresh callback was not invoked")
|
||||
}
|
||||
var result instanceRefreshResult
|
||||
select {
|
||||
case result = <-shell.refreshResults:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("refresh result was not produced")
|
||||
}
|
||||
shell.refreshResults <- result
|
||||
shell.consumeRefreshResults()
|
||||
row, ok := shell.instanceRow(target.ID)
|
||||
if !ok || shell.refreshClickState.running || row.Status != "运行中" || row.PID != 5432 || row.RemoteDebugPort != 9777 {
|
||||
t.Fatalf("refreshed row = %#v, running=%v", row, shell.refreshClickState.running)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellIgnoresRefreshResultAfterEditableFieldsChange(t *testing.T) {
|
||||
shell := NewShell(material.NewTheme())
|
||||
target := shell.rows[3]
|
||||
shell.OnRefreshInstances(func(_ context.Context, _ []InstanceRow) ([]InstanceRefreshResult, error) {
|
||||
return []InstanceRefreshResult{{ID: target.ID, Status: "运行中", PID: 5432}}, nil
|
||||
}, nil)
|
||||
shell.requestRefresh()
|
||||
var result instanceRefreshResult
|
||||
select {
|
||||
case result = <-shell.refreshResults:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("refresh result was not produced")
|
||||
}
|
||||
shell.rows[3].Name = "已编辑的实例"
|
||||
shell.refreshResults <- result
|
||||
shell.consumeRefreshResults()
|
||||
row, ok := shell.instanceRow(target.ID)
|
||||
if !ok || row.Status != "已退出" || row.PID != 0 {
|
||||
t.Fatalf("stale refresh overwrote edited row: %#v", row)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellEditsExitedInstanceAndPreservesRuntimePort(t *testing.T) {
|
||||
shell := NewShell(material.NewTheme())
|
||||
target := shell.rows[3]
|
||||
updatedDir := t.TempDir()
|
||||
changes := 0
|
||||
shell.OnInstancesChanged(func([]InstanceRow) { changes++ })
|
||||
|
||||
shell.beginEdit(target.ID)
|
||||
shell.editName.SetText("已更新实例")
|
||||
shell.editDir.SetText(updatedDir)
|
||||
shell.editURL.SetText("https://example.com/updated")
|
||||
shell.editBrowserKind.Value = "chrome"
|
||||
shell.saveEdit()
|
||||
|
||||
row, ok := shell.instanceRow(target.ID)
|
||||
if !ok || shell.editingID != "" || changes != 1 || row.Name != "已更新实例" || row.Browser != "Chrome" || row.UserDataDir != filepath.Clean(updatedDir) {
|
||||
t.Fatalf("saved instance = %#v, changes=%d", row, changes)
|
||||
}
|
||||
if row.TargetURL != "https://example.com/updated" {
|
||||
t.Fatalf("target URL = %q", row.TargetURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellLocksIdentityFieldsForRunningInstance(t *testing.T) {
|
||||
shell := NewShell(material.NewTheme())
|
||||
target := shell.rows[0]
|
||||
shell.beginEdit(target.ID)
|
||||
shell.editName.SetText("运行中更新名称")
|
||||
shell.editDir.SetText(t.TempDir())
|
||||
shell.editBrowserKind.Value = "edge"
|
||||
shell.saveEdit()
|
||||
|
||||
row, ok := shell.instanceRow(target.ID)
|
||||
if !ok || row.Name != "运行中更新名称" || row.UserDataDir != target.UserDataDir || row.Browser != target.Browser {
|
||||
t.Fatalf("running instance identity changed: %#v", row)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellRequestsDiscardConfirmationForDirtyEdit(t *testing.T) {
|
||||
shell := NewShell(material.NewTheme())
|
||||
shell.beginEdit(shell.rows[3].ID)
|
||||
shell.editName.SetText("未保存的更改")
|
||||
shell.cancelEdit()
|
||||
if !shell.pendingEditDiscard || shell.editingID == "" {
|
||||
t.Fatalf("dirty edit cancellation state = discard %v editing %q", shell.pendingEditDiscard, shell.editingID)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user