| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- import sinon from 'sinon'
- import { expect } from 'chai'
- import { useEffect } from 'react'
- import { render, screen } from '@testing-library/react'
- import usePersistedState from '../../../../frontend/js/shared/hooks/use-persisted-state'
- import localStorage from '@/infrastructure/local-storage'
- describe('usePersistedState', function () {
- beforeEach(function () {
- sinon.spy(window.Storage.prototype, 'getItem')
- sinon.spy(window.Storage.prototype, 'removeItem')
- sinon.spy(window.Storage.prototype, 'setItem')
- })
- afterEach(function () {
- sinon.restore()
- })
- it('reads the value from localStorage', function () {
- const key = 'test'
- localStorage.setItem(key, 'foo')
- expect(window.Storage.prototype.setItem).to.have.callCount(1)
- const Test = () => {
- const [value] = usePersistedState(key)
- return <div>{value}</div>
- }
- render(<Test />)
- screen.getByText('foo')
- expect(window.Storage.prototype.getItem).to.have.callCount(1)
- expect(window.Storage.prototype.removeItem).to.have.callCount(0)
- expect(window.Storage.prototype.setItem).to.have.callCount(1)
- expect(localStorage.getItem(key)).to.equal('foo')
- })
- it('uses the default value without storing anything', function () {
- const key = 'test:default'
- const Test = () => {
- const [value] = usePersistedState(key, 'foo')
- return <div>{value}</div>
- }
- render(<Test />)
- screen.getByText('foo')
- expect(window.Storage.prototype.getItem).to.have.callCount(1)
- expect(window.Storage.prototype.removeItem).to.have.callCount(0)
- expect(window.Storage.prototype.setItem).to.have.callCount(0)
- expect(localStorage.getItem(key)).to.be.null
- })
- it('stores the new value in localStorage', function () {
- const key = 'test:store'
- localStorage.setItem(key, 'foo')
- expect(window.Storage.prototype.setItem).to.have.callCount(1)
- const Test = () => {
- const [value, setValue] = usePersistedState(key, 'bar')
- useEffect(() => {
- setValue('baz')
- }, [setValue])
- return <div>{value}</div>
- }
- render(<Test />)
- screen.getByText('baz')
- expect(window.Storage.prototype.getItem).to.have.callCount(1)
- expect(window.Storage.prototype.removeItem).to.have.callCount(0)
- expect(window.Storage.prototype.setItem).to.have.callCount(2)
- expect(localStorage.getItem(key)).to.equal('baz')
- })
- it('removes the value from localStorage if it equals the default value', function () {
- const key = 'test:store-default'
- localStorage.setItem(key, 'foo')
- expect(window.Storage.prototype.setItem).to.have.callCount(1)
- const Test = () => {
- const [value, setValue] = usePersistedState(key, 'bar')
- useEffect(() => {
- // set a different value
- setValue('baz')
- expect(localStorage.getItem(key)).to.equal('baz')
- // set the default value again
- setValue('bar')
- }, [setValue])
- return <div>{value}</div>
- }
- render(<Test />)
- screen.getByText('bar')
- expect(window.Storage.prototype.getItem).to.have.callCount(2)
- expect(window.Storage.prototype.removeItem).to.have.callCount(1)
- expect(window.Storage.prototype.setItem).to.have.callCount(2)
- expect(localStorage.getItem(key)).to.be.null
- })
- it('handles function values', function () {
- const key = 'test:store'
- localStorage.setItem(key, 'foo')
- expect(window.Storage.prototype.setItem).to.have.callCount(1)
- const Test = () => {
- const [value, setValue] = usePersistedState<string>(key)
- useEffect(() => {
- setValue(value => value + 'bar')
- }, [setValue])
- return <div>{value}</div>
- }
- render(<Test />)
- screen.getByText('foobar')
- expect(window.Storage.prototype.getItem).to.have.callCount(1)
- expect(window.Storage.prototype.removeItem).to.have.callCount(0)
- expect(window.Storage.prototype.setItem).to.have.callCount(2)
- expect(localStorage.getItem(key)).to.equal('foobar')
- })
- it('handles syncing values via storage event', async function () {
- const key = 'test:sync'
- localStorage.setItem(key, 'foo')
- expect(window.Storage.prototype.setItem).to.have.callCount(1)
- // listen for storage events
- const storageEventListener = sinon.stub()
- window.addEventListener('storage', storageEventListener)
- const Test = () => {
- const [value, setValue] = usePersistedState(key, 'bar', true)
- useEffect(() => {
- setValue('baz')
- }, [setValue])
- return <div>{value}</div>
- }
- render(<Test />)
- screen.getByText('baz')
- expect(window.Storage.prototype.getItem).to.have.callCount(1)
- expect(window.Storage.prototype.removeItem).to.have.callCount(0)
- expect(window.Storage.prototype.setItem).to.have.callCount(2)
- expect(localStorage.getItem(key)).to.equal('baz')
- expect(storageEventListener).to.have.callCount(0)
- // set the new value in localStorage
- localStorage.setItem(key, 'cat')
- // dispatch a "storage" event and check that it's picked up by the hook
- window.dispatchEvent(new StorageEvent('storage', { key }))
- await screen.findByText('cat')
- expect(storageEventListener).to.have.callCount(1)
- })
- })
|