feat: add local cdp startup and external association

This commit is contained in:
QiuSW
2026-07-25 15:53:31 +08:00
parent 972c637716
commit 4063b851eb
17 changed files with 845 additions and 158 deletions
+44 -9
View File
@@ -16,7 +16,7 @@ func TestShellStartsWithSemanticInstanceStatuses(t *testing.T) {
for _, row := range shell.rows {
seen[row.Status] = true
}
for _, status := range []string{"运行中", "启动中", "外部占用", "已退出"} {
for _, status := range []string{"运行中", "启动中", "外部已关联", "已退出"} {
if !seen[status] {
t.Fatalf("status %q is missing", status)
}
@@ -51,7 +51,7 @@ func TestShellInstanceRowsContainRequiredColumnsAndIndependentActionControls(t *
}
func TestInstanceListColumnWeightsPrioritizeDirectoryAndCompactAction(t *testing.T) {
got := instanceNameColumnWeight + instanceBrowserColumnWeight + instanceDirectoryColumnWeight + instanceStatusColumnWeight + instanceActionColumnWeight
got := instanceNameColumnWeight + instanceBrowserColumnWeight + instanceDirectoryColumnWeight + instancePortColumnWeight + instanceStatusColumnWeight + instanceActionColumnWeight
if got != 100 {
t.Fatalf("instance column weights = %d, want 100", got)
}
@@ -120,9 +120,9 @@ func TestShellStartsInstanceAsynchronouslyAndAppliesResult(t *testing.T) {
shell := NewShell(material.NewTheme())
target := shell.rows[0]
started := make(chan InstanceRow, 1)
shell.OnStartInstance(func(_ context.Context, row InstanceRow, _ SettingsState) (int, error) {
shell.OnStartInstance(func(_ context.Context, row InstanceRow, _ SettingsState) (InstanceStartOutcome, error) {
started <- row
return 4242, nil
return InstanceStartOutcome{PID: 4242, RemoteDebugPort: 9666}, nil
}, nil)
shell.requestStart(target.ID)
@@ -145,7 +145,7 @@ func TestShellStartsInstanceAsynchronouslyAndAppliesResult(t *testing.T) {
}
shell.startResults <- result
shell.consumeStartResults()
if row, ok := shell.instanceRow(target.ID); !ok || row.Status != "运行中" {
if row, ok := shell.instanceRow(target.ID); !ok || row.Status != "运行中" || row.PID != 4242 || row.RemoteDebugPort != 9666 {
t.Fatalf("completed start status = %#v, want 运行中", row)
}
}
@@ -156,11 +156,11 @@ func TestShellDoesNotStartTheSameInstanceTwiceWhilePending(t *testing.T) {
entered := make(chan struct{}, 1)
release := make(chan struct{})
var calls atomic.Int32
shell.OnStartInstance(func(_ context.Context, _ InstanceRow, _ SettingsState) (int, error) {
shell.OnStartInstance(func(_ context.Context, _ InstanceRow, _ SettingsState) (InstanceStartOutcome, error) {
calls.Add(1)
entered <- struct{}{}
<-release
return 4242, nil
return InstanceStartOutcome{PID: 4242, RemoteDebugPort: 9666}, nil
}, nil)
shell.requestStart(target.ID)
@@ -230,12 +230,47 @@ func TestShellAppliesLatestPathSearchResult(t *testing.T) {
func TestShellSettingsCanBeRestored(t *testing.T) {
shell := NewShell(material.NewTheme())
shell.SetSettings(SettingsState{ChromePath: "chrome", EdgePath: "edge", DefaultDir: "profiles", LogDir: "logs", CloseOnExit: false})
if shell.chromePath.Text() != "chrome" || shell.edgePath.Text() != "edge" || shell.dataDir.Text() != "profiles" || shell.logDir.Text() != "logs" || shell.closeOnExit.Value {
shell.SetSettings(SettingsState{ChromePath: "chrome", EdgePath: "edge", DefaultDir: "profiles", LogDir: "logs", RemoteDebugStartPort: 9777, CloseOnExit: false})
if shell.chromePath.Text() != "chrome" || shell.edgePath.Text() != "edge" || shell.dataDir.Text() != "profiles" || shell.logDir.Text() != "logs" || shell.remoteDebugPort.Text() != "9777" || shell.closeOnExit.Value {
t.Fatalf("settings were not restored")
}
}
func TestShellNormalizesRemoteDebugStartPort(t *testing.T) {
shell := NewShell(material.NewTheme())
shell.remoteDebugPort.SetText("")
settings, err := shell.settingsState()
if err != nil || settings.RemoteDebugStartPort != 9666 {
t.Fatalf("empty port settings = %#v, error = %v", settings, err)
}
shell.remoteDebugPort.SetText("80")
if _, err := shell.settingsState(); err == nil {
t.Fatal("invalid remote debug port was accepted")
}
}
func TestShellMarksExternalAssociationWithoutManagingLifecycle(t *testing.T) {
shell := NewShell(material.NewTheme())
target := shell.rows[2]
shell.OnStartInstance(func(_ context.Context, _ InstanceRow, _ SettingsState) (InstanceStartOutcome, error) {
return InstanceStartOutcome{PID: 16108, RemoteDebugPort: 9668, External: true, Source: "browser_message_window"}, nil
}, nil)
shell.requestStart(target.ID)
var result instanceStartResult
select {
case result = <-shell.startResults:
case <-time.After(time.Second):
t.Fatal("external association result was not produced")
}
shell.startResults <- result
shell.consumeStartResults()
row, ok := shell.instanceRow(target.ID)
if !ok || row.Status != "外部已关联" || row.PID != 16108 || row.RemoteDebugPort != 9668 || row.OccupancySource != "browser_message_window" {
t.Fatalf("external association = %#v", row)
}
}
func TestShellKeepsSettingsStateAcrossPageSwitch(t *testing.T) {
shell := NewShell(material.NewTheme())
shell.page = pageSettings