Files
windmill/frontend/src/lib/components/pageHeaderRegistry.test.ts
T
GuilhemandClaude Opus 5.5 34db23952a feat: move the open in AI session button to the page header (#11888)
* feat: move the open in AI session button to the page header

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix: address local review findings on the header session button

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix: update header fold comments and test the page header session source

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix: show the header session button to users who cannot edit the item

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* feat: place the header session button right before the page's main action

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix: drop an always-true guard and stale comments around the header session button

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix: keep the compact autosave label for screen readers and correct two comments

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 18:34:00 +02:00

72 lines
2.8 KiB
TypeScript

import { describe, expect, it, afterEach } from 'vitest'
import { pageHeader, type PageHeaderContent } from './pageHeaderRegistry.svelte'
import type { Snippet } from 'svelte'
// The merge rule every registration site depends on: a page sets the frame and something nested
// in it fills the breadcrumb, so whether a component registers at all decides whose name the band
// shows. A viewer that registered wherever it was mounted renamed the band of the session hosting
// it.
const snippet = (name: string) => name as unknown as Snippet
const ids: number[] = []
function register(c: PageHeaderContent) {
ids.push(pageHeader.register(() => c))
}
afterEach(() => {
while (ids.length) pageHeader.release(ids.pop()!)
})
describe('pageHeader', () => {
it('is empty with nothing registered', () => {
expect(pageHeader.content).toBeUndefined()
})
it('lets a later registration win field by field', () => {
register({ section: { label: 'Sessions' }, barRightInset: 400 })
register({ item: { path: 'u/admin/app' } })
expect(pageHeader.content).toEqual({
section: { label: 'Sessions' },
item: { path: 'u/admin/app' },
barRightInset: 400
})
})
it('gives the fields of a released registration back', () => {
register({ section: { label: 'Sessions' } })
register({ item: { path: 'u/admin/app' } })
pageHeader.release(ids.pop()!)
expect(pageHeader.content?.item).toBeUndefined()
expect(pageHeader.content?.section).toEqual({ label: 'Sessions' })
})
it('orders actions by actionsOrder, then by registration', () => {
register({ actions: snippet('page'), actionsOrder: 10 })
register({ actions: snippet('nested') })
register({ actions: snippet('alsoNested') })
expect(pageHeader.actions.map((a) => a.render)).toEqual(['nested', 'alsoNested', 'page'])
})
it('carries the contexts of each registration with its actions', () => {
const pageContexts = new Map([['TriggerContext', 1]])
register({ actions: snippet('page'), contexts: pageContexts })
register({ actions: snippet('nested') })
expect(pageHeader.actions.map((a) => a.contexts)).toEqual([pageContexts, undefined])
})
// In-page buttons stand down when the header offers the same hand-off, so which registration
// is read decides which of them disappear; a nested registration that names no source must not
// hide the page's.
it('takes the session source of the innermost registration that has one', () => {
const route = { page: () => '/runs' }
const editor = { target: { kind: 'script' as const, path: 'u/admin/s' } }
register({ sessionSource: route })
register({ sessionSource: editor })
register({ section: { label: 'Nested' } })
expect(pageHeader.sessionSource).toBe(editor)
pageHeader.release(ids.splice(1, 1)[0])
expect(pageHeader.sessionSource).toBe(route)
pageHeader.release(ids.shift()!)
expect(pageHeader.sessionSource).toBeUndefined()
})
})