-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrating Async/Await to Promise (#403)
* setItem migrated from asyn/await to promise * setItem is more cleaner now * rehydrate on initial state migrated to promise 🔥 * a tiny bug fixed in middleware * .size-snapshot.json update * don't invoke serialize when storage is undefined Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com> * a makeThenable() used to mix sync/async thens * toThenable() API finalized * size snapshots updated * Add tests for sync storage * Fix loading the state from localStorage * new snapshot * types ✨ added to `toThenable()` 🎉 🎉 * new size snapshot * Update src/middleware.ts Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com> * remaining parts also migrated to `toThenable()` * a clear explanation of sync storage workaround * fix some inconsistencies * some inconsistency fixed * useless IIFE removed * some bugs and types fixed * stateFromStorage renamed to stateFromStorageInSync * Wrap tests into describe() * Make sure that test for async persist() actually tests an async middleware * size snap shot updated * Correctly handle version discrepancies with missing migrate functions * Update src/middleware.ts Co-authored-by: Anatole Lucet <anatole@hey.com> * Fix test * chages addressed in reviews applied * chore: refactor * fix: setItem can throw error in sync Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com> Co-authored-by: Benjamin Arbogast <benjamin.arbogast@gmail.com> Co-authored-by: Anatole Lucet <anatole@hey.com> Co-authored-by: daishi <daishi@axlight.com>
- Loading branch information
1 parent
26aaf95
commit f62d448
Showing
4 changed files
with
299 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
import create from '../src/index' | ||
import { persist } from '../src/middleware' | ||
|
||
const consoleError = console.error | ||
afterEach(() => { | ||
console.error = consoleError | ||
}) | ||
|
||
describe('persist middleware with sync configuration', () => { | ||
it('can rehydrate state', () => { | ||
let postRehydrationCallbackCallCount = 0 | ||
|
||
const storage = { | ||
getItem: (name: string) => | ||
JSON.stringify({ | ||
state: { count: 42, name }, | ||
version: 0, | ||
}), | ||
setItem: () => {}, | ||
} | ||
|
||
const useStore = create( | ||
persist( | ||
() => ({ | ||
count: 0, | ||
name: 'empty', | ||
}), | ||
{ | ||
name: 'test-storage', | ||
getStorage: () => storage, | ||
onRehydrateStorage: () => (state, error) => { | ||
postRehydrationCallbackCallCount++ | ||
expect(error).toBeUndefined() | ||
expect(state?.count).toBe(42) | ||
expect(state?.name).toBe('test-storage') | ||
}, | ||
} | ||
) | ||
) | ||
|
||
expect(useStore.getState()).toEqual({ count: 42, name: 'test-storage' }) | ||
expect(postRehydrationCallbackCallCount).toBe(1) | ||
}) | ||
|
||
it('can throw rehydrate error', () => { | ||
let postRehydrationCallbackCallCount = 0 | ||
|
||
const storage = { | ||
getItem: () => { | ||
throw new Error('getItem error') | ||
}, | ||
setItem: () => {}, | ||
} | ||
|
||
create( | ||
persist(() => ({ count: 0 }), { | ||
name: 'test-storage', | ||
getStorage: () => storage, | ||
onRehydrateStorage: () => (_, e) => { | ||
postRehydrationCallbackCallCount++ | ||
expect(e?.message).toBe('getItem error') | ||
}, | ||
}) | ||
) | ||
|
||
expect(postRehydrationCallbackCallCount).toBe(1) | ||
}) | ||
|
||
it('can persist state', () => { | ||
let setItemCallCount = 0 | ||
|
||
const storage = { | ||
getItem: () => null, | ||
setItem: (name: string, value: string) => { | ||
setItemCallCount++ | ||
expect(name).toBe('test-storage') | ||
expect(value).toBe( | ||
JSON.stringify({ | ||
state: { count: 42 }, | ||
version: 0, | ||
}) | ||
) | ||
}, | ||
} | ||
|
||
const useStore = create( | ||
persist(() => ({ count: 0 }), { | ||
name: 'test-storage', | ||
getStorage: () => storage, | ||
onRehydrateStorage: () => (_, error) => { | ||
expect(error).toBeUndefined() | ||
}, | ||
}) | ||
) | ||
|
||
expect(useStore.getState()).toEqual({ count: 0 }) | ||
useStore.setState({ count: 42 }) | ||
expect(useStore.getState()).toEqual({ count: 42 }) | ||
expect(setItemCallCount).toBe(1) | ||
}) | ||
|
||
it('can migrate persisted state', () => { | ||
let migrateCallCount = 0 | ||
let setItemCallCount = 0 | ||
|
||
const storage = { | ||
getItem: () => | ||
JSON.stringify({ | ||
state: { count: 42 }, | ||
version: 12, | ||
}), | ||
setItem: (_: string, value: string) => { | ||
setItemCallCount++ | ||
expect(value).toBe( | ||
JSON.stringify({ | ||
state: { count: 99 }, | ||
version: 13, | ||
}) | ||
) | ||
}, | ||
} | ||
|
||
const useStore = create( | ||
persist(() => ({ count: 0 }), { | ||
name: 'test-storage', | ||
version: 13, | ||
getStorage: () => storage, | ||
onRehydrateStorage: () => (_, error) => { | ||
expect(error).toBeUndefined() | ||
}, | ||
migrate: (state, version) => { | ||
migrateCallCount++ | ||
expect(state.count).toBe(42) | ||
expect(version).toBe(12) | ||
return { count: 99 } | ||
}, | ||
}) | ||
) | ||
|
||
expect(useStore.getState()).toEqual({ count: 99 }) | ||
expect(migrateCallCount).toBe(1) | ||
expect(setItemCallCount).toBe(1) | ||
}) | ||
|
||
it.only('can correclty handle a missing migrate function', () => { | ||
console.error = jest.fn() | ||
const storage = { | ||
getItem: () => | ||
JSON.stringify({ | ||
state: { count: 42 }, | ||
version: 12, | ||
}), | ||
setItem: (_: string, value: string) => {}, | ||
} | ||
|
||
const useStore = create( | ||
persist(() => ({ count: 0 }), { | ||
name: 'test-storage', | ||
version: 13, | ||
getStorage: () => storage, | ||
onRehydrateStorage: () => (_, error) => { | ||
expect(error).toBeUndefined() | ||
}, | ||
}) | ||
) | ||
|
||
expect(useStore.getState()).toEqual({ count: 0 }) | ||
expect(console.error).toHaveBeenCalled() | ||
}) | ||
|
||
it('can throw migrate error', () => { | ||
let postRehydrationCallbackCallCount = 0 | ||
|
||
const storage = { | ||
getItem: () => | ||
JSON.stringify({ | ||
state: {}, | ||
version: 12, | ||
}), | ||
setItem: () => {}, | ||
} | ||
|
||
const useStore = create( | ||
persist(() => ({ count: 0 }), { | ||
name: 'test-storage', | ||
version: 13, | ||
getStorage: () => storage, | ||
migrate: () => { | ||
throw new Error('migrate error') | ||
}, | ||
onRehydrateStorage: () => (_, e) => { | ||
postRehydrationCallbackCallCount++ | ||
expect(e?.message).toBe('migrate error') | ||
}, | ||
}) | ||
) | ||
|
||
expect(useStore.getState()).toEqual({ count: 0 }) | ||
expect(postRehydrationCallbackCallCount).toBe(1) | ||
}) | ||
}) |