mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
test: cover comment rendering and edge-route states
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
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 NotFound from '../not-found';
|
||||
|
||||
(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(() => ({
|
||||
directories: [{ address: 'music-posting.eth', title: '/mu/ - Music' }] as Array<{ address: string; title?: string }>,
|
||||
location: {
|
||||
pathname: '/mu/thread/missing-post',
|
||||
},
|
||||
resolvedAddress: 'music-posting.eth',
|
||||
shortAddress: 'mu',
|
||||
subplebbitAddress: 'music-posting.eth',
|
||||
}));
|
||||
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const actual = await vi.importActual<typeof import('react-router-dom')>('react-router-dom');
|
||||
return {
|
||||
...actual,
|
||||
Link: ({ children, to }: { children: React.ReactNode; to: string }) => createElement('a', { href: to }, children),
|
||||
useLocation: () => testState.location,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../../../hooks/use-stable-subplebbit', () => ({
|
||||
useSubplebbitField: (_address: string, selector: (subplebbit: { address?: string; shortAddress?: string }) => string | undefined) =>
|
||||
selector({
|
||||
address: testState.resolvedAddress,
|
||||
shortAddress: testState.shortAddress,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/use-directories', () => ({
|
||||
useDirectories: () => testState.directories,
|
||||
}));
|
||||
|
||||
vi.mock('../../../lib/utils/route-utils', () => ({
|
||||
getSubplebbitAddress: () => testState.subplebbitAddress,
|
||||
}));
|
||||
|
||||
vi.mock('../../home', () => ({
|
||||
HomeLogo: () => createElement('div', { 'data-testid': 'home-logo' }, 'home-logo'),
|
||||
}));
|
||||
|
||||
vi.mock('../../../components/not-found-image', () => ({
|
||||
default: () => createElement('div', { 'data-testid': 'not-found-image' }, 'image'),
|
||||
}));
|
||||
|
||||
let container: HTMLDivElement;
|
||||
let root: Root;
|
||||
|
||||
const renderNotFound = async () => {
|
||||
await act(async () => {
|
||||
root.render(createElement(NotFound));
|
||||
});
|
||||
};
|
||||
|
||||
describe('NotFound', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
testState.directories = [{ address: 'music-posting.eth', title: '/mu/ - Music' }];
|
||||
testState.location = {
|
||||
pathname: '/mu/thread/missing-post',
|
||||
};
|
||||
testState.resolvedAddress = 'music-posting.eth';
|
||||
testState.shortAddress = 'mu';
|
||||
testState.subplebbitAddress = 'music-posting.eth';
|
||||
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => root.unmount());
|
||||
container.remove();
|
||||
});
|
||||
|
||||
it('renders the board-specific back link when the missing route belongs to a known board', async () => {
|
||||
await renderNotFound();
|
||||
|
||||
expect(container.querySelector('[data-testid="home-logo"]')).toBeTruthy();
|
||||
expect(container.querySelector('[data-testid="not-found-image"]')).toBeTruthy();
|
||||
expect(container.textContent).toContain('Not Found');
|
||||
expect(container.textContent).toContain('Back to p/mu');
|
||||
expect(container.querySelector('a')?.getAttribute('href')).toBe('/mu');
|
||||
});
|
||||
|
||||
it('omits the board link for generic not-found routes', async () => {
|
||||
testState.location = {
|
||||
pathname: '/not-found',
|
||||
};
|
||||
testState.resolvedAddress = '';
|
||||
testState.shortAddress = '';
|
||||
testState.subplebbitAddress = '';
|
||||
|
||||
await renderNotFound();
|
||||
|
||||
expect(container.textContent).not.toContain('Back to p/');
|
||||
expect(container.querySelector('a')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,135 @@
|
||||
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 PendingPost from '../pending-post';
|
||||
|
||||
(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;
|
||||
subplebbitAddress?: string;
|
||||
};
|
||||
|
||||
const testState = vi.hoisted(() => ({
|
||||
accountCommentIndex: undefined as string | undefined,
|
||||
accountComments: [] as TestComment[],
|
||||
directories: [] as Array<{ address: string; title?: string }>,
|
||||
getBoardPathMock: vi.fn<(address: string) => string>(),
|
||||
navigateMock: vi.fn(),
|
||||
post: undefined as TestComment | undefined,
|
||||
}));
|
||||
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const actual = await vi.importActual<typeof import('react-router-dom')>('react-router-dom');
|
||||
return {
|
||||
...actual,
|
||||
useNavigate: () => testState.navigateMock,
|
||||
useParams: () => ({
|
||||
accountCommentIndex: testState.accountCommentIndex,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@bitsocialhq/bitsocial-react-hooks', () => ({
|
||||
useAccountComment: () => testState.post,
|
||||
useAccountComments: () => ({
|
||||
accountComments: testState.accountComments,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/use-directories', () => ({
|
||||
useDirectories: () => testState.directories,
|
||||
}));
|
||||
|
||||
vi.mock('../../../lib/utils/route-utils', () => ({
|
||||
getBoardPath: (address: string) => testState.getBoardPathMock(address),
|
||||
}));
|
||||
|
||||
vi.mock('../../post', () => ({
|
||||
Post: ({ post }: { post?: TestComment }) => createElement('div', { 'data-testid': 'post-view' }, post?.cid ?? 'no-post'),
|
||||
}));
|
||||
|
||||
let container: HTMLDivElement;
|
||||
let root: Root;
|
||||
const scrollToMock = vi.fn();
|
||||
const originalScrollTo = window.scrollTo;
|
||||
|
||||
const flushEffects = async (count = 4) => {
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const renderPendingPost = async () => {
|
||||
await act(async () => {
|
||||
root.render(createElement(PendingPost));
|
||||
});
|
||||
await flushEffects();
|
||||
};
|
||||
|
||||
describe('PendingPost', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
testState.accountCommentIndex = undefined;
|
||||
testState.accountComments = [];
|
||||
testState.directories = [];
|
||||
testState.getBoardPathMock.mockReset();
|
||||
testState.navigateMock.mockReset();
|
||||
testState.post = undefined;
|
||||
|
||||
window.scrollTo = scrollToMock;
|
||||
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
root = createRoot(container);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => root.unmount());
|
||||
container.remove();
|
||||
window.scrollTo = originalScrollTo;
|
||||
});
|
||||
|
||||
it('renders the pending post and scrolls to the top for valid indices', async () => {
|
||||
testState.accountCommentIndex = '0';
|
||||
testState.accountComments = [{}];
|
||||
testState.post = {
|
||||
subplebbitAddress: 'music-posting.eth',
|
||||
};
|
||||
|
||||
await renderPendingPost();
|
||||
|
||||
expect(scrollToMock).toHaveBeenCalledWith(0, 0);
|
||||
expect(container.querySelector('[data-testid="post-view"]')?.textContent).toBe('no-post');
|
||||
expect(testState.navigateMock).not.toHaveBeenCalledWith('/not-found', { replace: true });
|
||||
});
|
||||
|
||||
it('redirects invalid pending indices to not found', async () => {
|
||||
testState.accountCommentIndex = '-1';
|
||||
testState.accountComments = [{}, {}];
|
||||
|
||||
await renderPendingPost();
|
||||
|
||||
expect(testState.navigateMock).toHaveBeenCalledWith('/not-found', { replace: true });
|
||||
});
|
||||
|
||||
it('redirects resolved pending posts to the canonical thread route', async () => {
|
||||
testState.accountCommentIndex = '1';
|
||||
testState.accountComments = [{}, {}];
|
||||
testState.directories = [{ address: 'music-posting.eth', title: '/mu/ - Music' }];
|
||||
testState.getBoardPathMock.mockReturnValue('mu');
|
||||
testState.post = {
|
||||
cid: 'post-cid',
|
||||
subplebbitAddress: 'music-posting.eth',
|
||||
};
|
||||
|
||||
await renderPendingPost();
|
||||
|
||||
expect(testState.getBoardPathMock).toHaveBeenCalledWith('music-posting.eth');
|
||||
expect(testState.navigateMock).toHaveBeenCalledWith('/mu/thread/post-cid', { replace: true });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user