mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo-client.git
synced 2026-09-21 16:52:02 -07:00
Extract optimistic reply shaping into testable module
Move the optimistic reply object construction out of the ReplyThreadForm React component into a testable service module with unit tests, keeping the component a thin UI adapter. Behavior is preserved; all unit, property, and acceptance tests pass. By refactorer.
This commit is contained in:
@@ -15,6 +15,7 @@ import { Form, Button } from 'react-bootstrap'
|
||||
import MemoReply from '../../services/memo-reply'
|
||||
import ReplyThreadPage from '../../services/reply-thread-page'
|
||||
import { byteLength } from '../../services/utf8'
|
||||
import { buildOptimisticReply } from '../../services/optimistic-reply'
|
||||
|
||||
function ReplyThreadForm ({ parentTxid, rootPost, wallet, profiles, onOptimisticReply }) {
|
||||
const maxBytes = MemoReply.MAX_REPLY_BYTES
|
||||
@@ -42,16 +43,14 @@ function ReplyThreadForm ({ parentTxid, rootPost, wallet, profiles, onOptimistic
|
||||
if (typeof onOptimisticReply === 'function') {
|
||||
const cashAddress = wallet?.walletInfo?.cashAddress
|
||||
const displayName = profiles?.[cashAddress]?.name || null
|
||||
onOptimisticReply({
|
||||
onOptimisticReply(buildOptimisticReply({
|
||||
txid: result.txid,
|
||||
addr: cashAddress,
|
||||
text: input,
|
||||
seen: Date.now(),
|
||||
blockHeight: rootPost?.blockHeight,
|
||||
replyCount: 0,
|
||||
replies: [],
|
||||
profile: displayName ? { name: displayName } : undefined
|
||||
})
|
||||
displayName
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
if (result.error === 'reply_length') {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
'use strict'
|
||||
|
||||
// Build the optimistic reply object used by the reply form for immediate
|
||||
// thread rendering before the thread is refreshed from the network. This is a
|
||||
// pure data-shaping function so the reply object shape is unit-testable and
|
||||
// the React form stays a thin adapter.
|
||||
function buildOptimisticReply ({ txid, addr, text, seen, blockHeight, displayName }) {
|
||||
return {
|
||||
txid,
|
||||
addr,
|
||||
text,
|
||||
seen,
|
||||
blockHeight,
|
||||
replyCount: 0,
|
||||
replies: [],
|
||||
profile: displayName ? { name: displayName } : undefined
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { buildOptimisticReply }
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Unit tests for the optimistic reply object builder
|
||||
(src/services/optimistic-reply.js).
|
||||
|
||||
The reply form renders an optimistic reply immediately after a successful
|
||||
broadcast, before the thread is refreshed from the network. This module
|
||||
shapes that reply object so the shape is covered by unit tests and the
|
||||
React form stays a thin adapter.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const test = require('node:test')
|
||||
const assert = require('node:assert/strict')
|
||||
|
||||
const { buildOptimisticReply } = require('../../src/services/optimistic-reply')
|
||||
|
||||
const TXID = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||
const ADDR = 'bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d'
|
||||
|
||||
test('builds a reply with a zero reply count and no child replies', () => {
|
||||
const reply = buildOptimisticReply({
|
||||
txid: TXID,
|
||||
addr: ADDR,
|
||||
text: 'hello',
|
||||
seen: 1234,
|
||||
blockHeight: 800000,
|
||||
displayName: null
|
||||
})
|
||||
|
||||
assert.equal(reply.txid, TXID)
|
||||
assert.equal(reply.addr, ADDR)
|
||||
assert.equal(reply.text, 'hello')
|
||||
assert.equal(reply.seen, 1234)
|
||||
assert.equal(reply.blockHeight, 800000)
|
||||
assert.equal(reply.replyCount, 0)
|
||||
assert.deepEqual(reply.replies, [])
|
||||
assert.equal(reply.profile, undefined)
|
||||
})
|
||||
|
||||
test('attaches a profile when a display name is present', () => {
|
||||
const reply = buildOptimisticReply({
|
||||
txid: TXID,
|
||||
addr: ADDR,
|
||||
text: 'hello',
|
||||
seen: 1234,
|
||||
blockHeight: undefined,
|
||||
displayName: 'Trout'
|
||||
})
|
||||
|
||||
assert.deepEqual(reply.profile, { name: 'Trout' })
|
||||
assert.equal(reply.blockHeight, undefined)
|
||||
})
|
||||
|
||||
test('preserves an absent block height', () => {
|
||||
const reply = buildOptimisticReply({
|
||||
txid: TXID,
|
||||
addr: ADDR,
|
||||
text: 'hello',
|
||||
seen: 1234,
|
||||
blockHeight: null,
|
||||
displayName: null
|
||||
})
|
||||
|
||||
assert.equal(reply.blockHeight, null)
|
||||
})
|
||||
Reference in New Issue
Block a user