reigncode-app 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +30 -0
- package/Dockerfile +21 -0
- package/README.md +51 -0
- package/bunfig.toml +3 -0
- package/create-effect-simplification-spec.md +515 -0
- package/e2e/AGENTS.md +226 -0
- package/e2e/actions.ts +1018 -0
- package/e2e/app/home.spec.ts +24 -0
- package/e2e/app/navigation.spec.ts +10 -0
- package/e2e/app/palette.spec.ts +20 -0
- package/e2e/app/server-default.spec.ts +58 -0
- package/e2e/app/session.spec.ts +16 -0
- package/e2e/app/titlebar-history.spec.ts +120 -0
- package/e2e/commands/input-focus.spec.ts +15 -0
- package/e2e/commands/panels.spec.ts +33 -0
- package/e2e/commands/tab-close.spec.ts +32 -0
- package/e2e/files/file-open.spec.ts +31 -0
- package/e2e/files/file-tree.spec.ts +56 -0
- package/e2e/files/file-viewer.spec.ts +156 -0
- package/e2e/fixtures.ts +154 -0
- package/e2e/models/model-picker.spec.ts +48 -0
- package/e2e/models/models-visibility.spec.ts +61 -0
- package/e2e/projects/project-edit.spec.ts +43 -0
- package/e2e/projects/projects-close.spec.ts +54 -0
- package/e2e/projects/projects-switch.spec.ts +116 -0
- package/e2e/projects/workspace-new-session.spec.ts +94 -0
- package/e2e/projects/workspaces.spec.ts +375 -0
- package/e2e/prompt/context.spec.ts +95 -0
- package/e2e/prompt/prompt-async.spec.ts +76 -0
- package/e2e/prompt/prompt-drop-file-uri.spec.ts +22 -0
- package/e2e/prompt/prompt-drop-file.spec.ts +30 -0
- package/e2e/prompt/prompt-history.spec.ts +184 -0
- package/e2e/prompt/prompt-mention.spec.ts +26 -0
- package/e2e/prompt/prompt-multiline.spec.ts +24 -0
- package/e2e/prompt/prompt-shell.spec.ts +62 -0
- package/e2e/prompt/prompt-slash-open.spec.ts +22 -0
- package/e2e/prompt/prompt-slash-share.spec.ts +64 -0
- package/e2e/prompt/prompt-slash-terminal.spec.ts +18 -0
- package/e2e/prompt/prompt.spec.ts +55 -0
- package/e2e/selectors.ts +75 -0
- package/e2e/session/session-child-navigation.spec.ts +37 -0
- package/e2e/session/session-composer-dock.spec.ts +530 -0
- package/e2e/session/session-model-persistence.spec.ts +359 -0
- package/e2e/session/session-review.spec.ts +426 -0
- package/e2e/session/session-undo-redo.spec.ts +233 -0
- package/e2e/session/session.spec.ts +174 -0
- package/e2e/settings/settings-keybinds.spec.ts +389 -0
- package/e2e/settings/settings-models.spec.ts +122 -0
- package/e2e/settings/settings-providers.spec.ts +136 -0
- package/e2e/settings/settings.spec.ts +519 -0
- package/e2e/sidebar/sidebar-popover-actions.spec.ts +118 -0
- package/e2e/sidebar/sidebar-session-links.spec.ts +30 -0
- package/e2e/sidebar/sidebar.spec.ts +40 -0
- package/e2e/status/status-popover.spec.ts +94 -0
- package/e2e/terminal/terminal-init.spec.ts +28 -0
- package/e2e/terminal/terminal-reconnect.spec.ts +46 -0
- package/e2e/terminal/terminal-tabs.spec.ts +168 -0
- package/e2e/terminal/terminal.spec.ts +18 -0
- package/e2e/thinking-level.spec.ts +25 -0
- package/e2e/tsconfig.json +9 -0
- package/e2e/utils.ts +63 -0
- package/happydom.ts +75 -0
- package/index.html +23 -0
- package/package.json +77 -0
- package/playwright.config.ts +45 -0
- package/public/_headers +17 -0
- package/public/oc-theme-preload.js +35 -0
- package/script/e2e-local.ts +180 -0
- package/src/addons/serialize.test.ts +319 -0
- package/src/addons/serialize.ts +634 -0
- package/src/app.tsx +308 -0
- package/src/components/debug-bar.tsx +443 -0
- package/src/components/dialog-connect-provider.tsx +617 -0
- package/src/components/dialog-custom-provider-form.ts +158 -0
- package/src/components/dialog-custom-provider.test.ts +80 -0
- package/src/components/dialog-custom-provider.tsx +329 -0
- package/src/components/dialog-edit-project.tsx +255 -0
- package/src/components/dialog-fork.tsx +108 -0
- package/src/components/dialog-manage-models.tsx +101 -0
- package/src/components/dialog-release-notes.tsx +144 -0
- package/src/components/dialog-select-directory.tsx +392 -0
- package/src/components/dialog-select-file.tsx +466 -0
- package/src/components/dialog-select-mcp.tsx +107 -0
- package/src/components/dialog-select-model-unpaid.tsx +137 -0
- package/src/components/dialog-select-model.tsx +220 -0
- package/src/components/dialog-select-provider.tsx +86 -0
- package/src/components/dialog-select-server.tsx +649 -0
- package/src/components/dialog-settings.tsx +73 -0
- package/src/components/file-tree.test.ts +78 -0
- package/src/components/file-tree.tsx +507 -0
- package/src/components/link.tsx +26 -0
- package/src/components/model-tooltip.tsx +91 -0
- package/src/components/prompt-input/attachments.test.ts +44 -0
- package/src/components/prompt-input/attachments.ts +201 -0
- package/src/components/prompt-input/build-request-parts.test.ts +312 -0
- package/src/components/prompt-input/build-request-parts.ts +175 -0
- package/src/components/prompt-input/context-items.tsx +88 -0
- package/src/components/prompt-input/drag-overlay.tsx +25 -0
- package/src/components/prompt-input/editor-dom.test.ts +99 -0
- package/src/components/prompt-input/editor-dom.ts +148 -0
- package/src/components/prompt-input/files.ts +66 -0
- package/src/components/prompt-input/history.test.ts +153 -0
- package/src/components/prompt-input/history.ts +256 -0
- package/src/components/prompt-input/image-attachments.tsx +58 -0
- package/src/components/prompt-input/paste.ts +24 -0
- package/src/components/prompt-input/placeholder.test.ts +48 -0
- package/src/components/prompt-input/placeholder.ts +15 -0
- package/src/components/prompt-input/slash-popover.tsx +141 -0
- package/src/components/prompt-input/submit.test.ts +346 -0
- package/src/components/prompt-input/submit.ts +579 -0
- package/src/components/prompt-input.tsx +1595 -0
- package/src/components/server/server-row.tsx +130 -0
- package/src/components/session/index.ts +5 -0
- package/src/components/session/session-context-breakdown.test.ts +61 -0
- package/src/components/session/session-context-breakdown.ts +132 -0
- package/src/components/session/session-context-format.ts +20 -0
- package/src/components/session/session-context-metrics.test.ts +101 -0
- package/src/components/session/session-context-metrics.ts +82 -0
- package/src/components/session/session-context-tab.tsx +339 -0
- package/src/components/session/session-header.tsx +486 -0
- package/src/components/session/session-new-view.tsx +91 -0
- package/src/components/session/session-sortable-tab.tsx +70 -0
- package/src/components/session/session-sortable-terminal-tab.tsx +193 -0
- package/src/components/session-context-usage.tsx +122 -0
- package/src/components/settings-general.tsx +585 -0
- package/src/components/settings-keybinds.tsx +453 -0
- package/src/components/settings-list.tsx +5 -0
- package/src/components/settings-models.tsx +137 -0
- package/src/components/settings-providers.tsx +251 -0
- package/src/components/status-popover.tsx +419 -0
- package/src/components/terminal.tsx +653 -0
- package/src/components/titlebar-history.test.ts +63 -0
- package/src/components/titlebar-history.ts +57 -0
- package/src/components/titlebar.tsx +312 -0
- package/src/constants/file-picker.ts +89 -0
- package/src/context/command-keybind.test.ts +69 -0
- package/src/context/command.test.ts +25 -0
- package/src/context/command.tsx +437 -0
- package/src/context/comments.test.ts +186 -0
- package/src/context/comments.tsx +243 -0
- package/src/context/file/content-cache.ts +88 -0
- package/src/context/file/path.test.ts +360 -0
- package/src/context/file/path.ts +151 -0
- package/src/context/file/tree-store.ts +170 -0
- package/src/context/file/types.ts +41 -0
- package/src/context/file/view-cache.ts +146 -0
- package/src/context/file/watcher.test.ts +149 -0
- package/src/context/file/watcher.ts +53 -0
- package/src/context/file-content-eviction-accounting.test.ts +65 -0
- package/src/context/file.tsx +280 -0
- package/src/context/global-sdk.tsx +232 -0
- package/src/context/global-sync/bootstrap.ts +206 -0
- package/src/context/global-sync/child-store.test.ts +38 -0
- package/src/context/global-sync/child-store.ts +281 -0
- package/src/context/global-sync/event-reducer.test.ts +552 -0
- package/src/context/global-sync/event-reducer.ts +359 -0
- package/src/context/global-sync/eviction.ts +28 -0
- package/src/context/global-sync/queue.ts +83 -0
- package/src/context/global-sync/session-cache.test.ts +102 -0
- package/src/context/global-sync/session-cache.ts +62 -0
- package/src/context/global-sync/session-load.ts +25 -0
- package/src/context/global-sync/session-prefetch.test.ts +96 -0
- package/src/context/global-sync/session-prefetch.ts +100 -0
- package/src/context/global-sync/session-trim.test.ts +59 -0
- package/src/context/global-sync/session-trim.ts +56 -0
- package/src/context/global-sync/types.ts +133 -0
- package/src/context/global-sync/utils.ts +25 -0
- package/src/context/global-sync.test.ts +122 -0
- package/src/context/global-sync.tsx +408 -0
- package/src/context/highlights.tsx +233 -0
- package/src/context/language.tsx +248 -0
- package/src/context/layout-scroll.test.ts +64 -0
- package/src/context/layout-scroll.ts +126 -0
- package/src/context/layout.test.ts +69 -0
- package/src/context/layout.tsx +937 -0
- package/src/context/local.tsx +422 -0
- package/src/context/model-variant.test.ts +86 -0
- package/src/context/model-variant.ts +52 -0
- package/src/context/models.tsx +163 -0
- package/src/context/notification.tsx +373 -0
- package/src/context/permission-auto-respond.test.ts +102 -0
- package/src/context/permission-auto-respond.ts +51 -0
- package/src/context/permission.tsx +277 -0
- package/src/context/platform.tsx +99 -0
- package/src/context/prompt.tsx +297 -0
- package/src/context/sdk.tsx +49 -0
- package/src/context/server.tsx +295 -0
- package/src/context/settings.tsx +241 -0
- package/src/context/sync-optimistic.test.ts +123 -0
- package/src/context/sync.tsx +618 -0
- package/src/context/terminal-title.ts +51 -0
- package/src/context/terminal.test.ts +82 -0
- package/src/context/terminal.tsx +437 -0
- package/src/entry.tsx +144 -0
- package/src/env.d.ts +18 -0
- package/src/hooks/use-providers.ts +44 -0
- package/src/i18n/ar.ts +855 -0
- package/src/i18n/br.ts +867 -0
- package/src/i18n/bs.ts +943 -0
- package/src/i18n/da.ts +937 -0
- package/src/i18n/de.ts +879 -0
- package/src/i18n/en.ts +948 -0
- package/src/i18n/es.ts +950 -0
- package/src/i18n/fr.ts +878 -0
- package/src/i18n/ja.ts +861 -0
- package/src/i18n/ko.ts +860 -0
- package/src/i18n/no.ts +944 -0
- package/src/i18n/parity.test.ts +32 -0
- package/src/i18n/pl.ts +865 -0
- package/src/i18n/ru.ts +946 -0
- package/src/i18n/th.ts +933 -0
- package/src/i18n/tr.ts +952 -0
- package/src/i18n/zh.ts +930 -0
- package/src/i18n/zht.ts +925 -0
- package/src/index.css +29 -0
- package/src/index.ts +6 -0
- package/src/pages/directory-layout.tsx +88 -0
- package/src/pages/error.tsx +327 -0
- package/src/pages/home.tsx +131 -0
- package/src/pages/layout/deep-links.ts +50 -0
- package/src/pages/layout/helpers.test.ts +211 -0
- package/src/pages/layout/helpers.ts +98 -0
- package/src/pages/layout/inline-editor.tsx +126 -0
- package/src/pages/layout/sidebar-items.tsx +437 -0
- package/src/pages/layout/sidebar-project.tsx +384 -0
- package/src/pages/layout/sidebar-shell.tsx +125 -0
- package/src/pages/layout/sidebar-workspace.tsx +504 -0
- package/src/pages/layout.tsx +2509 -0
- package/src/pages/session/composer/index.ts +2 -0
- package/src/pages/session/composer/session-composer-region.tsx +255 -0
- package/src/pages/session/composer/session-composer-state.test.ts +128 -0
- package/src/pages/session/composer/session-composer-state.ts +249 -0
- package/src/pages/session/composer/session-followup-dock.tsx +109 -0
- package/src/pages/session/composer/session-permission-dock.tsx +74 -0
- package/src/pages/session/composer/session-question-dock.tsx +449 -0
- package/src/pages/session/composer/session-request-tree.ts +52 -0
- package/src/pages/session/composer/session-revert-dock.tsx +99 -0
- package/src/pages/session/composer/session-todo-dock.tsx +330 -0
- package/src/pages/session/file-tab-scroll.test.ts +40 -0
- package/src/pages/session/file-tab-scroll.ts +67 -0
- package/src/pages/session/file-tabs.tsx +456 -0
- package/src/pages/session/handoff.ts +36 -0
- package/src/pages/session/helpers.test.ts +181 -0
- package/src/pages/session/helpers.ts +198 -0
- package/src/pages/session/message-gesture.test.ts +62 -0
- package/src/pages/session/message-gesture.ts +21 -0
- package/src/pages/session/message-id-from-hash.ts +6 -0
- package/src/pages/session/message-timeline.tsx +1013 -0
- package/src/pages/session/review-tab.tsx +170 -0
- package/src/pages/session/session-layout.ts +20 -0
- package/src/pages/session/session-model-helpers.test.ts +51 -0
- package/src/pages/session/session-model-helpers.ts +16 -0
- package/src/pages/session/session-side-panel.tsx +453 -0
- package/src/pages/session/terminal-label.ts +16 -0
- package/src/pages/session/terminal-panel.test.ts +25 -0
- package/src/pages/session/terminal-panel.tsx +326 -0
- package/src/pages/session/use-session-commands.tsx +495 -0
- package/src/pages/session/use-session-hash-scroll.test.ts +16 -0
- package/src/pages/session/use-session-hash-scroll.ts +197 -0
- package/src/pages/session.tsx +1841 -0
- package/src/sst-env.d.ts +12 -0
- package/src/testing/model-selection.ts +80 -0
- package/src/testing/prompt.ts +56 -0
- package/src/testing/session-composer.ts +84 -0
- package/src/testing/terminal.ts +118 -0
- package/src/theme-preload.test.ts +46 -0
- package/src/utils/agent.ts +23 -0
- package/src/utils/aim.ts +138 -0
- package/src/utils/base64.ts +10 -0
- package/src/utils/comment-note.ts +88 -0
- package/src/utils/id.ts +99 -0
- package/src/utils/notification-click.test.ts +27 -0
- package/src/utils/notification-click.ts +13 -0
- package/src/utils/persist.test.ts +115 -0
- package/src/utils/persist.ts +476 -0
- package/src/utils/prompt.test.ts +44 -0
- package/src/utils/prompt.ts +203 -0
- package/src/utils/runtime-adapters.test.ts +62 -0
- package/src/utils/runtime-adapters.ts +39 -0
- package/src/utils/same.ts +6 -0
- package/src/utils/scoped-cache.test.ts +69 -0
- package/src/utils/scoped-cache.ts +104 -0
- package/src/utils/server-errors.test.ts +131 -0
- package/src/utils/server-errors.ts +80 -0
- package/src/utils/server-health.test.ts +123 -0
- package/src/utils/server-health.ts +91 -0
- package/src/utils/server.ts +22 -0
- package/src/utils/solid-dnd.tsx +49 -0
- package/src/utils/sound.ts +117 -0
- package/src/utils/terminal-writer.test.ts +64 -0
- package/src/utils/terminal-writer.ts +65 -0
- package/src/utils/time.ts +22 -0
- package/src/utils/uuid.test.ts +78 -0
- package/src/utils/uuid.ts +12 -0
- package/src/utils/worktree.test.ts +46 -0
- package/src/utils/worktree.ts +73 -0
- package/sst-env.d.ts +10 -0
- package/tsconfig.json +26 -0
- package/vite.config.ts +15 -0
- package/vite.js +26 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { test, expect } from "../fixtures"
|
|
2
|
+
import {
|
|
3
|
+
openSidebar,
|
|
4
|
+
openSessionMoreMenu,
|
|
5
|
+
clickMenuItem,
|
|
6
|
+
confirmDialog,
|
|
7
|
+
openSharePopover,
|
|
8
|
+
withSession,
|
|
9
|
+
} from "../actions"
|
|
10
|
+
import { sessionItemSelector, inlineInputSelector } from "../selectors"
|
|
11
|
+
|
|
12
|
+
const shareDisabled = process.env.REIGNCODE_DISABLE_SHARE === "true" || process.env.REIGNCODE_DISABLE_SHARE === "1"
|
|
13
|
+
|
|
14
|
+
type Sdk = Parameters<typeof withSession>[0]
|
|
15
|
+
|
|
16
|
+
async function seedMessage(sdk: Sdk, sessionID: string) {
|
|
17
|
+
await sdk.session.promptAsync({
|
|
18
|
+
sessionID,
|
|
19
|
+
noReply: true,
|
|
20
|
+
parts: [{ type: "text", text: "e2e seed" }],
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
await expect
|
|
24
|
+
.poll(
|
|
25
|
+
async () => {
|
|
26
|
+
const messages = await sdk.session.messages({ sessionID, limit: 1 }).then((r) => r.data ?? [])
|
|
27
|
+
return messages.length
|
|
28
|
+
},
|
|
29
|
+
{ timeout: 30_000 },
|
|
30
|
+
)
|
|
31
|
+
.toBeGreaterThan(0)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test("session can be renamed via header menu", async ({ page, sdk, gotoSession }) => {
|
|
35
|
+
const stamp = Date.now()
|
|
36
|
+
const originalTitle = `e2e rename test ${stamp}`
|
|
37
|
+
const renamedTitle = `e2e renamed ${stamp}`
|
|
38
|
+
|
|
39
|
+
await withSession(sdk, originalTitle, async (session) => {
|
|
40
|
+
await seedMessage(sdk, session.id)
|
|
41
|
+
await gotoSession(session.id)
|
|
42
|
+
await expect(page.getByRole("heading", { level: 1 }).first()).toHaveText(originalTitle)
|
|
43
|
+
|
|
44
|
+
const menu = await openSessionMoreMenu(page, session.id)
|
|
45
|
+
await clickMenuItem(menu, /rename/i)
|
|
46
|
+
|
|
47
|
+
const input = page.locator(".scroll-view__viewport").locator(inlineInputSelector).first()
|
|
48
|
+
await expect(input).toBeVisible()
|
|
49
|
+
await expect(input).toBeFocused()
|
|
50
|
+
await input.fill(renamedTitle)
|
|
51
|
+
await expect(input).toHaveValue(renamedTitle)
|
|
52
|
+
await input.press("Enter")
|
|
53
|
+
|
|
54
|
+
await expect
|
|
55
|
+
.poll(
|
|
56
|
+
async () => {
|
|
57
|
+
const data = await sdk.session.get({ sessionID: session.id }).then((r) => r.data)
|
|
58
|
+
return data?.title
|
|
59
|
+
},
|
|
60
|
+
{ timeout: 30_000 },
|
|
61
|
+
)
|
|
62
|
+
.toBe(renamedTitle)
|
|
63
|
+
|
|
64
|
+
await expect(page.getByRole("heading", { level: 1 }).first()).toHaveText(renamedTitle)
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test("session can be archived via header menu", async ({ page, sdk, gotoSession }) => {
|
|
69
|
+
const stamp = Date.now()
|
|
70
|
+
const title = `e2e archive test ${stamp}`
|
|
71
|
+
|
|
72
|
+
await withSession(sdk, title, async (session) => {
|
|
73
|
+
await seedMessage(sdk, session.id)
|
|
74
|
+
await gotoSession(session.id)
|
|
75
|
+
const menu = await openSessionMoreMenu(page, session.id)
|
|
76
|
+
await clickMenuItem(menu, /archive/i)
|
|
77
|
+
|
|
78
|
+
await expect
|
|
79
|
+
.poll(
|
|
80
|
+
async () => {
|
|
81
|
+
const data = await sdk.session.get({ sessionID: session.id }).then((r) => r.data)
|
|
82
|
+
return data?.time?.archived
|
|
83
|
+
},
|
|
84
|
+
{ timeout: 30_000 },
|
|
85
|
+
)
|
|
86
|
+
.not.toBeUndefined()
|
|
87
|
+
|
|
88
|
+
await openSidebar(page)
|
|
89
|
+
await expect(page.locator(sessionItemSelector(session.id))).toHaveCount(0)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test("session can be deleted via header menu", async ({ page, sdk, gotoSession }) => {
|
|
94
|
+
const stamp = Date.now()
|
|
95
|
+
const title = `e2e delete test ${stamp}`
|
|
96
|
+
|
|
97
|
+
await withSession(sdk, title, async (session) => {
|
|
98
|
+
await seedMessage(sdk, session.id)
|
|
99
|
+
await gotoSession(session.id)
|
|
100
|
+
const menu = await openSessionMoreMenu(page, session.id)
|
|
101
|
+
await clickMenuItem(menu, /delete/i)
|
|
102
|
+
await confirmDialog(page, /delete/i)
|
|
103
|
+
|
|
104
|
+
await expect
|
|
105
|
+
.poll(
|
|
106
|
+
async () => {
|
|
107
|
+
const data = await sdk.session
|
|
108
|
+
.get({ sessionID: session.id })
|
|
109
|
+
.then((r) => r.data)
|
|
110
|
+
.catch(() => undefined)
|
|
111
|
+
return data?.id
|
|
112
|
+
},
|
|
113
|
+
{ timeout: 30_000 },
|
|
114
|
+
)
|
|
115
|
+
.toBeUndefined()
|
|
116
|
+
|
|
117
|
+
await openSidebar(page)
|
|
118
|
+
await expect(page.locator(sessionItemSelector(session.id))).toHaveCount(0)
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test("session can be shared and unshared via header button", async ({ page, sdk, gotoSession }) => {
|
|
123
|
+
test.skip(shareDisabled, "Share is disabled in this environment (REIGNCODE_DISABLE_SHARE).")
|
|
124
|
+
|
|
125
|
+
const stamp = Date.now()
|
|
126
|
+
const title = `e2e share test ${stamp}`
|
|
127
|
+
|
|
128
|
+
await withSession(sdk, title, async (session) => {
|
|
129
|
+
await seedMessage(sdk, session.id)
|
|
130
|
+
await gotoSession(session.id)
|
|
131
|
+
|
|
132
|
+
const shared = await openSharePopover(page)
|
|
133
|
+
const publish = shared.popoverBody.getByRole("button", { name: "Publish" }).first()
|
|
134
|
+
await expect(publish).toBeVisible({ timeout: 30_000 })
|
|
135
|
+
await publish.click()
|
|
136
|
+
|
|
137
|
+
await expect(shared.popoverBody.getByRole("button", { name: "Unpublish" }).first()).toBeVisible({
|
|
138
|
+
timeout: 30_000,
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
await expect
|
|
142
|
+
.poll(
|
|
143
|
+
async () => {
|
|
144
|
+
const data = await sdk.session.get({ sessionID: session.id }).then((r) => r.data)
|
|
145
|
+
return data?.share?.url || undefined
|
|
146
|
+
},
|
|
147
|
+
{ timeout: 30_000 },
|
|
148
|
+
)
|
|
149
|
+
.not.toBeUndefined()
|
|
150
|
+
|
|
151
|
+
const unpublish = shared.popoverBody.getByRole("button", { name: "Unpublish" }).first()
|
|
152
|
+
await expect(unpublish).toBeVisible({ timeout: 30_000 })
|
|
153
|
+
await unpublish.click()
|
|
154
|
+
|
|
155
|
+
await expect(shared.popoverBody.getByRole("button", { name: "Publish" }).first()).toBeVisible({
|
|
156
|
+
timeout: 30_000,
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
await expect
|
|
160
|
+
.poll(
|
|
161
|
+
async () => {
|
|
162
|
+
const data = await sdk.session.get({ sessionID: session.id }).then((r) => r.data)
|
|
163
|
+
return data?.share?.url || undefined
|
|
164
|
+
},
|
|
165
|
+
{ timeout: 30_000 },
|
|
166
|
+
)
|
|
167
|
+
.toBeUndefined()
|
|
168
|
+
|
|
169
|
+
const unshared = await openSharePopover(page)
|
|
170
|
+
await expect(unshared.popoverBody.getByRole("button", { name: "Publish" }).first()).toBeVisible({
|
|
171
|
+
timeout: 30_000,
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
})
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { test, expect } from "../fixtures"
|
|
2
|
+
import { openSettings, closeDialog, waitTerminalFocusIdle, withSession } from "../actions"
|
|
3
|
+
import { keybindButtonSelector, terminalSelector } from "../selectors"
|
|
4
|
+
import { modKey } from "../utils"
|
|
5
|
+
|
|
6
|
+
test("changing sidebar toggle keybind works", async ({ page, gotoSession }) => {
|
|
7
|
+
await gotoSession()
|
|
8
|
+
|
|
9
|
+
const dialog = await openSettings(page)
|
|
10
|
+
await dialog.getByRole("tab", { name: "Shortcuts" }).click()
|
|
11
|
+
|
|
12
|
+
const keybindButton = dialog.locator(keybindButtonSelector("sidebar.toggle")).first()
|
|
13
|
+
await expect(keybindButton).toBeVisible()
|
|
14
|
+
|
|
15
|
+
const initialKeybind = await keybindButton.textContent()
|
|
16
|
+
expect(initialKeybind).toContain("B")
|
|
17
|
+
|
|
18
|
+
await keybindButton.click()
|
|
19
|
+
await expect(keybindButton).toHaveText(/press/i)
|
|
20
|
+
|
|
21
|
+
await page.keyboard.press(`${modKey}+Shift+KeyH`)
|
|
22
|
+
await page.waitForTimeout(100)
|
|
23
|
+
|
|
24
|
+
const newKeybind = await keybindButton.textContent()
|
|
25
|
+
expect(newKeybind).toContain("H")
|
|
26
|
+
|
|
27
|
+
const stored = await page.evaluate(() => {
|
|
28
|
+
const raw = localStorage.getItem("settings.v3")
|
|
29
|
+
return raw ? JSON.parse(raw) : null
|
|
30
|
+
})
|
|
31
|
+
expect(stored?.keybinds?.["sidebar.toggle"]).toBe("mod+shift+h")
|
|
32
|
+
|
|
33
|
+
await closeDialog(page, dialog)
|
|
34
|
+
|
|
35
|
+
const button = page.getByRole("button", { name: /toggle sidebar/i }).first()
|
|
36
|
+
const initiallyClosed = (await button.getAttribute("aria-expanded")) !== "true"
|
|
37
|
+
|
|
38
|
+
await page.keyboard.press(`${modKey}+Shift+H`)
|
|
39
|
+
await expect(button).toHaveAttribute("aria-expanded", initiallyClosed ? "true" : "false")
|
|
40
|
+
|
|
41
|
+
const afterToggleClosed = (await button.getAttribute("aria-expanded")) !== "true"
|
|
42
|
+
expect(afterToggleClosed).toBe(!initiallyClosed)
|
|
43
|
+
|
|
44
|
+
await page.keyboard.press(`${modKey}+Shift+H`)
|
|
45
|
+
await expect(button).toHaveAttribute("aria-expanded", initiallyClosed ? "false" : "true")
|
|
46
|
+
|
|
47
|
+
const finalClosed = (await button.getAttribute("aria-expanded")) !== "true"
|
|
48
|
+
expect(finalClosed).toBe(initiallyClosed)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test("sidebar toggle keybind guards against shortcut conflicts", async ({ page, gotoSession }) => {
|
|
52
|
+
await gotoSession()
|
|
53
|
+
|
|
54
|
+
const dialog = await openSettings(page)
|
|
55
|
+
await dialog.getByRole("tab", { name: "Shortcuts" }).click()
|
|
56
|
+
|
|
57
|
+
const keybindButton = dialog.locator(keybindButtonSelector("sidebar.toggle"))
|
|
58
|
+
await expect(keybindButton).toBeVisible()
|
|
59
|
+
|
|
60
|
+
const initialKeybind = await keybindButton.textContent()
|
|
61
|
+
expect(initialKeybind).toContain("B")
|
|
62
|
+
|
|
63
|
+
await keybindButton.click()
|
|
64
|
+
await expect(keybindButton).toHaveText(/press/i)
|
|
65
|
+
|
|
66
|
+
await page.keyboard.press(`${modKey}+Shift+KeyP`)
|
|
67
|
+
await page.waitForTimeout(100)
|
|
68
|
+
|
|
69
|
+
const toast = page.locator('[data-component="toast"]').last()
|
|
70
|
+
await expect(toast).toBeVisible()
|
|
71
|
+
await expect(toast).toContainText(/already/i)
|
|
72
|
+
|
|
73
|
+
await keybindButton.click()
|
|
74
|
+
await expect(keybindButton).toContainText("B")
|
|
75
|
+
|
|
76
|
+
const stored = await page.evaluate(() => {
|
|
77
|
+
const raw = localStorage.getItem("settings.v3")
|
|
78
|
+
return raw ? JSON.parse(raw) : null
|
|
79
|
+
})
|
|
80
|
+
expect(stored?.keybinds?.["sidebar.toggle"]).toBeUndefined()
|
|
81
|
+
|
|
82
|
+
await closeDialog(page, dialog)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test("resetting all keybinds to defaults works", async ({ page, gotoSession }) => {
|
|
86
|
+
await page.addInitScript(() => {
|
|
87
|
+
localStorage.setItem("settings.v3", JSON.stringify({ keybinds: { "sidebar.toggle": "mod+shift+x" } }))
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
await gotoSession()
|
|
91
|
+
|
|
92
|
+
const dialog = await openSettings(page)
|
|
93
|
+
await dialog.getByRole("tab", { name: "Shortcuts" }).click()
|
|
94
|
+
|
|
95
|
+
const keybindButton = dialog.locator(keybindButtonSelector("sidebar.toggle"))
|
|
96
|
+
await expect(keybindButton).toBeVisible()
|
|
97
|
+
|
|
98
|
+
const customKeybind = await keybindButton.textContent()
|
|
99
|
+
expect(customKeybind).toContain("X")
|
|
100
|
+
|
|
101
|
+
const resetButton = dialog.getByRole("button", { name: "Reset to defaults" })
|
|
102
|
+
await expect(resetButton).toBeVisible()
|
|
103
|
+
await expect(resetButton).toBeEnabled()
|
|
104
|
+
await resetButton.click()
|
|
105
|
+
await page.waitForTimeout(100)
|
|
106
|
+
|
|
107
|
+
const restoredKeybind = await keybindButton.textContent()
|
|
108
|
+
expect(restoredKeybind).toContain("B")
|
|
109
|
+
|
|
110
|
+
const stored = await page.evaluate(() => {
|
|
111
|
+
const raw = localStorage.getItem("settings.v3")
|
|
112
|
+
return raw ? JSON.parse(raw) : null
|
|
113
|
+
})
|
|
114
|
+
expect(stored?.keybinds?.["sidebar.toggle"]).toBeUndefined()
|
|
115
|
+
|
|
116
|
+
await closeDialog(page, dialog)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test("clearing a keybind works", async ({ page, gotoSession }) => {
|
|
120
|
+
await gotoSession()
|
|
121
|
+
|
|
122
|
+
const dialog = await openSettings(page)
|
|
123
|
+
await dialog.getByRole("tab", { name: "Shortcuts" }).click()
|
|
124
|
+
|
|
125
|
+
const keybindButton = dialog.locator(keybindButtonSelector("sidebar.toggle"))
|
|
126
|
+
await expect(keybindButton).toBeVisible()
|
|
127
|
+
|
|
128
|
+
const initialKeybind = await keybindButton.textContent()
|
|
129
|
+
expect(initialKeybind).toContain("B")
|
|
130
|
+
|
|
131
|
+
await keybindButton.click()
|
|
132
|
+
await expect(keybindButton).toHaveText(/press/i)
|
|
133
|
+
|
|
134
|
+
await page.keyboard.press("Delete")
|
|
135
|
+
await page.waitForTimeout(100)
|
|
136
|
+
|
|
137
|
+
const clearedKeybind = await keybindButton.textContent()
|
|
138
|
+
expect(clearedKeybind).toMatch(/unassigned|press/i)
|
|
139
|
+
|
|
140
|
+
const stored = await page.evaluate(() => {
|
|
141
|
+
const raw = localStorage.getItem("settings.v3")
|
|
142
|
+
return raw ? JSON.parse(raw) : null
|
|
143
|
+
})
|
|
144
|
+
expect(stored?.keybinds?.["sidebar.toggle"]).toBe("none")
|
|
145
|
+
|
|
146
|
+
await closeDialog(page, dialog)
|
|
147
|
+
|
|
148
|
+
await page.keyboard.press(`${modKey}+B`)
|
|
149
|
+
await page.waitForTimeout(100)
|
|
150
|
+
|
|
151
|
+
const stillOnSession = page.url().includes("/session")
|
|
152
|
+
expect(stillOnSession).toBe(true)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test("changing settings open keybind works", async ({ page, gotoSession }) => {
|
|
156
|
+
await gotoSession()
|
|
157
|
+
|
|
158
|
+
const dialog = await openSettings(page)
|
|
159
|
+
await dialog.getByRole("tab", { name: "Shortcuts" }).click()
|
|
160
|
+
|
|
161
|
+
const keybindButton = dialog.locator(keybindButtonSelector("settings.open"))
|
|
162
|
+
await expect(keybindButton).toBeVisible()
|
|
163
|
+
|
|
164
|
+
const initialKeybind = await keybindButton.textContent()
|
|
165
|
+
expect(initialKeybind).toContain(",")
|
|
166
|
+
|
|
167
|
+
await keybindButton.click()
|
|
168
|
+
await expect(keybindButton).toHaveText(/press/i)
|
|
169
|
+
|
|
170
|
+
await page.keyboard.press(`${modKey}+Slash`)
|
|
171
|
+
await page.waitForTimeout(100)
|
|
172
|
+
|
|
173
|
+
const newKeybind = await keybindButton.textContent()
|
|
174
|
+
expect(newKeybind).toContain("/")
|
|
175
|
+
|
|
176
|
+
const stored = await page.evaluate(() => {
|
|
177
|
+
const raw = localStorage.getItem("settings.v3")
|
|
178
|
+
return raw ? JSON.parse(raw) : null
|
|
179
|
+
})
|
|
180
|
+
expect(stored?.keybinds?.["settings.open"]).toBe("mod+/")
|
|
181
|
+
|
|
182
|
+
await closeDialog(page, dialog)
|
|
183
|
+
|
|
184
|
+
const settingsDialog = page.getByRole("dialog")
|
|
185
|
+
await expect(settingsDialog).toHaveCount(0)
|
|
186
|
+
|
|
187
|
+
await page.keyboard.press(`${modKey}+Slash`)
|
|
188
|
+
await page.waitForTimeout(100)
|
|
189
|
+
|
|
190
|
+
await expect(settingsDialog).toBeVisible()
|
|
191
|
+
|
|
192
|
+
await closeDialog(page, settingsDialog)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test("changing new session keybind works", async ({ page, sdk, gotoSession }) => {
|
|
196
|
+
await withSession(sdk, "test session for keybind", async (session) => {
|
|
197
|
+
await gotoSession(session.id)
|
|
198
|
+
|
|
199
|
+
const initialUrl = page.url()
|
|
200
|
+
expect(initialUrl).toContain(`/session/${session.id}`)
|
|
201
|
+
|
|
202
|
+
const dialog = await openSettings(page)
|
|
203
|
+
await dialog.getByRole("tab", { name: "Shortcuts" }).click()
|
|
204
|
+
|
|
205
|
+
const keybindButton = dialog.locator(keybindButtonSelector("session.new"))
|
|
206
|
+
await expect(keybindButton).toBeVisible()
|
|
207
|
+
|
|
208
|
+
await keybindButton.click()
|
|
209
|
+
await expect(keybindButton).toHaveText(/press/i)
|
|
210
|
+
|
|
211
|
+
await page.keyboard.press(`${modKey}+Shift+KeyN`)
|
|
212
|
+
await page.waitForTimeout(100)
|
|
213
|
+
|
|
214
|
+
const newKeybind = await keybindButton.textContent()
|
|
215
|
+
expect(newKeybind).toContain("N")
|
|
216
|
+
|
|
217
|
+
const stored = await page.evaluate(() => {
|
|
218
|
+
const raw = localStorage.getItem("settings.v3")
|
|
219
|
+
return raw ? JSON.parse(raw) : null
|
|
220
|
+
})
|
|
221
|
+
expect(stored?.keybinds?.["session.new"]).toBe("mod+shift+n")
|
|
222
|
+
|
|
223
|
+
await closeDialog(page, dialog)
|
|
224
|
+
|
|
225
|
+
await page.keyboard.press(`${modKey}+Shift+N`)
|
|
226
|
+
await page.waitForTimeout(200)
|
|
227
|
+
|
|
228
|
+
const newUrl = page.url()
|
|
229
|
+
expect(newUrl).toMatch(/\/session\/?$/)
|
|
230
|
+
expect(newUrl).not.toContain(session.id)
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
test("changing file open keybind works", async ({ page, gotoSession }) => {
|
|
235
|
+
await gotoSession()
|
|
236
|
+
|
|
237
|
+
const dialog = await openSettings(page)
|
|
238
|
+
await dialog.getByRole("tab", { name: "Shortcuts" }).click()
|
|
239
|
+
|
|
240
|
+
const keybindButton = dialog.locator(keybindButtonSelector("file.open"))
|
|
241
|
+
await expect(keybindButton).toBeVisible()
|
|
242
|
+
|
|
243
|
+
const initialKeybind = await keybindButton.textContent()
|
|
244
|
+
expect(initialKeybind).toContain("K")
|
|
245
|
+
|
|
246
|
+
await keybindButton.click()
|
|
247
|
+
await expect(keybindButton).toHaveText(/press/i)
|
|
248
|
+
|
|
249
|
+
await page.keyboard.press(`${modKey}+Shift+KeyF`)
|
|
250
|
+
await page.waitForTimeout(100)
|
|
251
|
+
|
|
252
|
+
const newKeybind = await keybindButton.textContent()
|
|
253
|
+
expect(newKeybind).toContain("F")
|
|
254
|
+
|
|
255
|
+
const stored = await page.evaluate(() => {
|
|
256
|
+
const raw = localStorage.getItem("settings.v3")
|
|
257
|
+
return raw ? JSON.parse(raw) : null
|
|
258
|
+
})
|
|
259
|
+
expect(stored?.keybinds?.["file.open"]).toBe("mod+shift+f")
|
|
260
|
+
|
|
261
|
+
await closeDialog(page, dialog)
|
|
262
|
+
|
|
263
|
+
const filePickerDialog = page.getByRole("dialog").filter({ has: page.getByPlaceholder(/search files/i) })
|
|
264
|
+
await expect(filePickerDialog).toHaveCount(0)
|
|
265
|
+
|
|
266
|
+
await page.keyboard.press(`${modKey}+Shift+F`)
|
|
267
|
+
await page.waitForTimeout(100)
|
|
268
|
+
|
|
269
|
+
await expect(filePickerDialog).toBeVisible()
|
|
270
|
+
|
|
271
|
+
await page.keyboard.press("Escape")
|
|
272
|
+
await expect(filePickerDialog).toHaveCount(0)
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
test("changing terminal toggle keybind works", async ({ page, gotoSession }) => {
|
|
276
|
+
await gotoSession()
|
|
277
|
+
|
|
278
|
+
const dialog = await openSettings(page)
|
|
279
|
+
await dialog.getByRole("tab", { name: "Shortcuts" }).click()
|
|
280
|
+
|
|
281
|
+
const keybindButton = dialog.locator(keybindButtonSelector("terminal.toggle"))
|
|
282
|
+
await expect(keybindButton).toBeVisible()
|
|
283
|
+
|
|
284
|
+
await keybindButton.click()
|
|
285
|
+
await expect(keybindButton).toHaveText(/press/i)
|
|
286
|
+
|
|
287
|
+
await page.keyboard.press(`${modKey}+KeyY`)
|
|
288
|
+
await page.waitForTimeout(100)
|
|
289
|
+
|
|
290
|
+
const newKeybind = await keybindButton.textContent()
|
|
291
|
+
expect(newKeybind).toContain("Y")
|
|
292
|
+
|
|
293
|
+
const stored = await page.evaluate(() => {
|
|
294
|
+
const raw = localStorage.getItem("settings.v3")
|
|
295
|
+
return raw ? JSON.parse(raw) : null
|
|
296
|
+
})
|
|
297
|
+
expect(stored?.keybinds?.["terminal.toggle"]).toBe("mod+y")
|
|
298
|
+
|
|
299
|
+
await closeDialog(page, dialog)
|
|
300
|
+
|
|
301
|
+
const terminal = page.locator(terminalSelector)
|
|
302
|
+
await expect(terminal).not.toBeVisible()
|
|
303
|
+
|
|
304
|
+
await page.keyboard.press(`${modKey}+Y`)
|
|
305
|
+
await waitTerminalFocusIdle(page, { term: terminal })
|
|
306
|
+
|
|
307
|
+
await page.keyboard.press(`${modKey}+Y`)
|
|
308
|
+
await expect(terminal).not.toBeVisible()
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
test("terminal toggle keybind persists after reload", async ({ page, gotoSession }) => {
|
|
312
|
+
await gotoSession()
|
|
313
|
+
|
|
314
|
+
const dialog = await openSettings(page)
|
|
315
|
+
await dialog.getByRole("tab", { name: "Shortcuts" }).click()
|
|
316
|
+
|
|
317
|
+
const keybindButton = dialog.locator(keybindButtonSelector("terminal.toggle"))
|
|
318
|
+
await expect(keybindButton).toBeVisible()
|
|
319
|
+
|
|
320
|
+
await keybindButton.click()
|
|
321
|
+
await expect(keybindButton).toHaveText(/press/i)
|
|
322
|
+
|
|
323
|
+
await page.keyboard.press(`${modKey}+Shift+KeyY`)
|
|
324
|
+
await page.waitForTimeout(100)
|
|
325
|
+
|
|
326
|
+
await expect(keybindButton).toContainText("Y")
|
|
327
|
+
await closeDialog(page, dialog)
|
|
328
|
+
|
|
329
|
+
await page.reload()
|
|
330
|
+
|
|
331
|
+
await expect
|
|
332
|
+
.poll(async () => {
|
|
333
|
+
return await page.evaluate(() => {
|
|
334
|
+
const raw = localStorage.getItem("settings.v3")
|
|
335
|
+
if (!raw) return
|
|
336
|
+
const parsed = JSON.parse(raw)
|
|
337
|
+
return parsed?.keybinds?.["terminal.toggle"]
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
.toBe("mod+shift+y")
|
|
341
|
+
|
|
342
|
+
const reloaded = await openSettings(page)
|
|
343
|
+
await reloaded.getByRole("tab", { name: "Shortcuts" }).click()
|
|
344
|
+
const reloadedKeybind = reloaded.locator(keybindButtonSelector("terminal.toggle")).first()
|
|
345
|
+
await expect(reloadedKeybind).toContainText("Y")
|
|
346
|
+
await closeDialog(page, reloaded)
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
test("changing command palette keybind works", async ({ page, gotoSession }) => {
|
|
350
|
+
await gotoSession()
|
|
351
|
+
|
|
352
|
+
const dialog = await openSettings(page)
|
|
353
|
+
await dialog.getByRole("tab", { name: "Shortcuts" }).click()
|
|
354
|
+
|
|
355
|
+
const keybindButton = dialog.locator(keybindButtonSelector("command.palette"))
|
|
356
|
+
await expect(keybindButton).toBeVisible()
|
|
357
|
+
|
|
358
|
+
const initialKeybind = await keybindButton.textContent()
|
|
359
|
+
expect(initialKeybind).toContain("P")
|
|
360
|
+
|
|
361
|
+
await keybindButton.click()
|
|
362
|
+
await expect(keybindButton).toHaveText(/press/i)
|
|
363
|
+
|
|
364
|
+
await page.keyboard.press(`${modKey}+Shift+KeyK`)
|
|
365
|
+
await page.waitForTimeout(100)
|
|
366
|
+
|
|
367
|
+
const newKeybind = await keybindButton.textContent()
|
|
368
|
+
expect(newKeybind).toContain("K")
|
|
369
|
+
|
|
370
|
+
const stored = await page.evaluate(() => {
|
|
371
|
+
const raw = localStorage.getItem("settings.v3")
|
|
372
|
+
return raw ? JSON.parse(raw) : null
|
|
373
|
+
})
|
|
374
|
+
expect(stored?.keybinds?.["command.palette"]).toBe("mod+shift+k")
|
|
375
|
+
|
|
376
|
+
await closeDialog(page, dialog)
|
|
377
|
+
|
|
378
|
+
const palette = page.getByRole("dialog").filter({ has: page.getByRole("textbox").first() })
|
|
379
|
+
await expect(palette).toHaveCount(0)
|
|
380
|
+
|
|
381
|
+
await page.keyboard.press(`${modKey}+Shift+K`)
|
|
382
|
+
await page.waitForTimeout(100)
|
|
383
|
+
|
|
384
|
+
await expect(palette).toBeVisible()
|
|
385
|
+
await expect(palette.getByRole("textbox").first()).toBeVisible()
|
|
386
|
+
|
|
387
|
+
await page.keyboard.press("Escape")
|
|
388
|
+
await expect(palette).toHaveCount(0)
|
|
389
|
+
})
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { test, expect } from "../fixtures"
|
|
2
|
+
import { promptSelector } from "../selectors"
|
|
3
|
+
import { closeDialog, openSettings } from "../actions"
|
|
4
|
+
|
|
5
|
+
test("hiding a model removes it from the model picker", async ({ page, gotoSession }) => {
|
|
6
|
+
await gotoSession()
|
|
7
|
+
|
|
8
|
+
await page.locator(promptSelector).click()
|
|
9
|
+
await page.keyboard.type("/model")
|
|
10
|
+
|
|
11
|
+
const command = page.locator('[data-slash-id="model.choose"]')
|
|
12
|
+
await expect(command).toBeVisible()
|
|
13
|
+
await command.hover()
|
|
14
|
+
await page.keyboard.press("Enter")
|
|
15
|
+
|
|
16
|
+
const picker = page.getByRole("dialog")
|
|
17
|
+
await expect(picker).toBeVisible()
|
|
18
|
+
|
|
19
|
+
const target = picker.locator('[data-slot="list-item"]').first()
|
|
20
|
+
await expect(target).toBeVisible()
|
|
21
|
+
|
|
22
|
+
const key = await target.getAttribute("data-key")
|
|
23
|
+
if (!key) throw new Error("Failed to resolve model key from list item")
|
|
24
|
+
|
|
25
|
+
const name = (await target.locator("span").first().innerText()).trim()
|
|
26
|
+
if (!name) throw new Error("Failed to resolve model name from list item")
|
|
27
|
+
|
|
28
|
+
await page.keyboard.press("Escape")
|
|
29
|
+
await expect(picker).toHaveCount(0)
|
|
30
|
+
|
|
31
|
+
const settings = await openSettings(page)
|
|
32
|
+
|
|
33
|
+
await settings.getByRole("tab", { name: "Models" }).click()
|
|
34
|
+
const search = settings.getByPlaceholder("Search models")
|
|
35
|
+
await expect(search).toBeVisible()
|
|
36
|
+
await search.fill(name)
|
|
37
|
+
|
|
38
|
+
const toggle = settings.locator('[data-component="switch"]').filter({ hasText: name }).first()
|
|
39
|
+
const input = toggle.locator('[data-slot="switch-input"]')
|
|
40
|
+
await expect(toggle).toBeVisible()
|
|
41
|
+
await expect(input).toHaveAttribute("aria-checked", "true")
|
|
42
|
+
await toggle.locator('[data-slot="switch-control"]').click()
|
|
43
|
+
await expect(input).toHaveAttribute("aria-checked", "false")
|
|
44
|
+
|
|
45
|
+
await closeDialog(page, settings)
|
|
46
|
+
|
|
47
|
+
await page.locator(promptSelector).click()
|
|
48
|
+
await page.keyboard.type("/model")
|
|
49
|
+
await expect(command).toBeVisible()
|
|
50
|
+
await command.hover()
|
|
51
|
+
await page.keyboard.press("Enter")
|
|
52
|
+
|
|
53
|
+
const pickerAgain = page.getByRole("dialog")
|
|
54
|
+
await expect(pickerAgain).toBeVisible()
|
|
55
|
+
await expect(pickerAgain.locator('[data-slot="list-item"]').first()).toBeVisible()
|
|
56
|
+
|
|
57
|
+
await expect(pickerAgain.locator(`[data-slot="list-item"][data-key="${key}"]`)).toHaveCount(0)
|
|
58
|
+
|
|
59
|
+
await page.keyboard.press("Escape")
|
|
60
|
+
await expect(pickerAgain).toHaveCount(0)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test("showing a hidden model restores it to the model picker", async ({ page, gotoSession }) => {
|
|
64
|
+
await gotoSession()
|
|
65
|
+
|
|
66
|
+
await page.locator(promptSelector).click()
|
|
67
|
+
await page.keyboard.type("/model")
|
|
68
|
+
|
|
69
|
+
const command = page.locator('[data-slash-id="model.choose"]')
|
|
70
|
+
await expect(command).toBeVisible()
|
|
71
|
+
await command.hover()
|
|
72
|
+
await page.keyboard.press("Enter")
|
|
73
|
+
|
|
74
|
+
const picker = page.getByRole("dialog")
|
|
75
|
+
await expect(picker).toBeVisible()
|
|
76
|
+
|
|
77
|
+
const target = picker.locator('[data-slot="list-item"]').first()
|
|
78
|
+
await expect(target).toBeVisible()
|
|
79
|
+
|
|
80
|
+
const key = await target.getAttribute("data-key")
|
|
81
|
+
if (!key) throw new Error("Failed to resolve model key from list item")
|
|
82
|
+
|
|
83
|
+
const name = (await target.locator("span").first().innerText()).trim()
|
|
84
|
+
if (!name) throw new Error("Failed to resolve model name from list item")
|
|
85
|
+
|
|
86
|
+
await page.keyboard.press("Escape")
|
|
87
|
+
await expect(picker).toHaveCount(0)
|
|
88
|
+
|
|
89
|
+
const settings = await openSettings(page)
|
|
90
|
+
|
|
91
|
+
await settings.getByRole("tab", { name: "Models" }).click()
|
|
92
|
+
const search = settings.getByPlaceholder("Search models")
|
|
93
|
+
await expect(search).toBeVisible()
|
|
94
|
+
await search.fill(name)
|
|
95
|
+
|
|
96
|
+
const toggle = settings.locator('[data-component="switch"]').filter({ hasText: name }).first()
|
|
97
|
+
const input = toggle.locator('[data-slot="switch-input"]')
|
|
98
|
+
await expect(toggle).toBeVisible()
|
|
99
|
+
await expect(input).toHaveAttribute("aria-checked", "true")
|
|
100
|
+
|
|
101
|
+
await toggle.locator('[data-slot="switch-control"]').click()
|
|
102
|
+
await expect(input).toHaveAttribute("aria-checked", "false")
|
|
103
|
+
|
|
104
|
+
await toggle.locator('[data-slot="switch-control"]').click()
|
|
105
|
+
await expect(input).toHaveAttribute("aria-checked", "true")
|
|
106
|
+
|
|
107
|
+
await closeDialog(page, settings)
|
|
108
|
+
|
|
109
|
+
await page.locator(promptSelector).click()
|
|
110
|
+
await page.keyboard.type("/model")
|
|
111
|
+
await expect(command).toBeVisible()
|
|
112
|
+
await command.hover()
|
|
113
|
+
await page.keyboard.press("Enter")
|
|
114
|
+
|
|
115
|
+
const pickerAgain = page.getByRole("dialog")
|
|
116
|
+
await expect(pickerAgain).toBeVisible()
|
|
117
|
+
|
|
118
|
+
await expect(pickerAgain.locator(`[data-slot="list-item"][data-key="${key}"]`)).toBeVisible()
|
|
119
|
+
|
|
120
|
+
await page.keyboard.press("Escape")
|
|
121
|
+
await expect(pickerAgain).toHaveCount(0)
|
|
122
|
+
})
|