test: add wiki editor route canonical-load test and feed edit-pencil visibility test; run prettier
All checks were successful
CI / build_test (push) Successful in 3m43s

This commit is contained in:
greenflame089
2025-08-22 01:37:50 -04:00
parent 490511a628
commit cd79cdf6ad
4 changed files with 167 additions and 4 deletions

View File

@@ -210,7 +210,9 @@ export const EditPost = () => {
const searchUrl = `/arbitrary/resources/search?mode=ALL&service=BLOG_POST&identifier=${encodeURIComponent(
fullId,
)}&limit=50&includemetadata=true&reverse=true&excludeblocked=true`;
const searchResp = await fetch(searchUrl, { headers: { 'Content-Type': 'application/json' } });
const searchResp = await fetch(searchUrl, {
headers: { 'Content-Type': 'application/json' },
});
const items = (await searchResp.json()) || [];
const revisions = items.map((it: any) => ({
id: it.identifier,

View File

@@ -89,7 +89,12 @@ export const UserBlogs: React.FC = () => {
<>
{isOwner && (
<Box sx={{ textAlign: 'right', mb: 1 }}>
<Button variant="contained" color="primary" onClick={onCreate} aria-label="Create Blog">
<Button
variant="contained"
color="primary"
onClick={onCreate}
aria-label="Create Blog"
>
Create Blog
</Button>
</Box>
@@ -100,11 +105,23 @@ export const UserBlogs: React.FC = () => {
<ListItem
secondaryAction={
<Box sx={{ display: 'flex', gap: 1 }}>
<Button size="small" color="primary" variant="outlined" onClick={() => onView(b)} aria-label={`View blog ${b.title || b.handle}`}>
<Button
size="small"
color="primary"
variant="outlined"
onClick={() => onView(b)}
aria-label={`View blog ${b.title || b.handle}`}
>
View
</Button>
{isOwner && (
<Button size="small" color="primary" variant="contained" onClick={() => onEdit(b)} aria-label={`Edit blog ${b.title || b.handle}`}>
<Button
size="small"
color="primary"
variant="contained"
onClick={() => onEdit(b)}
aria-label={`Edit blog ${b.title || b.handle}`}
>
Edit
</Button>
)}

View File

@@ -0,0 +1,67 @@
import React from 'react';
import { describe, it, expect } from 'vitest';
import { Provider } from 'react-redux';
import { store } from '@/state/store';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { MemoryRouter } from 'react-router-dom';
import { render, waitFor } from '@testing-library/react';
import { http, HttpResponse } from 'msw';
import { server } from '../msw/server';
import { BlogList } from '@/pages/BlogList/BlogList';
import { addPosts } from '@/state/features/blogSlice';
import { addUser } from '@/state/features/authSlice';
describe('BlogList — wiki edit pencil visibility', () => {
it('shows edit pencil for authorized non-owner editor', async () => {
// Configure auth user as bob
store.dispatch(addUser({ address: 'A', publicKey: 'P', name: 'bob' }));
// Seed one post from another user
store.dispatch(
addPosts([
{
id: 'q-blog-myblog-post-123',
user: 'carol',
title: 'T',
description: 'd',
createdAt: 1,
} as any,
]),
);
// Owner + settings (whitelist bob)
server.use(
http.get('/arbitrary/resources', ({ request }) => {
const url = new URL(request.url);
if (
url.searchParams.get('service') === 'BLOG' &&
url.searchParams.get('identifier') === 'q-blog-myblog'
) {
return HttpResponse.json([{ name: 'alice', identifier: 'q-blog-myblog' }]);
}
return HttpResponse.json([]);
}),
http.get('/arbitrary/BLOG/:name/:id', ({ params }) => {
const { name, id } = params as any;
if (name === 'alice' && id === 'q-blog-myblog') {
return HttpResponse.json({ wikiEnabled: true, editorWhitelist: ['bob'] });
}
return HttpResponse.json({}, { status: 404 });
}),
);
const { container } = render(
<Provider store={store}>
<ThemeProvider theme={createTheme()}>
<MemoryRouter>
<BlogList />
</MemoryRouter>
</ThemeProvider>
</Provider>,
);
await waitFor(() => {
const pencil = container.querySelector('.edit-btn');
expect(pencil).toBeTruthy();
});
});
});

View File

@@ -0,0 +1,77 @@
import React from 'react';
import { describe, it, expect } from 'vitest';
import { Provider } from 'react-redux';
import { store } from '@/state/store';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { render, screen, waitFor } from '@testing-library/react';
import { http, HttpResponse } from 'msw';
import { server } from '../msw/server';
import { EditPost } from '@/pages/EditPost/EditPost';
describe('EditPost — wiki canonical loading', () => {
it('loads canonical content by identifier across names', async () => {
// BLOG owner + settings
server.use(
http.get('/arbitrary/resources', ({ request }) => {
const url = new URL(request.url);
if (
url.searchParams.get('service') === 'BLOG' &&
url.searchParams.get('identifier') === 'q-blog-myblog'
) {
return HttpResponse.json([{ name: 'alice', identifier: 'q-blog-myblog' }]);
}
return HttpResponse.json([]);
}),
http.get('/arbitrary/BLOG/:name/:id', ({ params }) => {
const { name, id } = params as any;
if (name === 'alice' && id === 'q-blog-myblog') {
return HttpResponse.json({ wikiEnabled: true });
}
return HttpResponse.json({}, { status: 404 });
}),
// Search across names by full identifier
http.get('/arbitrary/resources/search', ({ request }) => {
const url = new URL(request.url);
if (
url.searchParams.get('service') === 'BLOG_POST' &&
url.searchParams.get('identifier') === 'q-blog-myblog-post-123'
) {
return HttpResponse.json([
{ name: 'alice', identifier: 'q-blog-myblog-post-123', updated: 1000 },
{ name: 'carol', identifier: 'q-blog-myblog-post-123', updated: 2000 },
]);
}
return HttpResponse.json([]);
}),
// Canonical chosen should be carol (newer)
http.get('/arbitrary/BLOG_POST/:name/:fullId', ({ params }) => {
const { name, fullId } = params as any;
if (name === 'carol' && fullId === 'q-blog-myblog-post-123') {
return HttpResponse.json({
title: 'Newer Title',
createdAt: 1,
postContent: [{ type: 'editor', version: 1, id: 'e1', content: [{ text: 'Hello' }] }],
});
}
return HttpResponse.json({}, { status: 404 });
}),
);
render(
<Provider store={store}>
<ThemeProvider theme={createTheme()}>
<MemoryRouter initialEntries={[`/bob/myblog/123/edit`]}>
<Routes>
<Route path="/:user/:blog/:postId/edit" element={<EditPost />} />
</Routes>
</MemoryRouter>
</ThemeProvider>
</Provider>,
);
// Title input should pick canonical content's title
const titleInput = await screen.findByDisplayValue('Newer Title');
expect(titleInput).toBeInTheDocument();
});
});