mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(quotes): resolve external quote links across boards (#1064)
This commit is contained in:
@@ -11,13 +11,23 @@ import usePublishReplyStore from '../../stores/use-publish-reply-store';
|
||||
const act = (React as { act?: (cb: () => void | Promise<void>) => void | Promise<void> }).act as (cb: () => void | Promise<void>) => void | Promise<void>;
|
||||
|
||||
const testState = vi.hoisted(() => ({
|
||||
account: { id: 'account-1' } as Record<string, any>,
|
||||
abandonPublishMock: vi.fn(async () => undefined),
|
||||
directories: [] as Array<Record<string, unknown>>,
|
||||
index: 7,
|
||||
lastPublishOptions: undefined as Record<string, any> | undefined,
|
||||
publishCommentMock: vi.fn(),
|
||||
resolveExternalQuoteTargetMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string, options?: Record<string, unknown>) => (options ? `${key}:${JSON.stringify(options)}` : key),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
|
||||
useAccount: () => testState.account,
|
||||
usePublishComment: (options: Record<string, any>) => {
|
||||
testState.lastPublishOptions = options;
|
||||
return {
|
||||
@@ -28,6 +38,14 @@ vi.mock('@bitsocialnet/bitsocial-react-hooks', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../hooks/use-directories', () => ({
|
||||
useDirectories: () => testState.directories,
|
||||
}));
|
||||
|
||||
vi.mock('../../lib/utils/external-quote-resolver', () => ({
|
||||
resolveExternalQuoteTarget: (...args: any[]) => testState.resolveExternalQuoteTargetMock(...args),
|
||||
}));
|
||||
|
||||
let container: HTMLDivElement;
|
||||
let latestValue: ReturnType<typeof usePublishReply>;
|
||||
let root: Root;
|
||||
@@ -46,6 +64,8 @@ const renderHook = () => {
|
||||
describe('usePublishReply', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
testState.account = { id: 'account-1' };
|
||||
testState.directories = [];
|
||||
testState.index = 7;
|
||||
testState.lastPublishOptions = undefined;
|
||||
useChallengesStore.setState({ challenges: [] });
|
||||
@@ -81,7 +101,7 @@ describe('usePublishReply', () => {
|
||||
});
|
||||
|
||||
expect(latestValue.replyIndex).toBe(7);
|
||||
expect(latestValue.publishReply).toBe(testState.publishCommentMock);
|
||||
expect(typeof latestValue.publishReply).toBe('function');
|
||||
expect(testState.lastPublishOptions).toMatchObject({
|
||||
author: { displayName: 'Bob' },
|
||||
content: 'Replying to >>12',
|
||||
@@ -94,6 +114,66 @@ describe('usePublishReply', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves same-board external quote references before triggering publish', async () => {
|
||||
testState.resolveExternalQuoteTargetMock.mockResolvedValue({
|
||||
cid: 'external-cid',
|
||||
route: '/music/thread/external-cid',
|
||||
subplebbitAddress: 'music.eth',
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
latestValue.setPublishReplyOptions({
|
||||
content: 'Replying to >>44',
|
||||
} as never);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await latestValue.publishReply();
|
||||
await Promise.resolve();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
});
|
||||
|
||||
expect(testState.resolveExternalQuoteTargetMock).toHaveBeenCalledTimes(1);
|
||||
expect(testState.lastPublishOptions?.quotedCids).toEqual(['external-cid']);
|
||||
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not resolve cross-board numeric quotes before publish', async () => {
|
||||
await act(async () => {
|
||||
latestValue.setPublishReplyOptions({
|
||||
content: 'Replying to >>>/fit/44',
|
||||
} as never);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await latestValue.publishReply();
|
||||
await Promise.resolve();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
});
|
||||
|
||||
expect(testState.resolveExternalQuoteTargetMock).not.toHaveBeenCalled();
|
||||
expect(testState.lastPublishOptions?.quotedCids).toBeUndefined();
|
||||
expect(testState.publishCommentMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('blocks publish when a same-board external quote cannot be resolved', async () => {
|
||||
testState.resolveExternalQuoteTargetMock.mockResolvedValue(null);
|
||||
|
||||
await act(async () => {
|
||||
latestValue.setPublishReplyOptions({
|
||||
content: 'Replying to >>44',
|
||||
} as never);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await latestValue.publishReply();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(latestValue.publishReplyError).toContain('external_quote_publish_missing');
|
||||
expect(testState.publishCommentMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('queues reply challenges and clears the scoped reply store on reset', async () => {
|
||||
await act(async () => {
|
||||
latestValue.setPublishReplyOptions({
|
||||
|
||||
Reference in New Issue
Block a user