@johngw/stream
Version:
Reactive programming tools using the WHATWG Streams API.
180 lines (161 loc) • 3.91 kB
text/typescript
import { write } from '@johngw/stream/sinks/write'
import { StatefulSubject } from '@johngw/stream/subjects/StatefulSubject'
import { timeout } from '@johngw/stream-common'
interface State {
authors: string[]
}
type Actions = {
'add author': string
nothing: void
}
let subject: StatefulSubject<Actions, State>
beforeEach(() => {
subject = new StatefulSubject<Actions, State>({
__INIT__: () => ({ authors: [] }),
'add author': (state, author) =>
state.authors.includes(author)
? state
: {
...state,
authors: [...state.authors, author],
},
nothing: (state) => state,
})
})
test('the __INIT__ action', async () => {
const fn = jest.fn()
const controller = subject.control()
controller.close()
await subject.fork().pipeTo(write(fn))
expect(fn).toHaveBeenCalledTimes(1)
expect(fn.mock.calls).toMatchInlineSnapshot(`
[
[
{
"action": "__INIT__",
"state": {
"authors": [],
},
},
],
]
`)
})
test('a reducer that changes state', async () => {
const fn = jest.fn()
const controller = subject.control()
controller.dispatch('add author', 'Jane Austin')
controller.close()
await subject.fork().pipeTo(write(fn))
expect(fn).toHaveBeenCalledTimes(1)
expect(fn.mock.calls).toMatchInlineSnapshot(`
[
[
{
"action": "add author",
"param": "Jane Austin",
"state": {
"authors": [
"Jane Austin",
],
},
},
],
]
`)
})
test('a reducer that doesnt change state', async () => {
const fn = jest.fn()
const controller = subject.control()
controller.dispatch('nothing')
controller.close()
await subject.fork().pipeTo(write(fn))
expect(fn).toHaveBeenCalledTimes(1)
expect(fn.mock.calls).toMatchInlineSnapshot(`
[
[
{
"action": "__INIT__",
"state": {
"authors": [],
},
},
],
]
`)
})
test('multiple calls', async () => {
const fn = jest.fn()
const promise = subject.fork().pipeTo(write(fn))
const controller = subject.control()
// There's a bug in the web-streams-polyfill that resolves the above
// promise too early.
await timeout()
controller.dispatch('add author', 'Jane Austin')
controller.dispatch('add author', 'George Orwell')
controller.dispatch('add author', 'Jane Austin')
controller.close()
await promise
expect(fn).toHaveBeenCalledTimes(3)
expect(fn.mock.calls).toMatchInlineSnapshot(`
[
[
{
"action": "__INIT__",
"state": {
"authors": [],
},
},
],
[
{
"action": "add author",
"param": "Jane Austin",
"state": {
"authors": [
"Jane Austin",
],
},
},
],
[
{
"action": "add author",
"param": "George Orwell",
"state": {
"authors": [
"Jane Austin",
"George Orwell",
],
},
},
],
]
`)
})
test('typing errors', () => {
expect(
() =>
new StatefulSubject<Actions, State>(
// @ts-expect-error There is no __INIT__ method
{
'add author': (state) => state,
}
)
).toThrow()
new StatefulSubject<Actions, State>({
// @ts-expect-error Incorrect state shape
__INIT__: () => ({ mung: 'face' }),
// @ts-expect-error Function declaration does not match action
nothing: (state, param: string) => ({ ...state, authors: [param] }),
})
subject.control().close()
return subject.fork().pipeTo(
write((chunk) => {
// @ts-expect-error Unknown action name
if (chunk.action === 'unknown') {
//
}
})
)
})