- Registry: Remove 'parent.name' from listFields (all shown records are top-level with no parent), remove 'parent: true' from include since unused - +page.server.ts: Custom load filters to parentId=null (top-level only); custom upsert syncs children (create/update/delete) and handles P2002 duplicate-name errors; custom delete cascades to children first to avoid FK constraint violations - CategoryForm.svelte: Replace parentId select dropdown with dynamic list of sub-category inputs. Each row has a name field and Remove button. Pre-populates from record.children when editing existing categories. Add Sub-category button appends new rows - +page.svelte: Remove unused relationOptionsData prop from CategoryForm; rename submit button label from 'Update' to 'Save' - prisma: Add ProclamationCategorySubscriptionLevel join table migration and regenerate client - General: Update TipTap mock, Toast, license-keys page, proclamations server/route tests, README, AGENTS.md, and other admin forms
123 lines
3.6 KiB
TypeScript
123 lines
3.6 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { mockProclamation, mockCategory, createUrl } from '$lib/test-utils';
|
|
|
|
vi.mock('$lib/db', () => ({
|
|
prisma: {
|
|
proclamation: { findMany: vi.fn() },
|
|
proclamationCategory: { findMany: vi.fn() }
|
|
}
|
|
}));
|
|
|
|
import { prisma } from '$lib/db';
|
|
import { load } from './+page.server';
|
|
|
|
const proclamationFindMany = prisma.proclamation.findMany as ReturnType<typeof vi.fn>;
|
|
const categoryFindMany = prisma.proclamationCategory.findMany as ReturnType<typeof vi.fn>;
|
|
|
|
describe('(protected)/admin/proclamations page server load', () => {
|
|
beforeEach(() => {
|
|
proclamationFindMany.mockReset();
|
|
categoryFindMany.mockReset();
|
|
});
|
|
|
|
it('returns proclamations and categories with no filters', async () => {
|
|
const proclamations = [mockProclamation()];
|
|
const categories = [mockCategory()];
|
|
proclamationFindMany.mockResolvedValue(proclamations);
|
|
categoryFindMany.mockResolvedValue(categories);
|
|
|
|
const url = createUrl('/admin/proclamations');
|
|
const result = (await load({ url } as any)) as any;
|
|
expect(result.proclamations).toEqual(proclamations);
|
|
expect(result.categories[0]).toEqual(
|
|
expect.objectContaining({
|
|
...categories[0],
|
|
displayName: categories[0].name
|
|
})
|
|
);
|
|
expect(result.search).toBe('');
|
|
expect(result.categoryId).toBeNull();
|
|
});
|
|
|
|
it('passes search filter to findMany when search param is present', async () => {
|
|
proclamationFindMany.mockResolvedValue([]);
|
|
categoryFindMany.mockResolvedValue([]);
|
|
|
|
const url = createUrl('/admin/proclamations', { search: 'healing' });
|
|
await load({ url } as any);
|
|
|
|
expect(proclamationFindMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
where: { name: { contains: 'healing', mode: 'insensitive' } }
|
|
})
|
|
);
|
|
});
|
|
|
|
it('returns search string from URL params', async () => {
|
|
proclamationFindMany.mockResolvedValue([]);
|
|
categoryFindMany.mockResolvedValue([]);
|
|
|
|
const url = createUrl('/admin/proclamations', { search: 'peace' });
|
|
const result = (await load({ url } as any)) as any;
|
|
expect(result.search).toBe('peace');
|
|
});
|
|
|
|
it('passes categoryId filter when category param is present', async () => {
|
|
proclamationFindMany.mockResolvedValue([]);
|
|
categoryFindMany.mockResolvedValue([]);
|
|
|
|
const url = createUrl('/admin/proclamations', { category: '3' });
|
|
await load({ url } as any);
|
|
|
|
expect(proclamationFindMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
where: { categoryId: 3 }
|
|
})
|
|
);
|
|
});
|
|
|
|
it('returns parsed categoryId from URL params', async () => {
|
|
proclamationFindMany.mockResolvedValue([]);
|
|
categoryFindMany.mockResolvedValue([]);
|
|
|
|
const url = createUrl('/admin/proclamations', { category: '5' });
|
|
const result = (await load({ url } as any)) as any;
|
|
expect(result.categoryId).toBe(5);
|
|
});
|
|
|
|
it('combines search and category filters', async () => {
|
|
proclamationFindMany.mockResolvedValue([]);
|
|
categoryFindMany.mockResolvedValue([]);
|
|
|
|
const url = createUrl('/admin/proclamations', { search: 'love', category: '2' });
|
|
await load({ url } as any);
|
|
|
|
expect(proclamationFindMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
where: {
|
|
name: { contains: 'love', mode: 'insensitive' },
|
|
categoryId: 2
|
|
}
|
|
})
|
|
);
|
|
});
|
|
|
|
it('includes solfeggioFrequency and category relations', async () => {
|
|
proclamationFindMany.mockResolvedValue([]);
|
|
categoryFindMany.mockResolvedValue([]);
|
|
|
|
const url = createUrl('/admin/proclamations');
|
|
await load({ url } as any);
|
|
|
|
expect(proclamationFindMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
include: {
|
|
solfeggioFrequency: true,
|
|
category: { include: { parent: true } },
|
|
subscriptionLevel: true
|
|
}
|
|
})
|
|
);
|
|
});
|
|
});
|