Skip to content

Commit

Permalink
Merge pull request #94 from undoZen/testcases-for-fromEven-fromQuery
Browse files Browse the repository at this point in the history
test(remesh-react): 添加关于 fromEvent, fromQuery 回收机制的测试用例
  • Loading branch information
Lucifier129 committed Apr 1, 2024
2 parents 73253a5 + a517333 commit 815dcd2
Show file tree
Hide file tree
Showing 5 changed files with 2,535 additions and 1,488 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [14.x, 16.x, 17.x, 18.x]
node-version: [16.x, 17.x, 18.x, 20.x]
os: [ubuntu-latest, macos-latest, windows-latest]

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- name: Check out
uses: actions/checkout@v2

- uses: pnpm/action-setup@v2.0.1
- uses: pnpm/action-setup@v2.4.0
with:
version: '6'
version: '8'

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1.4.4
uses: actions/setup-node@v4.0.2
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
Expand Down
326 changes: 326 additions & 0 deletions packages/remesh-react/__tests__/remesh-react-query-basic.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
import { act } from 'react-dom/test-utils'
import { interval } from 'rxjs'
import { map, startWith, switchMap, take, tap } from 'rxjs/operators'

import { Remesh } from 'remesh'

import React, { useState } from 'react'
import ReactDOM, { Root } from 'react-dom/client'
import { RemeshRoot, RemeshScope, useRemeshDomain, useRemeshEvent, useRemeshQuery, useRemeshSend } from '../src'
import { delay } from './utils'

const SendCodeDomain = Remesh.domain({
name: 'SendCodeDomain',
impl(domain) {
const SuccessEvent = domain.event({
name: 'SuccessEvent',
})

const SendCommand = domain.command({
name: 'SendCommand',
impl() {
return [SuccessEvent()]
},
})

return {
command: {
SendCommand,
},
event: {
SuccessEvent,
},
}
},
})

const PageDomain = Remesh.domain({
name: 'PageDomain',
impl(domain) {
const PageState = domain.state({
name: 'PageState',
default: {
step: 'conflict' as 'additional' | 'conflict',
},
})

const PageQuery = domain.query({
name: 'PageQuery',
impl({ get }) {
return get(PageState())
},
})

const BackCommand = domain.command({
name: 'BackCommand',
impl() {
return PageState().new({ step: 'conflict' })
},
})

const CreateCommand = domain.command({
name: 'CreateCommand',
impl() {
return PageState().new({ step: 'additional' })
},
})

return {
query: {
PageQuery,
},
command: {
BackCommand,
CreateCommand,
},
}
},
})

const COUNTDOWN_SECONDS = 5

jest.setTimeout(15 * 1000)
describe('remesh-react-event', () => {
let container!: HTMLDivElement
let root!: Root

beforeEach(() => {
container = document.createElement('div')
container.id = 'root'
document.body.appendChild(container)
root = ReactDOM.createRoot(container)
})

afterEach(() => {
root.unmount()
document.body.removeChild(container)
})

it('can subscribe domain-query via fromQuery and useRemeshQuery at the same time', async () => {
const triggerEffectFn = jest.fn()
const eventEffectFn = jest.fn()
const queryEffectFn = jest.fn()
const useRemeshEventFn = jest.fn()

const AdditionalDomain = Remesh.domain({
name: 'AdditionalDomain',
impl(domain, id: number) {
const sendCodeDomain = domain.getDomain(SendCodeDomain())

const SendCodeCommand = domain.command({
name: 'SendCodeCommand',
impl() {
return sendCodeDomain.command.SendCommand()
},
})

const SendCodeState = domain.state({
name: 'SendCodeState',
default: {
tick: 0,
},
})

const SendCodeQuery = domain.query({
name: 'SendCodeQuery',
impl({ get }) {
const state = get(SendCodeState())
return { tick: state.tick, disabled: state.tick > 0 }
},
})

const UpdateSendCodeCommand = domain.command({
name: 'UpdateSendCodeCommand',
impl(_, tick: number) {
return SendCodeState().new({ tick })
},
})

domain.effect({
name: 'SendCodeEffect',
impl({ fromEvent }) {
return fromEvent(sendCodeDomain.event.SuccessEvent).pipe(
tap((v) => {
triggerEffectFn()
console.log('receive event in effect')
}),
switchMap(() =>
interval(1000)
.pipe(
map((tick) => COUNTDOWN_SECONDS - tick - 1),
take(COUNTDOWN_SECONDS),
)
.pipe(startWith(COUNTDOWN_SECONDS)),
),
tap((tick) => {
console.log('tick', tick)
eventEffectFn()
}),
map((tick) => [UpdateSendCodeCommand(tick)]),
)
},
})

domain.effect({
name: 'PrintTickEffect',
impl({ fromQuery }) {
return fromQuery(SendCodeQuery()).pipe(
tap((state) => {
console.log('got query update:', JSON.stringify(state))
queryEffectFn()
}),
map((state) => []),
)
},
})

return {
query: {
SendCodeQuery,
},
command: {
SendCodeCommand,
},
event: {
SendCodeSuccessEvent: sendCodeDomain.event.SuccessEvent,
},
}
},
})

function Additional({ id }: { id: number }) {
console.log('get prop id:', id)
const send = useRemeshSend()
const domain = useRemeshDomain(AdditionalDomain(id))

useRemeshEvent(domain.event.SendCodeSuccessEvent, () => {
useRemeshEventFn()
console.log('receive event in Additional View')
})

const state = useRemeshQuery(domain.query.SendCodeQuery())

return (
<div>
<button
id={'send-code'}
disabled={state.disabled}
onClick={() => {
send(domain.command.SendCodeCommand())
}}
>
<>Send Code</>
<>({state.tick})</>
</button>
</div>
)
}
function Page() {
const send = useRemeshSend()
const domain = useRemeshDomain(PageDomain())
const { step } = useRemeshQuery(domain.query.PageQuery())
const [id, setId] = useState(0)

return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
<div
id={'back'}
onClick={() => {
send(domain.command.BackCommand())
}}
>
Back
</div>
{step === 'conflict' && (
<div
id={'conflict'}
onClick={() => {
setId((id) => id + 1)
send(domain.command.CreateCommand())
}}
>
Expand
</div>
)}

{step === 'additional' && <Additional id={id} />}
</div>
)
}

const store = Remesh.store()
await act(() => {
root.render(
<RemeshRoot store={store}>
<RemeshScope domains={[SendCodeDomain()]}>
<Page />
</RemeshScope>
</RemeshRoot>,
)
})

await act(() => {
const conflictButton = document.getElementById('conflict') as HTMLButtonElement
conflictButton.dispatchEvent(new MouseEvent('click', { bubbles: true }))
})

await act(() => {
const sendCode = document.getElementById('send-code') as HTMLSpanElement
sendCode.dispatchEvent(new MouseEvent('click', { bubbles: true }))
})

expect(triggerEffectFn).toBeCalledTimes(1)
expect(useRemeshEventFn).toBeCalledTimes(1)

await act(() => {
const sendCode = document.getElementById('send-code') as HTMLSpanElement
expect(sendCode).not.toBe(null)
})

await delay(2600)
expect(queryEffectFn).toBeCalledTimes(3)
expect(eventEffectFn).toBeCalledTimes(3)

await act(() => {
const backButton = document.getElementById('back') as HTMLButtonElement
backButton.dispatchEvent(new MouseEvent('click', { bubbles: true }))
})

await act(() => {
const sendCode = document.getElementById('send-code') as HTMLSpanElement
expect(sendCode).toBe(null)
})

await act(() => {
const conflictButton = document.getElementById('conflict') as HTMLButtonElement
conflictButton.dispatchEvent(new MouseEvent('click', { bubbles: true }))
})

await delay(2600)
expect(queryEffectFn).toBeCalledTimes(3)
expect(eventEffectFn).toBeCalledTimes(3)

await act(() => {
const sendCode = document.getElementById('send-code') as HTMLSpanElement
sendCode.dispatchEvent(new MouseEvent('click', { bubbles: true }))
})

await delay(200)
expect(triggerEffectFn).toBeCalledTimes(2)
expect(useRemeshEventFn).toBeCalledTimes(2)

await delay(2600)

await act(() => {
const backButton = document.getElementById('back') as HTMLButtonElement
backButton.dispatchEvent(new MouseEvent('click', { bubbles: true }))
})

await delay(2600)
expect(queryEffectFn).toBeCalledTimes(6)
expect(eventEffectFn).toBeCalledTimes(6)

expect(triggerEffectFn).toBeCalledTimes(2)
expect(useRemeshEventFn).toBeCalledTimes(2)
})
})
Loading

0 comments on commit 815dcd2

Please sign in to comment.