mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-15 08:02:53 +00:00
* feat(frontend): chat-scoped unified session changes bar Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(frontend): drop diff-baseline toggle, show natural per-row diffs Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): carry Draft marker to expanded raw-app file rows Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): show raw-app Draft badge once at tree root, not per file Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(frontend): reuse shared DraftBadge in session diff drawer Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): show draft-author avatars in session diff badge, icon-only in sidebar Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): keep badge pill around avatar in icon-only DraftBadge Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): small draft marker = indigo pen + avatar; correct itemKind label Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(frontend): drop package-lock churn from merge (match origin/main) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): dedup diff-button count for legacy fork sessions; test mask helper Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): collapsible session diff panel + per-row open-diff action Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): shared sessionDeployModel for review & deploy (S1) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): model-driven session review drawer, deploy inert (S2) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): wire session deploy + on-behalf/conflict gating (S3) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): behind banner + Update fork + deployment request (S4) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): session changes dock opens drawer by filter (S5) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): session review UI polish (badge, dock, In parent, tree width) - draft rows show only the avatar DraftBadge, not a duplicate state pill - drop redundant dock Review button (same as "N to review") - rename Done -> In parent with a "deployed in parent workspace" tooltip - widen the file tree Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): existence-gate In parent rows; Badge filters; badge hover - drop discarded mask-only items from the In-parent segment (existence check) - use the Badge component for the drawer filter segments and the changes dock - soften the blue Badge hover (blue-50 base was jumping to blue-200) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): polish session diff drawer (layout, badges, actions) - remove empty fork-banner gap; uniform sidebar tree padding - full-bleed diff list: drop card borders/side padding, separators between items - clamp tree x-overflow; right-align tree badges (min-w-0 on the row button) - brand-compliant selected filter badges; smaller draft badge - hide per-row open-diff button when the panel is open - rename "Delete draft" to "Discard draft" (destructive); remove header Review button - larger sm deploy/discard action buttons; remove per-item diff-content collapse Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): session Edits dock — deploy gating + change-op tracking Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): session bar per-status badges; drop change-op tracking Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): unwrap raw apps into per-file tree in session diff sidebar Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): dot-parcours pipeline (badge-derived, melt tooltip) + discard confirm Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): behind-only session item reads as deployed, not bare Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * style(frontend): even sidebar tree margins; gutter-aware right padding Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): pass chat id as from_session; wire deploying flag Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): session diff drawer scroll-to-flush, ordering, spacer, deploy gating Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): preserve chat mask on compact; guard stale existence checks; clear poll timers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): refresh bar after drawer deploys; conflict hint over chip; plain conflict badge Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * style(frontend): diff drawer card layout with flash ring and aligned insets Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): hide stale deployed chip once row status badge reads deployed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(frontend): session dock to two states; drop parent deploy Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): staged deploy animation in session edits drawer Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): stale-draft warning in session edits drawer Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): keep chat mask honest on deploy and discard Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): map trigger_email deploy kind; serialize mask persists Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): reset mask on new chat; close review-flagged races Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * style(frontend): rename session drawer title to Edited during session Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): keep mask persist queue alive after a failed save Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): sync session chatId on chat rotation; gate deploy on canWrite Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): keep compare handoff for deletion-only session edits Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): hold deploy success beat across re-keyed rows Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
88 lines
3.6 KiB
TypeScript
88 lines
3.6 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
|
|
// Documents the cache-invalidation contract behind the session fork diff. The
|
|
// bug (cubic P2): per-item raw diffs (`loadedDiffs[key]`) and per-item
|
|
// `summaries` are component-local $state in WorkspaceDiffDrawer that persist for
|
|
// the drawer's lifetime. The comparison is re-fetched on every open(), but
|
|
// loadDiffFor short-circuits on `loadedDiffs[key]` — so an edit-then-reopen
|
|
// would show fresh counts but the prior open's cached expanded raw content. The
|
|
// fix clears both records on open() (and, for an in-place data-source swap like
|
|
// the draft↔fork toggle, on a `resetKey` change). This test pins the contract
|
|
// from the outside: nothing is allowed to read a stale per-item value across a
|
|
// re-fetch.
|
|
|
|
type LoadedDiff = { state: 'ready'; parentRaw: string; forkRaw: string }
|
|
|
|
function simulateFetchComparison(
|
|
state: { loadedDiffs: Record<string, LoadedDiff>; summaries: Record<string, string> },
|
|
items: Array<{ key: string; parentRaw: string; forkRaw: string; summary: string }>
|
|
) {
|
|
// The fix: clear caches *before* re-populating, matching the production
|
|
// code's `loadedDiffs = {}; summaries = {}` at the top of fetchComparison.
|
|
state.loadedDiffs = {}
|
|
state.summaries = {}
|
|
for (const it of items) {
|
|
// loadDiffFor's cache-hit shortcut: if state.loadedDiffs[key] exists,
|
|
// it returns early. With the reset above this is always false here, so
|
|
// every item gets fresh content.
|
|
if (state.loadedDiffs[it.key]) continue
|
|
state.loadedDiffs[it.key] = { state: 'ready', parentRaw: it.parentRaw, forkRaw: it.forkRaw }
|
|
state.summaries[it.key] = it.summary
|
|
}
|
|
}
|
|
|
|
describe('ForkDiffDrawer.fetchComparison — cache invalidation', () => {
|
|
it('reopen after edit replaces stale per-item raw content', () => {
|
|
const state = {
|
|
loadedDiffs: {} as Record<string, LoadedDiff>,
|
|
summaries: {} as Record<string, string>
|
|
}
|
|
|
|
// First open: populate.
|
|
simulateFetchComparison(state, [
|
|
{ key: 'script/f/foo', parentRaw: 'v1', forkRaw: 'v1-fork', summary: 'summary v1' }
|
|
])
|
|
expect(state.loadedDiffs['script/f/foo']).toEqual({
|
|
state: 'ready',
|
|
parentRaw: 'v1',
|
|
forkRaw: 'v1-fork'
|
|
})
|
|
expect(state.summaries['script/f/foo']).toBe('summary v1')
|
|
|
|
// User edits the script in the session editor, closes drawer, reopens.
|
|
// Without the reset, the cache-hit shortcut would keep 'v1-fork'.
|
|
simulateFetchComparison(state, [
|
|
{ key: 'script/f/foo', parentRaw: 'v1', forkRaw: 'v2-fork', summary: 'summary v2' }
|
|
])
|
|
expect(state.loadedDiffs['script/f/foo']).toEqual({
|
|
state: 'ready',
|
|
parentRaw: 'v1',
|
|
forkRaw: 'v2-fork'
|
|
})
|
|
expect(state.summaries['script/f/foo']).toBe('summary v2')
|
|
})
|
|
|
|
it('reopen drops entries for items that vanished from the new comparison', () => {
|
|
const state = {
|
|
loadedDiffs: {} as Record<string, LoadedDiff>,
|
|
summaries: {} as Record<string, string>
|
|
}
|
|
|
|
simulateFetchComparison(state, [
|
|
{ key: 'script/f/a', parentRaw: 'x', forkRaw: 'x-fork', summary: 'a' },
|
|
{ key: 'flow/f/b', parentRaw: 'y', forkRaw: 'y-fork', summary: 'b' }
|
|
])
|
|
expect(Object.keys(state.loadedDiffs).sort()).toEqual(['flow/f/b', 'script/f/a'])
|
|
|
|
// User merges 'flow/f/b' so it's no longer ahead of parent.
|
|
simulateFetchComparison(state, [
|
|
{ key: 'script/f/a', parentRaw: 'x', forkRaw: 'x-fork', summary: 'a' }
|
|
])
|
|
// Without the reset, the orphan entry would linger in loadedDiffs and
|
|
// the tree could render a row whose raw content references a path no
|
|
// longer in the live comparison.
|
|
expect(Object.keys(state.loadedDiffs)).toEqual(['script/f/a'])
|
|
expect(state.summaries['flow/f/b']).toBeUndefined()
|
|
})
|
|
})
|