mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-13 08:02:47 +00:00
fix: measure a queued discard against the value it named, not a write count
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6cbf34c045
commit
8a6fa9b8a4
@@ -472,13 +472,15 @@ class Entry<V> {
|
||||
}
|
||||
|
||||
discard(): Promise<DiscardOutcome> {
|
||||
// What a discard throws away is the value as of the click, like a save sends the value as
|
||||
// of the click: one typed while it waited its turn behind a save is newer than it, and
|
||||
// reverting that would drop it with nothing left to recover it from.
|
||||
const askedAt = this.edits
|
||||
// What a discard throws away is the value as of the click, the way a save sends the value
|
||||
// as of the click. One typed while it waited its turn is newer than the discard, and
|
||||
// reverting it would drop it with nothing left holding it. Measured as the value and not
|
||||
// as a count of writes: an outside write of what is already on screen changes nothing the
|
||||
// user can see, so it does not make their discard stale.
|
||||
const asked = serialize(this.value)
|
||||
return this.run(async () => {
|
||||
if (!this.loaded || this.retired) return { removed: false }
|
||||
if (this.edits !== askedAt) return { removed: false }
|
||||
if (serialize(this.value) !== asked) return { removed: false }
|
||||
if (this.origin === 'draft') {
|
||||
const kept = snapshot(this.value)
|
||||
const keptRow = this.row
|
||||
|
||||
@@ -519,6 +519,29 @@ describe('item store: origins', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('discards behind an outside write of the value already on screen', async () => {
|
||||
const rows = fakeRows()
|
||||
const gate = deferred()
|
||||
const { item } = await open(
|
||||
rows,
|
||||
adapter({ deployed: deployedRes }, () => gate.promise)
|
||||
)
|
||||
item.value = { ...deployedRes, description: 'edited' }
|
||||
const saving = item.save()
|
||||
await settle()
|
||||
const discarding = item.discard()
|
||||
await settle()
|
||||
// The chat saves what the user is already looking at: nothing on screen moves, so the
|
||||
// discard is still about the value its click named.
|
||||
item.applyExternal({ ...deployedRes, description: 'edited' })
|
||||
gate.resolve()
|
||||
|
||||
expect(await saving).toMatchObject({ ok: true })
|
||||
expect(await discarding).toEqual({ removed: false })
|
||||
expect(item.dirty).toBe(false)
|
||||
expect(rows.writes.at(-1)).toEqual({ path: 'u/me/r', value: null })
|
||||
})
|
||||
|
||||
it('keeps a draft-only item an outside write put back while its delete was going', async () => {
|
||||
const rows = fakeRows()
|
||||
const draft = { ...deployedRes, description: 'only a draft' }
|
||||
|
||||
Reference in New Issue
Block a user