mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
210 lines
5.4 KiB
TypeScript
210 lines
5.4 KiB
TypeScript
import * as React from 'react';
|
|
import { createElement } from 'react';
|
|
import { createRoot, type Root } from 'react-dom/client';
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import useFreshReplies from '../use-fresh-replies';
|
|
|
|
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
|
|
|
type TestComment = {
|
|
cid?: string;
|
|
content?: string;
|
|
index?: number;
|
|
number?: number;
|
|
pendingApproval?: boolean;
|
|
communityAddress?: string;
|
|
};
|
|
|
|
const testState = vi.hoisted(() => ({
|
|
accountComments: [] as TestComment[],
|
|
accountCommentsCalls: [] as Array<{ commentIndices?: number[] } | undefined>,
|
|
replies: [] as TestComment[],
|
|
}));
|
|
|
|
vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
|
|
useAccountComments: (options?: { commentIndices?: number[] }) => {
|
|
testState.accountCommentsCalls.push(options);
|
|
|
|
if (!options?.commentIndices?.length) {
|
|
return {
|
|
accountComments: testState.accountComments,
|
|
};
|
|
}
|
|
|
|
const normalizedCommentIndices = options.commentIndices.filter((commentIndex) => Number.isInteger(commentIndex) && commentIndex >= 0);
|
|
|
|
return {
|
|
accountComments: normalizedCommentIndices
|
|
.map((commentIndex) => testState.accountComments.find((accountComment) => accountComment.index === commentIndex))
|
|
.filter(Boolean),
|
|
};
|
|
},
|
|
}));
|
|
|
|
let container: HTMLDivElement;
|
|
let latestValue: ReturnType<typeof useFreshReplies>;
|
|
let root: Root;
|
|
|
|
const HookHarness = () => {
|
|
latestValue = useFreshReplies(testState.replies as never);
|
|
return null;
|
|
};
|
|
|
|
const renderHook = () => {
|
|
act(() => {
|
|
root.render(createElement(HookHarness));
|
|
});
|
|
};
|
|
|
|
describe('useFreshReplies', () => {
|
|
beforeEach(() => {
|
|
testState.accountComments = [];
|
|
testState.accountCommentsCalls = [];
|
|
testState.replies = [];
|
|
|
|
container = document.createElement('div');
|
|
document.body.appendChild(container);
|
|
root = createRoot(container);
|
|
});
|
|
|
|
afterEach(() => {
|
|
act(() => root.unmount());
|
|
container.remove();
|
|
});
|
|
|
|
it('replaces stale indexed replies with the latest account comment objects', () => {
|
|
testState.replies = [
|
|
{
|
|
cid: 'reply-cid',
|
|
content: 'stale reply',
|
|
index: 3,
|
|
number: undefined,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
{
|
|
cid: 'network-reply-cid',
|
|
content: 'network reply',
|
|
communityAddress: 'music.eth',
|
|
},
|
|
];
|
|
testState.accountComments = [
|
|
{
|
|
cid: 'reply-cid',
|
|
content: 'fresh reply',
|
|
index: 3,
|
|
number: 27,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
];
|
|
|
|
renderHook();
|
|
|
|
expect(latestValue[0]).toBe(testState.accountComments[0] as never);
|
|
expect(latestValue[0]?.number).toBe(27);
|
|
expect(latestValue[1]).toBe(testState.replies[1] as never);
|
|
expect(testState.accountCommentsCalls).toContainEqual({ commentIndices: [3] });
|
|
|
|
testState.accountComments = [
|
|
{
|
|
...testState.accountComments[0],
|
|
number: 28,
|
|
},
|
|
];
|
|
|
|
renderHook();
|
|
|
|
expect(latestValue[0]?.number).toBe(28);
|
|
});
|
|
|
|
it('dedupes retried local replies when a stale reply and fresh reply share index 0', () => {
|
|
testState.replies = [
|
|
{
|
|
content: 'stale failed reply',
|
|
index: 0,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
{
|
|
content: 'duplicate stale failed reply',
|
|
index: 0,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
];
|
|
testState.accountComments = [
|
|
{
|
|
content: 'retried pending reply',
|
|
index: 0,
|
|
number: 99,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
];
|
|
|
|
renderHook();
|
|
|
|
expect(latestValue).toHaveLength(1);
|
|
expect(latestValue[0]).toBe(testState.accountComments[0] as never);
|
|
expect(testState.accountCommentsCalls).toContainEqual({ commentIndices: [0] });
|
|
});
|
|
|
|
it('orders replies by final post number after a pending reply is approved', () => {
|
|
testState.replies = [
|
|
{
|
|
cid: 'reply-8',
|
|
index: 8,
|
|
number: 8,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
{
|
|
cid: 'reply-12',
|
|
index: 12,
|
|
number: 12,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
{
|
|
cid: 'pending-reply',
|
|
index: 11,
|
|
number: undefined,
|
|
pendingApproval: true,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
];
|
|
testState.accountComments = [
|
|
{
|
|
cid: 'reply-11',
|
|
index: 11,
|
|
number: 11,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
];
|
|
|
|
renderHook();
|
|
|
|
expect(latestValue.map((reply) => reply.cid)).toEqual(['reply-8', 'reply-11', 'reply-12']);
|
|
expect(testState.accountCommentsCalls).toContainEqual({ commentIndices: [8, 12, 11] });
|
|
});
|
|
|
|
it('orders already-numbered replies when an approved queued reply was appended', () => {
|
|
testState.replies = [
|
|
{
|
|
cid: 'reply-8',
|
|
number: 8,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
{
|
|
cid: 'reply-12',
|
|
number: 12,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
{
|
|
cid: 'reply-11',
|
|
number: 11,
|
|
communityAddress: 'music.eth',
|
|
},
|
|
];
|
|
|
|
renderHook();
|
|
|
|
expect(latestValue.map((reply) => reply.cid)).toEqual(['reply-8', 'reply-11', 'reply-12']);
|
|
});
|
|
});
|