|
| 1 | +/** |
| 2 | + * ObjectUI -- Persistence Tests |
| 3 | + */ |
| 4 | + |
| 5 | +import { describe, it, expect, vi, beforeEach } from 'vitest'; |
| 6 | +import { render, screen, fireEvent } from '@testing-library/react'; |
| 7 | +import { ListView } from '../ListView'; |
| 8 | +import type { ListViewSchema } from '@object-ui/types'; |
| 9 | +import { SchemaRendererProvider } from '@object-ui/react'; |
| 10 | + |
| 11 | +// Mock localStorage |
| 12 | +const localStorageMock = (() => { |
| 13 | + let store: Record<string, string> = {}; |
| 14 | + return { |
| 15 | + getItem: (key: string) => store[key] || null, |
| 16 | + setItem: (key: string, value: string) => { store[key] = value; }, |
| 17 | + clear: () => { store = {}; }, |
| 18 | + removeItem: (key: string) => { delete store[key]; }, |
| 19 | + }; |
| 20 | +})(); |
| 21 | + |
| 22 | +const mockDataSource = { |
| 23 | + find: vi.fn().mockResolvedValue([]), |
| 24 | + findOne: vi.fn(), |
| 25 | + create: vi.fn(), |
| 26 | + update: vi.fn(), |
| 27 | + delete: vi.fn(), |
| 28 | +}; |
| 29 | + |
| 30 | +const renderWithProvider = (component: React.ReactNode) => { |
| 31 | + return render( |
| 32 | + <SchemaRendererProvider dataSource={mockDataSource}> |
| 33 | + {component} |
| 34 | + </SchemaRendererProvider> |
| 35 | + ); |
| 36 | +}; |
| 37 | + |
| 38 | +Object.defineProperty(window, 'localStorage', { value: localStorageMock }); |
| 39 | + |
| 40 | +describe('ListView Persistence', () => { |
| 41 | + beforeEach(() => { |
| 42 | + localStorageMock.clear(); |
| 43 | + vi.clearAllMocks(); |
| 44 | + }); |
| 45 | + |
| 46 | + it('should use unique storage key when schema.id is provided', () => { |
| 47 | + const schema: ListViewSchema = { |
| 48 | + type: 'list-view', |
| 49 | + id: 'my-custom-view', |
| 50 | + objectName: 'tasks', |
| 51 | + viewType: 'grid', // Start with grid |
| 52 | + }; |
| 53 | + |
| 54 | + renderWithProvider(<ListView schema={schema} />); |
| 55 | + |
| 56 | + // Simulate changing to list view |
| 57 | + const listButton = screen.getByLabelText('List'); |
| 58 | + fireEvent.click(listButton); |
| 59 | + |
| 60 | + // Check scoped storage key |
| 61 | + const expectedKey = 'listview-tasks-my-custom-view-view'; |
| 62 | + expect(localStorageMock.getItem(expectedKey)).toBe('list'); |
| 63 | + |
| 64 | + // Check fallback key is NOT set |
| 65 | + expect(localStorageMock.getItem('listview-tasks-view')).toBeNull(); |
| 66 | + }); |
| 67 | + |
| 68 | + it('should not conflict with other views of the same object', () => { |
| 69 | + // Setup: View A (Global/Default) prefers Grid |
| 70 | + localStorageMock.setItem('listview-tasks-view', 'grid'); |
| 71 | + |
| 72 | + // Setup: View B (Special) prefers List |
| 73 | + // We define View B with valid options for Kanban to force it to render the button just in case, |
| 74 | + // but we will test switching between Grid/List. |
| 75 | + |
| 76 | + const viewB_Schema: ListViewSchema = { |
| 77 | + type: 'list-view', |
| 78 | + id: 'special-view', |
| 79 | + objectName: 'tasks', |
| 80 | + viewType: 'list' // Default to List |
| 81 | + }; |
| 82 | + |
| 83 | + renderWithProvider(<ListView schema={viewB_Schema} />); |
| 84 | + |
| 85 | + // Should use the schema default 'list' (since no storage exists for THIS view id) |
| 86 | + // It should NOT use 'grid' from the global/default view. |
| 87 | + |
| 88 | + const listButton = screen.getByLabelText('List'); |
| 89 | + expect(listButton.getAttribute('data-state')).toBe('on'); |
| 90 | + |
| 91 | + const gridButton = screen.getByLabelText('Grid'); |
| 92 | + expect(gridButton.getAttribute('data-state')).toBe('off'); |
| 93 | + }); |
| 94 | + |
| 95 | + it('should switch correctly when storage has a value for THIS view', () => { |
| 96 | + // Setup: This specific view was previously set to 'list' |
| 97 | + localStorageMock.setItem('listview-tasks-my-board-view', 'list'); |
| 98 | + |
| 99 | + const schema: ListViewSchema = { |
| 100 | + type: 'list-view', |
| 101 | + id: 'my-board', |
| 102 | + objectName: 'tasks', |
| 103 | + viewType: 'grid' // Default in schema is grid |
| 104 | + }; |
| 105 | + |
| 106 | + renderWithProvider(<ListView schema={schema} />); |
| 107 | + |
| 108 | + // Should respect storage ('list') over schema ('grid') |
| 109 | + const listButton = screen.getByLabelText('List'); |
| 110 | + expect(listButton.getAttribute('data-state')).toBe('on'); |
| 111 | + }); |
| 112 | +}); |
0 commit comments