Skip to content

Commit 745b327

Browse files
committed
added tests for debounce
1 parent 921f705 commit 745b327

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

test/unit/pad.test.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { JSDOM } from 'jsdom'
33
import * as RdfLib from 'rdflib'
44
import { lightColorHash, notepad } from '../../src/pad'
55
import { log } from '../../src/debug'
6+
import ns from '../../src/ns'
7+
import { solidLogicSingleton } from 'solid-logic'
68

79
silenceDebugMessages()
810
const window = new JSDOM('<!DOCTYPE html><p>Hello world</p>').window
@@ -25,6 +27,39 @@ describe('lightColorHash', () => {
2527
})
2628

2729
describe('notepad', () => {
30+
const store: any = solidLogicSingleton.store
31+
const PAD = RdfLib.Namespace('http://www.w3.org/ns/pim/pad#')
32+
let originalUpdater: any
33+
34+
function setupExistingPadFixture () {
35+
const id = Date.now().toString() + Math.random().toString().slice(2)
36+
const padDoc = new RdfLib.NamedNode(`https://pad.example/${id}.ttl`)
37+
const subject = new RdfLib.NamedNode(`https://pad.example/${id}.ttl#pad`)
38+
const chunk = new RdfLib.NamedNode(`https://pad.example/${id}.ttl#line1`)
39+
const me = new RdfLib.NamedNode('https://sharonstrats.inrupt.net/profile/card#me')
40+
41+
store.add(subject, PAD('next'), chunk, padDoc)
42+
store.add(chunk, PAD('next'), subject, padDoc)
43+
store.add(chunk, ns.sioc('content'), 'initial', padDoc)
44+
store.add(chunk, ns.dc('author'), me, padDoc)
45+
46+
const table = notepad(dom, padDoc, subject, me, { exists: true })
47+
const part = table.querySelector('input') as any
48+
if (!part) {
49+
throw new Error('Expected notepad to render an input part')
50+
}
51+
return { padDoc, subject, chunk, me, part }
52+
}
53+
54+
beforeEach(() => {
55+
originalUpdater = store.updater
56+
})
57+
58+
afterEach(() => {
59+
store.updater = originalUpdater
60+
jest.useRealTimers()
61+
})
62+
2863
it('to be exposed by the Public API', () => {
2964
expect(notepad).toBe(notepad)
3065
})
@@ -75,4 +110,84 @@ describe('notepad', () => {
75110
expect(notepad(dom, padDoc, subject, me, options)
76111
).resolves.toBe({})
77112
})
113+
114+
it('debounces rapid input and sends one update after pause', () => {
115+
jest.useFakeTimers()
116+
117+
const update = jest.fn((_del, _ins, cb) => cb(null, true, '', { status: 200 }))
118+
store.updater = {
119+
update,
120+
requestDownstreamAction: jest.fn(),
121+
reload: jest.fn(),
122+
store
123+
}
124+
125+
const { padDoc, subject, chunk, part } = setupExistingPadFixture()
126+
127+
expect(() => {
128+
part.value = 'a'
129+
part.dispatchEvent(new window.Event('input', { bubbles: true }))
130+
part.value = 'ab'
131+
part.dispatchEvent(new window.Event('input', { bubbles: true }))
132+
part.value = 'abc'
133+
part.dispatchEvent(new window.Event('input', { bubbles: true }))
134+
}).not.toThrow()
135+
136+
expect(update).toHaveBeenCalledTimes(0)
137+
jest.advanceTimersByTime(399)
138+
expect(update).toHaveBeenCalledTimes(0)
139+
jest.advanceTimersByTime(1)
140+
expect(update).toHaveBeenCalledTimes(1)
141+
142+
// Cleanup this test fixture's statements.
143+
store.removeMatches(subject, null, null, padDoc)
144+
store.removeMatches(chunk, null, null, padDoc)
145+
store.removeMatches(null, null, chunk, padDoc)
146+
})
147+
148+
it('retries on transient 503 and keeps state/lastSent coherent', () => {
149+
jest.useFakeTimers()
150+
151+
let callCount = 0
152+
const update = jest.fn((_del, _ins, cb) => {
153+
callCount += 1
154+
if (callCount === 1) {
155+
cb(null, false, 'transient', { status: 503 })
156+
} else {
157+
cb(null, true, '', { status: 200 })
158+
}
159+
})
160+
161+
store.updater = {
162+
update,
163+
requestDownstreamAction: jest.fn(),
164+
reload: jest.fn(),
165+
store
166+
}
167+
168+
const { padDoc, subject, chunk, part } = setupExistingPadFixture()
169+
part.value = 'queued text'
170+
171+
expect(() => {
172+
part.dispatchEvent(new window.Event('input', { bubbles: true }))
173+
jest.advanceTimersByTime(400) // debounce fires first PATCH
174+
}).not.toThrow()
175+
176+
expect(update).toHaveBeenCalledTimes(1)
177+
expect(part.state).toBe(0)
178+
expect(part.lastSent).toBeUndefined()
179+
180+
jest.advanceTimersByTime(1999)
181+
expect(update).toHaveBeenCalledTimes(1)
182+
jest.advanceTimersByTime(1)
183+
184+
expect(update).toHaveBeenCalledTimes(2)
185+
expect(part.state).toBe(0)
186+
expect(part.lastSent).toBe('queued text')
187+
188+
// Cleanup this test fixture's statements.
189+
store.removeMatches(subject, null, null, padDoc)
190+
store.removeMatches(chunk, null, null, padDoc)
191+
store.removeMatches(null, null, chunk, padDoc)
192+
})
78193
})

0 commit comments

Comments
 (0)