Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
commit
03634b1ca3
11340 files changed
+3442369
No files matched your search
@@ -0,0 +1,16 @@
|
||||
/** A promise plus the handle that settles it.
|
||||
*
|
||||
* For tests that need a call to stay in flight — asserting the pending UI,
|
||||
* proving a second call is coalesced or ignored, ordering two resolutions —
|
||||
* and then release it on their own schedule. */
|
||||
export function deferred<T = void>() {
|
||||
let resolve!: (value: PromiseLike<T> | T) => void
|
||||
let reject!: (reason?: unknown) => void
|
||||
|
||||
const promise = new Promise<T>((done, fail) => {
|
||||
resolve = done
|
||||
reject = fail
|
||||
})
|
||||
|
||||
return { promise, reject, resolve }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// jsdom implements no layout and no animation, so component libraries that
|
||||
// call those APIs unconditionally throw on mount. These install inert
|
||||
// stand-ins: enough for the component to render, never enough to assert on. A
|
||||
// test that needs one of them to actually report should install its own.
|
||||
|
||||
import { vi } from 'vitest'
|
||||
|
||||
class InertResizeObserver {
|
||||
disconnect() {}
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
}
|
||||
|
||||
/** A ResizeObserver that accepts observers and never calls them back. */
|
||||
export function stubResizeObserver() {
|
||||
vi.stubGlobal('ResizeObserver', InertResizeObserver)
|
||||
}
|
||||
|
||||
/** The pointer-capture and scroll calls Radix and cmdk make while opening a
|
||||
* popover, menu, or combobox — and again on the item they focus. */
|
||||
export function stubMenuDomApis() {
|
||||
Element.prototype.hasPointerCapture ??= () => false
|
||||
Element.prototype.setPointerCapture ??= () => undefined
|
||||
Element.prototype.releasePointerCapture ??= () => undefined
|
||||
Element.prototype.scrollIntoView ??= () => undefined
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { OAuthProvider } from '@/types/hermes'
|
||||
|
||||
/** A logged-out PKCE provider. Onboarding branches on `flow` and
|
||||
* `status.logged_in`; the rest is filler the UI only echoes. */
|
||||
export function makeOAuthProvider(id: string, name = id): OAuthProvider {
|
||||
return {
|
||||
cli_command: `hermes login ${id}`,
|
||||
docs_url: `https://example.com/${id}`,
|
||||
flow: 'pkce',
|
||||
id,
|
||||
name,
|
||||
status: { logged_in: false }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { act, type ReactNode } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
|
||||
/** A React root in a container attached to the document, for the handful of
|
||||
* suites that cannot use Testing Library's `render`.
|
||||
*
|
||||
* They need the component mounted in the real document — a canvas that must
|
||||
* paint, an element measured by its parent, a portal that reads
|
||||
* `document.body` — and they need to unmount inside `act` mid-test to assert
|
||||
* on teardown. Cleanup is idempotent so `afterEach` can call it after a test
|
||||
* already did. */
|
||||
export function reactRoot() {
|
||||
let root: Root | null = null
|
||||
let container: HTMLDivElement | null = null
|
||||
|
||||
return {
|
||||
get container() {
|
||||
return container
|
||||
},
|
||||
/** The root itself, for a re-render that has to share an `act` with
|
||||
* something else — a mutation-observer callback, a timer flush. */
|
||||
get root() {
|
||||
return root
|
||||
},
|
||||
render(ui: ReactNode) {
|
||||
container = document.createElement('div')
|
||||
document.body.append(container)
|
||||
root = createRoot(container)
|
||||
|
||||
act(() => {
|
||||
root!.render(ui)
|
||||
})
|
||||
},
|
||||
unmount() {
|
||||
if (root) {
|
||||
act(() => root!.unmount())
|
||||
}
|
||||
|
||||
container?.remove()
|
||||
root = null
|
||||
container = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { SessionInfo } from '@/types/hermes'
|
||||
|
||||
/** A `SessionInfo` with every field at its zero value.
|
||||
*
|
||||
* `SessionInfo` mirrors a backend row and grows a field whenever the API does.
|
||||
* Spelling the whole shape out per test file meant each new field broke a
|
||||
* handful of unrelated suites; here it breaks one line. Tests override only
|
||||
* what they assert on. */
|
||||
export function makeSessionInfo(overrides: Partial<SessionInfo> = {}): SessionInfo {
|
||||
return {
|
||||
archived: false,
|
||||
cwd: null,
|
||||
ended_at: null,
|
||||
id: 'session',
|
||||
input_tokens: 0,
|
||||
is_active: false,
|
||||
last_active: 0,
|
||||
message_count: 0,
|
||||
model: null,
|
||||
output_tokens: 0,
|
||||
preview: null,
|
||||
source: null,
|
||||
started_at: 0,
|
||||
title: null,
|
||||
tool_call_count: 0,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
let nextId = 0
|
||||
|
||||
/** A real-looking CLI session rooted at `cwd`, with a fresh id. What the
|
||||
* folder-grouping and per-project colour tests bucket; they care about the
|
||||
* path and that the session is otherwise unremarkable. */
|
||||
export function makeCwdSession(cwd: null | string, overrides: Partial<SessionInfo> = {}): SessionInfo {
|
||||
return makeSessionInfo({
|
||||
cwd,
|
||||
id: `s${nextId++}`,
|
||||
last_active: 1_000,
|
||||
message_count: 1,
|
||||
model: 'claude',
|
||||
source: 'cli',
|
||||
started_at: 1_000,
|
||||
...overrides
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Stand-ins for the two window-visibility signals the renderer reacts to:
|
||||
// Electron's `hermesDesktop.onWindowStateChanged` bridge and the DOM's own
|
||||
// `document.hidden`. Both are read-only under jsdom, and anything that pauses
|
||||
// work while the window is hidden — the pet, the terminal pane, budgeted loops,
|
||||
// pulse animations — needs to drive them.
|
||||
|
||||
import { vi } from 'vitest'
|
||||
|
||||
export interface WindowStatePayload {
|
||||
isMinimized?: boolean
|
||||
isVisible?: boolean
|
||||
}
|
||||
|
||||
export interface WindowStateBridge {
|
||||
/** Push a state change to whoever subscribed through the bridge. */
|
||||
emit: (payload: WindowStatePayload) => void
|
||||
/** The unsubscribe handed back to the subscriber, for teardown assertions. */
|
||||
off: ReturnType<typeof vi.fn>
|
||||
}
|
||||
|
||||
/** Install a fake `window.hermesDesktop` window-state channel. */
|
||||
export function installWindowStateBridge(): WindowStateBridge {
|
||||
let callback: ((payload: WindowStatePayload) => void) | null = null
|
||||
|
||||
const off = vi.fn(() => {
|
||||
callback = null
|
||||
})
|
||||
|
||||
Object.defineProperty(window, 'hermesDesktop', {
|
||||
configurable: true,
|
||||
value: {
|
||||
onWindowStateChanged: vi.fn((next: (payload: WindowStatePayload) => void) => {
|
||||
callback = next
|
||||
|
||||
return off
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return { emit: payload => callback?.(payload), off }
|
||||
}
|
||||
|
||||
/** Drive `document.hidden` / `document.visibilityState` together. */
|
||||
export function setDocumentHidden(hidden: boolean) {
|
||||
Object.defineProperty(document, 'hidden', { configurable: true, value: hidden })
|
||||
Object.defineProperty(document, 'visibilityState', { configurable: true, value: hidden ? 'hidden' : 'visible' })
|
||||
}
|
||||
Reference in new issue
Block a user