mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
Board browsing, catalog filtering, post publishing, and reply publishing had little or no honest whole-repo coverage, which let route-level regressions and duplicated form side effects slip through. This pass adds targeted tests for those user flows and fixes the duplicate `PostFormTable` mounting bug they exposed.
119 lines
3.7 KiB
TypeScript
119 lines
3.7 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 usePublishPost from '../use-publish-post';
|
|
import useChallengesStore from '../../stores/use-challenges-store';
|
|
import usePublishPostStore from '../../stores/use-publish-post-store';
|
|
|
|
(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>;
|
|
|
|
const testState = vi.hoisted(() => ({
|
|
abandonPublishMock: vi.fn(async () => undefined),
|
|
index: 12,
|
|
lastPublishOptions: undefined as Record<string, any> | undefined,
|
|
publishCommentMock: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('@bitsocialhq/bitsocial-react-hooks', () => ({
|
|
usePublishComment: (options: Record<string, any>) => {
|
|
testState.lastPublishOptions = options;
|
|
return {
|
|
abandonPublish: testState.abandonPublishMock,
|
|
index: testState.index,
|
|
publishComment: testState.publishCommentMock,
|
|
};
|
|
},
|
|
}));
|
|
|
|
let container: HTMLDivElement;
|
|
let latestValue: ReturnType<typeof usePublishPost>;
|
|
let root: Root;
|
|
|
|
const HookHarness = () => {
|
|
latestValue = usePublishPost({ subplebbitAddress: 'music.eth' });
|
|
return null;
|
|
};
|
|
|
|
const renderHook = () => {
|
|
act(() => {
|
|
root.render(createElement(HookHarness));
|
|
});
|
|
};
|
|
|
|
describe('usePublishPost', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
testState.index = 12;
|
|
testState.lastPublishOptions = undefined;
|
|
useChallengesStore.setState({ challenges: [] });
|
|
usePublishPostStore.getState().resetPublishPostStore();
|
|
|
|
container = document.createElement('div');
|
|
document.body.appendChild(container);
|
|
root = createRoot(container);
|
|
renderHook();
|
|
});
|
|
|
|
afterEach(() => {
|
|
act(() => root.unmount());
|
|
container.remove();
|
|
});
|
|
|
|
it('sanitizes publish options into portable store state and exposes the publish action', async () => {
|
|
await act(async () => {
|
|
latestValue.setPublishPostOptions({
|
|
author: { displayName: 'Alice' },
|
|
content: '',
|
|
link: '',
|
|
spoiler: true,
|
|
title: 'Hello world',
|
|
} as never);
|
|
});
|
|
|
|
expect(latestValue.postIndex).toBe(12);
|
|
expect(latestValue.publishPost).toBe(testState.publishCommentMock);
|
|
expect(latestValue.publishPostOptions).toMatchObject({
|
|
author: { displayName: 'Alice' },
|
|
content: undefined,
|
|
link: undefined,
|
|
spoiler: true,
|
|
subplebbitAddress: 'music.eth',
|
|
title: 'Hello world',
|
|
});
|
|
expect(typeof latestValue.publishPostOptions.onChallengeVerification).toBe('function');
|
|
expect(typeof latestValue.publishPostOptions.onError).toBe('function');
|
|
});
|
|
|
|
it('routes publish challenges through the challenge store and abandons the current publish when requested', async () => {
|
|
await act(async () => {
|
|
latestValue.setPublishPostOptions({
|
|
content: 'Thread body',
|
|
} as never);
|
|
});
|
|
|
|
expect(typeof testState.lastPublishOptions?.onChallenge).toBe('function');
|
|
|
|
await act(async () => {
|
|
await testState.lastPublishOptions?.onChallenge('captcha', 'nonce');
|
|
});
|
|
|
|
const challenges = useChallengesStore.getState().challenges;
|
|
expect(challenges).toHaveLength(1);
|
|
expect(challenges[0]?.challenge).toEqual(['captcha', 'nonce']);
|
|
|
|
await act(async () => {
|
|
await useChallengesStore.getState().abandonCurrentChallenge();
|
|
});
|
|
|
|
expect(testState.abandonPublishMock).toHaveBeenCalledTimes(1);
|
|
|
|
await act(async () => {
|
|
latestValue.resetPublishPostOptions();
|
|
});
|
|
|
|
expect(latestValue.publishPostOptions).toEqual({});
|
|
});
|
|
});
|