- Add can_edit_staff and can_edit_customers dangerous permissions - Move Site Builder, Services, Locations, Time Blocks, Payments to Settings permissions - Link Edit Others' Schedules and Edit Own Schedule permissions - Add permission checks to StaffViewSet (partial_update, toggle_active, verify_email) - Add permission checks to CustomerViewSet (update, partial_update, verify_email) - Fix CustomerViewSet permission key mismatch (can_access_customers) - Hide Edit/Verify buttons on Staff and Customers pages without permission - Make dangerous permissions section more visually distinct (darker red) - Fix StaffDashboard links to use correct paths (/dashboard/my-schedule) - Disable settings sub-permissions when Access Settings is unchecked 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
211 lines
6.9 KiB
TypeScript
211 lines
6.9 KiB
TypeScript
/**
|
|
* Tests for helpSearchIndex data and utility functions
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
helpSearchIndex,
|
|
getHelpContextForAI,
|
|
HelpPage,
|
|
} from '../helpSearchIndex';
|
|
|
|
describe('helpSearchIndex', () => {
|
|
it('contains help pages', () => {
|
|
expect(helpSearchIndex.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('all pages have required properties', () => {
|
|
helpSearchIndex.forEach((page) => {
|
|
expect(page.path).toBeTruthy();
|
|
expect(page.title).toBeTruthy();
|
|
expect(page.description).toBeTruthy();
|
|
expect(Array.isArray(page.topics)).toBe(true);
|
|
expect(page.topics.length).toBeGreaterThan(0);
|
|
expect(page.category).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
it('all paths start with /dashboard/help', () => {
|
|
helpSearchIndex.forEach((page) => {
|
|
expect(page.path.startsWith('/dashboard/help')).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('paths are unique', () => {
|
|
const paths = helpSearchIndex.map((p) => p.path);
|
|
const uniquePaths = new Set(paths);
|
|
expect(uniquePaths.size).toBe(paths.length);
|
|
});
|
|
|
|
describe('categories', () => {
|
|
it('contains Core Features category', () => {
|
|
const corePages = helpSearchIndex.filter((p) => p.category === 'Core Features');
|
|
expect(corePages.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('contains Management category', () => {
|
|
const managementPages = helpSearchIndex.filter((p) => p.category === 'Management');
|
|
expect(managementPages.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('contains Communication category', () => {
|
|
const communicationPages = helpSearchIndex.filter((p) => p.category === 'Communication');
|
|
expect(communicationPages.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('contains Payments category', () => {
|
|
const paymentPages = helpSearchIndex.filter((p) => p.category === 'Payments');
|
|
expect(paymentPages.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('contains Automations category', () => {
|
|
const automationPages = helpSearchIndex.filter((p) => p.category === 'Automations');
|
|
expect(automationPages.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('contains API category', () => {
|
|
const apiPages = helpSearchIndex.filter((p) => p.category === 'API');
|
|
expect(apiPages.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('contains Settings category', () => {
|
|
const settingsPages = helpSearchIndex.filter((p) => p.category === 'Settings');
|
|
expect(settingsPages.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('specific pages', () => {
|
|
it('includes Dashboard page', () => {
|
|
const dashboard = helpSearchIndex.find((p) => p.path === '/dashboard/help/dashboard');
|
|
expect(dashboard).toBeDefined();
|
|
expect(dashboard?.title).toBe('Dashboard');
|
|
expect(dashboard?.category).toBe('Core Features');
|
|
});
|
|
|
|
it('includes Scheduler page', () => {
|
|
const scheduler = helpSearchIndex.find((p) => p.path === '/dashboard/help/scheduler');
|
|
expect(scheduler).toBeDefined();
|
|
expect(scheduler?.title).toBe('Scheduler');
|
|
expect(scheduler?.topics).toContain('calendar');
|
|
});
|
|
|
|
it('includes Customers page', () => {
|
|
const customers = helpSearchIndex.find((p) => p.path === '/dashboard/help/customers');
|
|
expect(customers).toBeDefined();
|
|
expect(customers?.title).toBe('Customers');
|
|
expect(customers?.category).toBe('Management');
|
|
});
|
|
|
|
it('includes Services page', () => {
|
|
const services = helpSearchIndex.find((p) => p.path === '/dashboard/help/services');
|
|
expect(services).toBeDefined();
|
|
expect(services?.topics).toContain('pricing');
|
|
});
|
|
|
|
it('includes API Overview page', () => {
|
|
const api = helpSearchIndex.find((p) => p.path === '/dashboard/help/api');
|
|
expect(api).toBeDefined();
|
|
expect(api?.title).toBe('API Overview');
|
|
expect(api?.category).toBe('API');
|
|
});
|
|
|
|
it('includes Booking Settings page', () => {
|
|
const booking = helpSearchIndex.find((p) => p.path === '/dashboard/help/settings/booking');
|
|
expect(booking).toBeDefined();
|
|
expect(booking?.topics).toContain('cancellation');
|
|
expect(booking?.topics).toContain('reschedule');
|
|
});
|
|
});
|
|
|
|
describe('topics', () => {
|
|
it('pages have relevant topics', () => {
|
|
const scheduler = helpSearchIndex.find((p) => p.path === '/dashboard/help/scheduler');
|
|
expect(scheduler?.topics).toContain('appointments');
|
|
expect(scheduler?.topics).toContain('bookings');
|
|
expect(scheduler?.topics).toContain('calendar');
|
|
});
|
|
|
|
it('payments page has payment-related topics', () => {
|
|
const payments = helpSearchIndex.find((p) => p.path === '/dashboard/help/payments');
|
|
expect(payments?.topics).toContain('payments');
|
|
expect(payments?.topics).toContain('stripe');
|
|
expect(payments?.topics).toContain('credit card');
|
|
});
|
|
|
|
it('staff page has staff-related topics', () => {
|
|
const staff = helpSearchIndex.find((p) => p.path === '/dashboard/help/staff');
|
|
expect(staff?.topics).toContain('permissions');
|
|
expect(staff?.topics).toContain('roles');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('getHelpContextForAI', () => {
|
|
it('returns a string', () => {
|
|
const context = getHelpContextForAI();
|
|
expect(typeof context).toBe('string');
|
|
});
|
|
|
|
it('contains all page titles', () => {
|
|
const context = getHelpContextForAI();
|
|
helpSearchIndex.forEach((page) => {
|
|
expect(context).toContain(`Page: ${page.title}`);
|
|
});
|
|
});
|
|
|
|
it('contains all page paths', () => {
|
|
const context = getHelpContextForAI();
|
|
helpSearchIndex.forEach((page) => {
|
|
expect(context).toContain(`Path: ${page.path}`);
|
|
});
|
|
});
|
|
|
|
it('contains all page categories', () => {
|
|
const context = getHelpContextForAI();
|
|
helpSearchIndex.forEach((page) => {
|
|
expect(context).toContain(`Category: ${page.category}`);
|
|
});
|
|
});
|
|
|
|
it('contains all page descriptions', () => {
|
|
const context = getHelpContextForAI();
|
|
helpSearchIndex.forEach((page) => {
|
|
expect(context).toContain(`Description: ${page.description}`);
|
|
});
|
|
});
|
|
|
|
it('contains topics for each page', () => {
|
|
const context = getHelpContextForAI();
|
|
helpSearchIndex.forEach((page) => {
|
|
expect(context).toContain(`Topics: ${page.topics.join(', ')}`);
|
|
});
|
|
});
|
|
|
|
it('uses separator between pages', () => {
|
|
const context = getHelpContextForAI();
|
|
expect(context).toContain('---');
|
|
});
|
|
|
|
it('is non-empty', () => {
|
|
const context = getHelpContextForAI();
|
|
expect(context.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('HelpPage type', () => {
|
|
it('can create a valid HelpPage object', () => {
|
|
const page: HelpPage = {
|
|
path: '/test',
|
|
title: 'Test Page',
|
|
description: 'A test page',
|
|
topics: ['test', 'example'],
|
|
category: 'Test Category',
|
|
};
|
|
|
|
expect(page.path).toBe('/test');
|
|
expect(page.title).toBe('Test Page');
|
|
expect(page.description).toBe('A test page');
|
|
expect(page.topics).toContain('test');
|
|
expect(page.category).toBe('Test Category');
|
|
});
|
|
});
|