|
@@ -40,9 +40,9 @@ function effectAt<T extends InputEffect['type']>(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/** Drive plain → adjudicating and hand back the minted attempt. */
|
|
/** Drive plain → adjudicating and hand back the minted attempt. */
|
|
|
-function enterAdjudicating(m: InputMachine, draft: string, mode: 'queue' | 'steer' = 'queue'): SubmitAttempt {
|
|
|
|
|
|
|
+function enterAdjudicating(m: InputMachine, draft: string): SubmitAttempt {
|
|
|
m.dispatch({ type: 'draft-changed', draft })
|
|
m.dispatch({ type: 'draft-changed', draft })
|
|
|
- const fx = m.dispatch({ type: 'enter', mode })
|
|
|
|
|
|
|
+ const fx = m.dispatch({ type: 'enter' })
|
|
|
return effectAt(fx, 0, 'adjudicate').attempt
|
|
return effectAt(fx, 0, 'adjudicate').attempt
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -52,7 +52,7 @@ function enterSubmitting(m: InputMachine, name: string, args: string): { attempt
|
|
|
m.dispatch({ type: 'draft-changed', draft: `/${name.slice(0, 2)}` })
|
|
m.dispatch({ type: 'draft-changed', draft: `/${name.slice(0, 2)}` })
|
|
|
m.dispatch({ type: 'begin-command', claim, span: spanOf(m, 0, m.state.draft.length) })
|
|
m.dispatch({ type: 'begin-command', claim, span: spanOf(m, 0, m.state.draft.length) })
|
|
|
m.dispatch({ type: 'draft-changed', draft: claim.token + args })
|
|
m.dispatch({ type: 'draft-changed', draft: claim.token + args })
|
|
|
- const fx = m.dispatch({ type: 'enter', mode: 'queue' })
|
|
|
|
|
|
|
+ const fx = m.dispatch({ type: 'enter' })
|
|
|
return { attempt: effectAt(fx, 0, 'begin-submit').attempt, claim }
|
|
return { attempt: effectAt(fx, 0, 'begin-submit').attempt, claim }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -63,24 +63,24 @@ function staleAttempt(): SubmitAttempt {
|
|
|
describe('input-machine: plain × enter', () => {
|
|
describe('input-machine: plain × enter', () => {
|
|
|
it('empty and whitespace-only drafts produce nothing', () => {
|
|
it('empty and whitespace-only drafts produce nothing', () => {
|
|
|
const m = new InputMachine()
|
|
const m = new InputMachine()
|
|
|
- expect(m.dispatch({ type: 'enter', mode: 'queue' })).toEqual([])
|
|
|
|
|
|
|
+ expect(m.dispatch({ type: 'enter' })).toEqual([])
|
|
|
m.dispatch({ type: 'draft-changed', draft: ' \n ' })
|
|
m.dispatch({ type: 'draft-changed', draft: ' \n ' })
|
|
|
- expect(m.dispatch({ type: 'enter', mode: 'queue' })).toEqual([])
|
|
|
|
|
|
|
+ expect(m.dispatch({ type: 'enter' })).toEqual([])
|
|
|
expect(m.state.phase).toBe('plain')
|
|
expect(m.state.phase).toBe('plain')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('non-command text falls to the default sink with the given mode', () => {
|
|
|
|
|
|
|
+ it('non-command text falls to the default sink', () => {
|
|
|
const m = new InputMachine()
|
|
const m = new InputMachine()
|
|
|
m.dispatch({ type: 'draft-changed', draft: 'hello world' })
|
|
m.dispatch({ type: 'draft-changed', draft: 'hello world' })
|
|
|
- expect(m.dispatch({ type: 'enter', mode: 'steer' }))
|
|
|
|
|
- .toEqual([{ type: 'default-sink', draft: 'hello world', mode: 'steer' }])
|
|
|
|
|
|
|
+ expect(m.dispatch({ type: 'enter' }))
|
|
|
|
|
+ .toEqual([{ type: 'default-sink', draft: 'hello world' }])
|
|
|
expect(m.state.phase).toBe('plain')
|
|
expect(m.state.phase).toBe('plain')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('leading "/" enters adjudicating with a minted attempt carrying the draft snapshot', () => {
|
|
it('leading "/" enters adjudicating with a minted attempt carrying the draft snapshot', () => {
|
|
|
const m = new InputMachine()
|
|
const m = new InputMachine()
|
|
|
m.dispatch({ type: 'draft-changed', draft: '/goal x' })
|
|
m.dispatch({ type: 'draft-changed', draft: '/goal x' })
|
|
|
- const fx = m.dispatch({ type: 'enter', mode: 'queue' })
|
|
|
|
|
|
|
+ const fx = m.dispatch({ type: 'enter' })
|
|
|
const eff = effectAt(fx, 0, 'adjudicate')
|
|
const eff = effectAt(fx, 0, 'adjudicate')
|
|
|
expect(eff.draft).toBe('/goal x')
|
|
expect(eff.draft).toBe('/goal x')
|
|
|
expect(eff.attempt.draftSnapshot).toBe('/goal x')
|
|
expect(eff.attempt.draftSnapshot).toBe('/goal x')
|
|
@@ -91,14 +91,14 @@ describe('input-machine: plain × enter', () => {
|
|
|
it('leading is judged after trim including newlines', () => {
|
|
it('leading is judged after trim including newlines', () => {
|
|
|
const m = new InputMachine()
|
|
const m = new InputMachine()
|
|
|
m.dispatch({ type: 'draft-changed', draft: '\n\n/goal x' })
|
|
m.dispatch({ type: 'draft-changed', draft: '\n\n/goal x' })
|
|
|
- expect(m.dispatch({ type: 'enter', mode: 'queue' })[0]?.type).toBe('adjudicate')
|
|
|
|
|
|
|
+ expect(m.dispatch({ type: 'enter' })[0]?.type).toBe('adjudicate')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('a non-whitespace prefix before "/" is not leading — default sink', () => {
|
|
it('a non-whitespace prefix before "/" is not leading — default sink', () => {
|
|
|
const m = new InputMachine()
|
|
const m = new InputMachine()
|
|
|
m.dispatch({ type: 'draft-changed', draft: '第一行\n/goal x' })
|
|
m.dispatch({ type: 'draft-changed', draft: '第一行\n/goal x' })
|
|
|
- expect(m.dispatch({ type: 'enter', mode: 'queue' }))
|
|
|
|
|
- .toEqual([{ type: 'default-sink', draft: '第一行\n/goal x', mode: 'queue' }])
|
|
|
|
|
|
|
+ expect(m.dispatch({ type: 'enter' }))
|
|
|
|
|
+ .toEqual([{ type: 'default-sink', draft: '第一行\n/goal x' }])
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
@@ -124,11 +124,11 @@ describe('input-machine: adjudication outcomes', () => {
|
|
|
expect(effectAt(b.dispatch({ type: 'adjudicated', attempt: attemptB, outcome: { claim: claimOf('goal') } }), 0, 'begin-submit').args).toBe('x')
|
|
expect(effectAt(b.dispatch({ type: 'adjudicated', attempt: attemptB, outcome: { claim: claimOf('goal') } }), 0, 'begin-submit').args).toBe('x')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('undefined outcome falls back to the default sink preserving the enter mode', () => {
|
|
|
|
|
|
|
+ it('undefined outcome falls back to the default sink', () => {
|
|
|
const m = new InputMachine()
|
|
const m = new InputMachine()
|
|
|
- const attempt = enterAdjudicating(m, '/unknown thing', 'steer')
|
|
|
|
|
|
|
+ const attempt = enterAdjudicating(m, '/unknown thing')
|
|
|
expect(m.dispatch({ type: 'adjudicated', attempt, outcome: undefined }))
|
|
expect(m.dispatch({ type: 'adjudicated', attempt, outcome: undefined }))
|
|
|
- .toEqual([{ type: 'default-sink', draft: '/unknown thing', mode: 'steer' }])
|
|
|
|
|
|
|
+ .toEqual([{ type: 'default-sink', draft: '/unknown thing' }])
|
|
|
expect(m.state.phase).toBe('plain')
|
|
expect(m.state.phase).toBe('plain')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
@@ -152,7 +152,7 @@ describe('input-machine: adjudication outcomes', () => {
|
|
|
it('enter is a no-op while adjudicating (pending lock)', () => {
|
|
it('enter is a no-op while adjudicating (pending lock)', () => {
|
|
|
const m = new InputMachine()
|
|
const m = new InputMachine()
|
|
|
enterAdjudicating(m, '/goal x')
|
|
enterAdjudicating(m, '/goal x')
|
|
|
- expect(m.dispatch({ type: 'enter', mode: 'queue' })).toEqual([])
|
|
|
|
|
|
|
+ expect(m.dispatch({ type: 'enter' })).toEqual([])
|
|
|
expect(m.state.phase).toBe('adjudicating')
|
|
expect(m.state.phase).toBe('adjudicating')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
@@ -587,7 +587,7 @@ describe('input-machine: paste plane', () => {
|
|
|
|
|
|
|
|
const b = new InputMachine()
|
|
const b = new InputMachine()
|
|
|
b.dispatch({ type: 'paste-begin', text: 'plain text', selection: { start: 0, end: 0 } })
|
|
b.dispatch({ type: 'paste-begin', text: 'plain text', selection: { start: 0, end: 0 } })
|
|
|
- b.dispatch({ type: 'enter', mode: 'queue' })
|
|
|
|
|
|
|
+ b.dispatch({ type: 'enter' })
|
|
|
expect(b.state.paste).toBeUndefined()
|
|
expect(b.state.paste).toBeUndefined()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
@@ -755,7 +755,7 @@ describe('input-machine: submitting transaction', () => {
|
|
|
it('enter and begin-command are locked; draft-changed is recorded without leaving submitting', () => {
|
|
it('enter and begin-command are locked; draft-changed is recorded without leaving submitting', () => {
|
|
|
const m = new InputMachine()
|
|
const m = new InputMachine()
|
|
|
enterSubmitting(m, 'goal', 'x')
|
|
enterSubmitting(m, 'goal', 'x')
|
|
|
- expect(m.dispatch({ type: 'enter', mode: 'queue' })).toEqual([])
|
|
|
|
|
|
|
+ expect(m.dispatch({ type: 'enter' })).toEqual([])
|
|
|
expect(m.dispatch({ type: 'draft-changed', draft: '/goal y' })).toEqual([])
|
|
expect(m.dispatch({ type: 'draft-changed', draft: '/goal y' })).toEqual([])
|
|
|
expect(m.state).toMatchObject({ phase: 'submitting', draft: '/goal y' })
|
|
expect(m.state).toMatchObject({ phase: 'submitting', draft: '/goal y' })
|
|
|
})
|
|
})
|
|
@@ -768,7 +768,7 @@ describe('input-machine: submitting transaction', () => {
|
|
|
m.dispatch({ type: 'draft-changed', draft: '/go', editRange: { start: 0, end: 1, insertedLength: 0 } })
|
|
m.dispatch({ type: 'draft-changed', draft: '/go', editRange: { start: 0, end: 1, insertedLength: 0 } })
|
|
|
m.dispatch({ type: 'begin-command', claim: claimOf('goal'), span: spanOf(m, 0, 3) })
|
|
m.dispatch({ type: 'begin-command', claim: claimOf('goal'), span: spanOf(m, 0, 3) })
|
|
|
m.dispatch({ type: 'draft-changed', draft: '/goal go' })
|
|
m.dispatch({ type: 'draft-changed', draft: '/goal go' })
|
|
|
- const attempt = effectAt(m.dispatch({ type: 'enter', mode: 'queue' }), 0, 'begin-submit').attempt
|
|
|
|
|
|
|
+ const attempt = effectAt(m.dispatch({ type: 'enter' }), 0, 'begin-submit').attempt
|
|
|
const fx = m.dispatch({ type: 'submit-settled', attempt, ok: true, outcome: { kind: 'success', text: 'goal set' } })
|
|
const fx = m.dispatch({ type: 'submit-settled', attempt, ok: true, outcome: { kind: 'success', text: 'goal set' } })
|
|
|
expect(fx).toEqual([{ type: 'notice', level: 'info', text: 'goal set' }])
|
|
expect(fx).toEqual([{ type: 'notice', level: 'info', text: 'goal set' }])
|
|
|
expect(m.state).toMatchObject({ phase: 'plain', draft: '', occurrences: [] })
|
|
expect(m.state).toMatchObject({ phase: 'plain', draft: '', occurrences: [] })
|
|
@@ -809,7 +809,7 @@ describe('input-machine: submitting transaction', () => {
|
|
|
const m = new InputMachine()
|
|
const m = new InputMachine()
|
|
|
const { attempt: first } = enterSubmitting(m, 'goal', 'x')
|
|
const { attempt: first } = enterSubmitting(m, 'goal', 'x')
|
|
|
m.dispatch({ type: 'submit-settled', attempt: first, ok: false, message: 'retry' })
|
|
m.dispatch({ type: 'submit-settled', attempt: first, ok: false, message: 'retry' })
|
|
|
- const second = effectAt(m.dispatch({ type: 'enter', mode: 'queue' }), 0, 'begin-submit').attempt
|
|
|
|
|
|
|
+ const second = effectAt(m.dispatch({ type: 'enter' }), 0, 'begin-submit').attempt
|
|
|
expect(second.seq).not.toBe(first.seq)
|
|
expect(second.seq).not.toBe(first.seq)
|
|
|
expect(m.dispatch({ type: 'submit-settled', attempt: first, ok: true })).toEqual([])
|
|
expect(m.dispatch({ type: 'submit-settled', attempt: first, ok: true })).toEqual([])
|
|
|
expect(m.state.phase).toBe('submitting')
|
|
expect(m.state.phase).toBe('submitting')
|