-
-
Notifications
You must be signed in to change notification settings - Fork 114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: migrate to vitest #948
base: v3
Are you sure you want to change the base?
Changes from 29 commits
cdb34e3
2556fa9
c176cad
2342cd7
18bddb2
fc7e8dd
875c494
b7498a7
96d54f7
a58ca8a
420d6f3
e779c67
4038179
5518edb
93b9c21
951f726
9a8f674
de29681
3209ab9
85c35b0
9feed0d
a9614c9
f090394
39b0f69
05e93a9
4fdd4d5
6c9c88e
b6175b5
ecad5c4
7f5ae68
c268243
01dfa9c
201b184
6e3b4cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
import { test } from 'uvu' | ||
import * as assert from 'uvu/assert' | ||
import { describe, it, expect } from 'vitest' | ||
import { createTestCtx } from '@reatom/testing' | ||
import { atom } from '@reatom/core' | ||
import { onConnect } from '@reatom/hooks' | ||
|
@@ -13,9 +12,9 @@ describe('optimistic update', () => { | |
We want to update the list immediately, but we want to rollback the update | ||
if the server returns an error. | ||
|
||
Also, we use `onConnect` to fetch the list from the server every 5 seconds | ||
and we don't want to call subscriptions extra times so we use `isDeepEqual` | ||
in `withDataAtom` to prevent new reference stream if nothing really changed. | ||
Also, we use onConnect to fetch the list from the server every 5 seconds | ||
and we don't want to call subscriptions extra times so we use isDeepEqual | ||
in withDataAtom to prevent new reference stream if nothing really changed. | ||
*/ | ||
|
||
//#region BACKEND IMITATION | ||
|
@@ -30,25 +29,16 @@ describe('optimistic update', () => { | |
} | ||
//#endregion | ||
|
||
// this is short for test purposes, use ~5000 in real code | ||
const INTERVAL = 5 | ||
|
||
const getData = reatomAsync.from(api.getData).pipe( | ||
// add `dataAtom` and map the effect payload into it | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the comments removed? |
||
// try to prevent new reference stream if nothing really changed | ||
withDataAtom([], (ctx, payload, state) => | ||
isDeepEqual(payload, state) ? state : payload, | ||
), | ||
) | ||
const getData = reatomAsync | ||
.from(api.getData) | ||
.pipe(withDataAtom([], (ctx, payload, state) => (isDeepEqual(payload, state) ? state : payload))) | ||
const putData = reatomAsync.from(api.putData) | ||
putData.onCall((ctx, promise, params) => { | ||
const [id, value] = params | ||
const oldList = ctx.get(getData.dataAtom) | ||
// optimistic update | ||
const newList = getData.dataAtom(ctx, (state) => | ||
state.map((item) => (item.id === id ? { ...item, value } : item)), | ||
) | ||
// rollback on error | ||
const newList = getData.dataAtom(ctx, (state) => state.map((item) => (item.id === id ? { ...item, value } : item))) | ||
promise.catch((error) => { | ||
if (ctx.get(getData.dataAtom) === newList) { | ||
getData.dataAtom(ctx, oldList) | ||
|
@@ -67,36 +57,29 @@ describe('optimistic update', () => { | |
} | ||
}) | ||
|
||
test('optimistic update', async () => { | ||
it('optimistic update', async () => { | ||
const ctx = createTestCtx() | ||
const effectTrack = ctx.subscribeTrack(getData.onFulfill) | ||
const dataTrack = ctx.subscribeTrack(getData.dataAtom) | ||
|
||
// every subscription calls passed callback immediately | ||
assert.is(effectTrack.calls.length, 1) | ||
assert.is(dataTrack.calls.length, 1) | ||
assert.equal(dataTrack.lastInput(), []) | ||
expect(effectTrack.calls.length).toBe(1) | ||
expect(dataTrack.calls.length).toBe(1) | ||
expect(dataTrack.lastInput()).toEqual([]) | ||
|
||
// `onConnect` calls `fetchData`, wait it and check changes | ||
await sleep() | ||
assert.is(dataTrack.calls.length, 2) | ||
assert.equal(dataTrack.lastInput(), [{ id: 1, value: 1 }]) | ||
expect(dataTrack.calls.length).toBe(2) | ||
expect(dataTrack.lastInput()).toEqual([{ id: 1, value: 1 }]) | ||
|
||
// call `updateData` and check changes | ||
putData(ctx, 1, 2) | ||
assert.is(dataTrack.calls.length, 3) | ||
assert.equal(dataTrack.lastInput(), [{ id: 1, value: 2 }]) | ||
expect(dataTrack.calls.length).toBe(3) | ||
expect(dataTrack.lastInput()).toEqual([{ id: 1, value: 2 }]) | ||
|
||
// wait for `fetchData` and check changes | ||
assert.is(effectTrack.calls.length, 2) | ||
expect(effectTrack.calls.length).toBe(2) | ||
await sleep(INTERVAL) | ||
// the effect is called again, but dataAtom is not updated | ||
assert.is(effectTrack.calls.length, 3) | ||
assert.is(dataTrack.calls.length, 3) | ||
expect(effectTrack.calls.length).toBe(3) | ||
expect(dataTrack.calls.length).toBe(3) | ||
|
||
// cleanup test | ||
dataTrack.unsubscribe() | ||
;`👍` //? | ||
}) | ||
}) | ||
|
||
|
@@ -106,7 +89,7 @@ describe('concurrent pooling', () => { | |
every 5 seconds. We want to abort the previous pooling if the new one | ||
was started. The problem with the most tooling for async management is that no causes tracking | ||
and we can't abort some step of the previous pooling if the new one was started. | ||
Reatom handle it perfectly, because `ctx` is immutable and could be traced when needed. | ||
Reatom handle it perfectly, because ctx is immutable and could be traced when needed. | ||
*/ | ||
|
||
//#region BACKEND IMITATION | ||
|
@@ -119,7 +102,6 @@ describe('concurrent pooling', () => { | |
await sleep(5) | ||
const progress = (tasks.get(taskId) ?? -10) + 10 | ||
tasks.set(taskId, progress) | ||
|
||
return progress | ||
}, | ||
} | ||
|
@@ -141,7 +123,7 @@ describe('concurrent pooling', () => { | |
} | ||
}).pipe(withAbort({ strategy: 'last-in-win' })) | ||
|
||
test('concurrent pooling', async () => { | ||
it('concurrent pooling', async () => { | ||
const ctx = createTestCtx() | ||
const track = ctx.subscribeTrack(progressAtom) | ||
|
||
|
@@ -151,20 +133,10 @@ describe('concurrent pooling', () => { | |
|
||
await Promise.allSettled([promise1, promise2]) | ||
|
||
assert.is(ctx.get(progressAtom), 100) | ||
expect(ctx.get(progressAtom)).toBe(100) | ||
|
||
const expectedProgress = [ | ||
0, 10, /* start again */ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, | ||
] | ||
const expectedProgress = [0, 10, /* start again */ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100] | ||
|
||
// assert.equal(track.inputs(), expectedProgress) | ||
;`👍` //? | ||
// expect(track.inputs()).toEqual(expectedProgress) | ||
}) | ||
}) | ||
|
||
test.run() | ||
|
||
// uvu have no own describe | ||
function describe(name: string, fn: () => any) { | ||
fn() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,52 @@ | ||
import { suite } from 'uvu' | ||
import * as assert from 'uvu/assert' | ||
import { describe, it, expect } from 'vitest' | ||
import { take, takeNested } from '@reatom/effects' | ||
import { createTestCtx } from '@reatom/testing' | ||
import { atom } from '@reatom/core' | ||
import { mapToAsync, withDataAtom } from './index' | ||
|
||
export const test = suite('mapToAsync') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no need to use "test": "vitest run src/*.test.ts",
"test:watch": "vitest src/*.test.ts" |
||
describe('mapToAsync', () => { | ||
it(`mapToAsync interface`, () => { | ||
const argumentAtom = atom(0, 'argumentAtom') | ||
const asyncAction = argumentAtom.pipe(mapToAsync(async (ctx, arg) => arg)) | ||
|
||
test(`mapToAsync interface`, () => { | ||
const argumentAtom = atom(0, 'argumentAtom') | ||
const asyncAction = argumentAtom.pipe(mapToAsync(async (ctx, arg) => arg)) | ||
expect(typeof asyncAction).toBe('function') | ||
expect(asyncAction.__reatom.name).toBe('argumentAtom.mapToAsync') | ||
expect(typeof asyncAction.unstable_unhook).toBe('function') | ||
;`👍` //? | ||
}) | ||
|
||
assert.type(asyncAction, 'function') | ||
assert.is(asyncAction.__reatom.name, 'argumentAtom.mapToAsync') | ||
assert.type(asyncAction.unstable_unhook, 'function') | ||
;`👍` //? | ||
}) | ||
|
||
test(`is called whenever argument is changed`, async () => { | ||
const argumentAtom = atom('initial', 'argumentAtom') | ||
const asyncAction = argumentAtom.pipe( | ||
mapToAsync(async (ctx, arg) => arg), | ||
withDataAtom('default'), | ||
) | ||
const ctx = createTestCtx() | ||
it(`is called whenever argument is changed`, async () => { | ||
const argumentAtom = atom('initial', 'argumentAtom') | ||
const asyncAction = argumentAtom.pipe( | ||
mapToAsync(async (ctx, arg) => arg), | ||
withDataAtom('default'), | ||
) | ||
const ctx = createTestCtx() | ||
|
||
assert.is(ctx.get(asyncAction.dataAtom), 'default') | ||
expect(ctx.get(asyncAction.dataAtom)).toBe('default') | ||
|
||
const hijackedCall = take(ctx, asyncAction) | ||
const hijackedCall = take(ctx, asyncAction) | ||
|
||
argumentAtom(ctx, 'updated') | ||
argumentAtom(ctx, 'updated') | ||
|
||
assert.is(await hijackedCall, 'updated') | ||
assert.is(ctx.get(asyncAction.dataAtom), 'updated') | ||
;`👍` //? | ||
}) | ||
expect(await hijackedCall).toBe('updated') | ||
expect(ctx.get(asyncAction.dataAtom)).toBe('updated') | ||
;`👍` //? | ||
}) | ||
|
||
test(`can be unhooked`, async () => { | ||
const argumentAtom = atom('initial', 'argumentAtom') | ||
const asyncAction = argumentAtom.pipe( | ||
mapToAsync(async (ctx, n) => n), | ||
withDataAtom('default'), | ||
) | ||
it(`can be unhooked`, async () => { | ||
const argumentAtom = atom('initial', 'argumentAtom') | ||
const asyncAction = argumentAtom.pipe( | ||
mapToAsync(async (ctx, n) => n), | ||
withDataAtom('default'), | ||
) | ||
|
||
asyncAction.unstable_unhook() | ||
asyncAction.unstable_unhook() | ||
|
||
const ctx = createTestCtx() | ||
const ctx = createTestCtx() | ||
|
||
await takeNested(ctx, argumentAtom, 'updated') | ||
assert.is(ctx.get(asyncAction.dataAtom), 'default') | ||
;`👍` //? | ||
await takeNested(ctx, argumentAtom, 'updated') | ||
expect(ctx.get(asyncAction.dataAtom)).toBe('default') | ||
;`👍` //? | ||
}) | ||
}) | ||
|
||
test.run() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the quotes removed?