Articoli: cestino con ripristino, e azioni a icona come in Utenti
Eliminare un articolo non lasciava nulla di recuperabile: ora la riga finisce nel cestino (colonna deleted_at, aggiunta anche ai database già esistenti) e sparisce da sito e pannello, ma resta ripristinabile dalla nuova pagina /admin/cestino, dove sta anche la cancellazione definitiva. L'API la accetta solo su articoli già cestinati, così il contenuto non si perde per un clic distratto. Ogni lettura pubblica — elenco, recenti, archivio per mese, pagina del singolo articolo — filtra il cestino, e chi non gestisce vede nel cestino soltanto i propri articoli. Le azioni di riga diventano icone (matita e cestino) come nell'elenco utenti, con etichetta accessibile che nomina l'articolo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import type Database from 'better-sqlite3';
|
||||
import { createDb } from '../src/lib/db';
|
||||
import {
|
||||
createPost, trashPost, restorePost, deletePost, getPostById, listAllPosts, listByAuthor,
|
||||
listTrashed, listPublished, listRecent, listArchiveMonths, getPublishedBySlug,
|
||||
} from '../src/lib/posts';
|
||||
|
||||
let db: Database.Database;
|
||||
let id: number;
|
||||
|
||||
const articolo = (slug: string, authorId: number | null = 1) => ({
|
||||
title: `Titolo ${slug}`, slug, category: 'training', excerpt: 'x',
|
||||
body_html: '<p>x</p>', draft: false, author_id: authorId,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
db = createDb(':memory:');
|
||||
id = createPost(db, articolo('uno'));
|
||||
createPost(db, articolo('due', 2));
|
||||
});
|
||||
|
||||
describe('cestino', () => {
|
||||
it('cestinare non cancella: l\'articolo resta leggibile per id', () => {
|
||||
trashPost(db, id);
|
||||
const p = getPostById(db, id);
|
||||
expect(p).toBeDefined();
|
||||
expect(p!.deleted_at).not.toBeNull();
|
||||
});
|
||||
|
||||
it('lo toglie da tutti gli elenchi del pannello', () => {
|
||||
trashPost(db, id);
|
||||
expect(listAllPosts(db).map((p) => p.slug)).toEqual(['due']);
|
||||
expect(listByAuthor(db, 1)).toEqual([]);
|
||||
expect(listTrashed(db).map((p) => p.slug)).toEqual(['uno']);
|
||||
});
|
||||
|
||||
it('lo toglie dal sito pubblico: elenco, recenti, archivio e pagina singola', () => {
|
||||
trashPost(db, id);
|
||||
expect(listPublished(db).items.map((p) => p.slug)).toEqual(['due']);
|
||||
expect(listPublished(db).total).toBe(1);
|
||||
expect(listPublished(db, { category: 'training' }).total).toBe(1);
|
||||
expect(listRecent(db).map((p) => p.slug)).toEqual(['due']);
|
||||
expect(listArchiveMonths(db).reduce((n, m) => n + m.count, 0)).toBe(1);
|
||||
expect(getPublishedBySlug(db, 'uno')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('il cestino di un autore contiene solo i suoi articoli', () => {
|
||||
trashPost(db, id);
|
||||
expect(listTrashed(db, 1).map((p) => p.slug)).toEqual(['uno']);
|
||||
expect(listTrashed(db, 2)).toEqual([]);
|
||||
});
|
||||
|
||||
it('il ripristino lo rimette dov\'era', () => {
|
||||
trashPost(db, id);
|
||||
restorePost(db, id);
|
||||
expect(getPostById(db, id)!.deleted_at).toBeNull();
|
||||
expect(listTrashed(db)).toEqual([]);
|
||||
expect(listAllPosts(db).map((p) => p.slug)).toContain('uno');
|
||||
expect(getPublishedBySlug(db, 'uno')).toBeDefined();
|
||||
});
|
||||
|
||||
it('la cancellazione definitiva rimuove davvero la riga', () => {
|
||||
trashPost(db, id);
|
||||
deletePost(db, id);
|
||||
expect(getPostById(db, id)).toBeUndefined();
|
||||
expect(listTrashed(db)).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user