返回 DeepSeek-Reasonix
file_browser_preview_test.go
根目录 / desktop / file_browser_preview_test.go
1 package main
2
3 import (
4 "context"
5 "errors"
6 "os"
7 "path/filepath"
8 "testing"
9
10 "reasonix/internal/browser"
11 )
12
13 type previewTestExecutor struct {
14 tabs map[string]browser.Tab
15 opens []browser.OpenRequest
16 navs []browser.NavigateRequest
17 closes []browser.CloseRequest
18 userNavs []browser.NavigateRequest
19 takenOver bool
20 }
21
22 func (e *previewTestExecutor) Tabs(context.Context) ([]browser.Tab, error) {
23 out := make([]browser.Tab, 0, len(e.tabs))
24 for _, tab := range e.tabs {
25 out = append(out, tab)
26 }
27 return out, nil
28 }
29 func (e *previewTestExecutor) Open(_ context.Context, req browser.OpenRequest) (browser.Tab, error) {
30 e.opens = append(e.opens, req)
31 tab := browser.Tab{ID: "browser-" + req.OperationID, URL: req.URL}
32 e.tabs[tab.ID] = tab
33 return tab, nil
34 }
35 func (e *previewTestExecutor) Navigate(_ context.Context, req browser.NavigateRequest) (browser.Tab, error) {
36 if e.takenOver {
37 return browser.Tab{}, browser.ErrTakenOver
38 }
39 e.navs = append(e.navs, req)
40 tab := e.tabs[req.TabID]
41 tab.URL = req.URL
42 e.tabs[req.TabID] = tab
43 return tab, nil
44 }
45 func (e *previewTestExecutor) navigateFilePreview(_ context.Context, req browser.NavigateRequest) (browser.Tab, error) {
46 e.userNavs = append(e.userNavs, req)
47 tab := e.tabs[req.TabID]
48 tab.URL = req.URL
49 e.tabs[req.TabID] = tab
50 return tab, nil
51 }
52 func (e *previewTestExecutor) Close(_ context.Context, req browser.CloseRequest) error {
53 e.closes = append(e.closes, req)
54 delete(e.tabs, req.TabID)
55 return nil
56 }
57 func (*previewTestExecutor) Snapshot(context.Context, browser.SnapshotRequest) (browser.Snapshot, error) {
58 return browser.Snapshot{}, browser.ErrNoGrant
59 }
60 func (*previewTestExecutor) Screenshot(context.Context, browser.ScreenshotRequest) (browser.Screenshot, error) {
61 return browser.Screenshot{}, browser.ErrNoGrant
62 }
63 func (*previewTestExecutor) Act(context.Context, browser.ActRequest) (browser.ActResult, error) {
64 return browser.ActResult{}, browser.ErrNoGrant
65 }
66 func (*previewTestExecutor) Downloads(context.Context, browser.DownloadsRequest) ([]browser.Download, error) {
67 return nil, browser.ErrNoGrant
68 }
69
70 func TestFileBrowserPreviewReusesTaskTabAndHonoursGeneration(t *testing.T) {
71 root := t.TempDir()
72 if err := os.WriteFile(filepath.Join(root, "index.html"), []byte("<!doctype html><button>first</button>"), 0o644); err != nil {
73 t.Fatal(err)
74 }
75 app := NewApp()
76 app.tabs["task"] = &WorkspaceTab{ID: "task", Scope: "project", WorkspaceRoot: root, SessionGeneration: 7}
77 t.Cleanup(app.stopWorkspacePreviewOrigin)
78 exec := &previewTestExecutor{tabs: map[string]browser.Tab{}}
79
80 first, err := app.openFileBrowserPreview(context.Background(), "task", FileBrowserPreviewRequest{
81 Source: "workspace", Path: "index.html", OperationID: "open-1", ExpectedSessionGeneration: 7,
82 }, exec)
83 if err != nil || first.TabID == "" || len(exec.opens) != 1 || len(exec.navs) != 0 {
84 t.Fatalf("first preview = %+v err=%v opens=%d navs=%d", first, err, len(exec.opens), len(exec.navs))
85 }
86 second, err := app.openFileBrowserPreview(context.Background(), "task", FileBrowserPreviewRequest{
87 Source: "workspace", Path: "index.html", OperationID: "open-2", ExpectedSessionGeneration: 7,
88 }, exec)
89 if err != nil || second.TabID != first.TabID || len(exec.opens) != 1 || len(exec.navs) != 1 {
90 t.Fatalf("second preview = %+v err=%v opens=%d navs=%d", second, err, len(exec.opens), len(exec.navs))
91 }
92
93 exec.takenOver = true
94 if _, err := app.openFileBrowserPreview(context.Background(), "task", FileBrowserPreviewRequest{
95 Source: "workspace", Path: "index.html", OperationID: "open-3", ExpectedSessionGeneration: 7,
96 }, exec); !errors.Is(err, browser.ErrTakenOver) {
97 t.Fatalf("taken-over refresh error = %v, want %v", err, browser.ErrTakenOver)
98 }
99 if len(exec.opens) != 1 {
100 t.Fatalf("taken-over refresh opened a bypass tab: %d opens", len(exec.opens))
101 }
102 userRefresh, err := app.openFileBrowserPreview(context.Background(), "task", FileBrowserPreviewRequest{
103 Source: "workspace", Path: "index.html", OperationID: "user-refresh", ExpectedSessionGeneration: 7, UserInitiated: true,
104 }, exec)
105 if err != nil || userRefresh.TabID != first.TabID || len(exec.userNavs) != 1 || len(exec.opens) != 1 {
106 t.Fatalf("explicit user refresh = %+v err=%v userNavs=%d opens=%d", userRefresh, err, len(exec.userNavs), len(exec.opens))
107 }
108
109 if _, err := app.openFileBrowserPreview(context.Background(), "task", FileBrowserPreviewRequest{
110 Source: "workspace", Path: "index.html", OperationID: "stale", ExpectedSessionGeneration: 6,
111 }, exec); err == nil {
112 t.Fatal("stale session generation opened a preview")
113 }
114 }
115
116 func TestFileBrowserPreviewDoesNotOverwriteAUserNavigation(t *testing.T) {
117 root := t.TempDir()
118 if err := os.WriteFile(filepath.Join(root, "index.html"), []byte("<!doctype html>"), 0o644); err != nil {
119 t.Fatal(err)
120 }
121 app := NewApp()
122 app.tabs["task"] = &WorkspaceTab{ID: "task", Scope: "project", WorkspaceRoot: root, SessionGeneration: 1}
123 t.Cleanup(app.stopWorkspacePreviewOrigin)
124 exec := &previewTestExecutor{tabs: map[string]browser.Tab{}}
125 first, err := app.openFileBrowserPreview(context.Background(), "task", FileBrowserPreviewRequest{Source: "workspace", Path: "index.html", OperationID: "one"}, exec)
126 if err != nil {
127 t.Fatal(err)
128 }
129 tab := exec.tabs[first.TabID]
130 tab.URL = "https://example.test/user-page"
131 exec.tabs[first.TabID] = tab
132 second, err := app.openFileBrowserPreview(context.Background(), "task", FileBrowserPreviewRequest{Source: "workspace", Path: "index.html", OperationID: "two"}, exec)
133 if err != nil {
134 t.Fatal(err)
135 }
136 if second.TabID == first.TabID || len(exec.opens) != 2 || len(exec.navs) != 0 {
137 t.Fatalf("user navigation overwritten: first=%s second=%s opens=%d navs=%d", first.TabID, second.TabID, len(exec.opens), len(exec.navs))
138 }
139 }
140
141 func TestFileBrowserPreviewExplicitRevokeReleasesBinding(t *testing.T) {
142 root := t.TempDir()
143 if err := os.WriteFile(filepath.Join(root, "index.html"), []byte("<!doctype html>"), 0o644); err != nil {
144 t.Fatal(err)
145 }
146 app := NewApp()
147 app.tabs["task"] = &WorkspaceTab{ID: "task", Scope: "project", WorkspaceRoot: root, SessionGeneration: 1}
148 t.Cleanup(app.stopWorkspacePreviewOrigin)
149 exec := &previewTestExecutor{tabs: map[string]browser.Tab{}}
150 opened, err := app.openFileBrowserPreview(context.Background(), "task", FileBrowserPreviewRequest{
151 Source: "workspace", Path: "index.html", OperationID: "open",
152 }, exec)
153 if err != nil {
154 t.Fatal(err)
155 }
156 app.RevokeWorkspaceBrowserPreview(opened.URL)
157 if len(app.filePreviews.bindings) != 0 {
158 t.Fatalf("preview registry retained %d bindings after explicit close revoke", len(app.filePreviews.bindings))
159 }
160 }
161
161 lines GO