Spaces:
Sleeping
Sleeping
| import { describe, it, expect } from 'vitest'; | |
| import { buildModifyTextRequests } from './modifyText.js'; | |
| describe('buildModifyTextRequests', () => { | |
| describe('insert mode', () => { | |
| it('should produce a single insertText request with no delete', () => { | |
| const requests = buildModifyTextRequests({ startIndex: 5, text: 'hello' }); | |
| expect(requests).toHaveLength(1); | |
| expect(requests[0]).toHaveProperty('insertText'); | |
| expect(requests[0]).not.toHaveProperty('deleteContentRange'); | |
| expect(requests[0].insertText!.location!.index).toBe(5); | |
| expect(requests[0].insertText!.text).toBe('hello'); | |
| }); | |
| }); | |
| describe('replace mode', () => { | |
| it('should produce deleteContentRange then insertText in that order', () => { | |
| const requests = buildModifyTextRequests({ startIndex: 5, endIndex: 10, text: 'hi' }); | |
| expect(requests).toHaveLength(2); | |
| // First request must be delete (not insert — wrong order causes index shift) | |
| expect(requests[0]).toHaveProperty('deleteContentRange'); | |
| expect(requests[0].deleteContentRange!.range!.startIndex).toBe(5); | |
| expect(requests[0].deleteContentRange!.range!.endIndex).toBe(10); | |
| // Second request is insert at the same startIndex | |
| expect(requests[1]).toHaveProperty('insertText'); | |
| expect(requests[1].insertText!.location!.index).toBe(5); | |
| expect(requests[1].insertText!.text).toBe('hi'); | |
| }); | |
| }); | |
| describe('format-only mode', () => { | |
| it('should produce a single updateTextStyle using the original range', () => { | |
| const requests = buildModifyTextRequests({ | |
| startIndex: 5, | |
| endIndex: 10, | |
| style: { bold: true }, | |
| }); | |
| expect(requests).toHaveLength(1); | |
| expect(requests[0]).not.toHaveProperty('deleteContentRange'); | |
| expect(requests[0]).not.toHaveProperty('insertText'); | |
| expect(requests[0]).toHaveProperty('updateTextStyle'); | |
| expect(requests[0].updateTextStyle!.range!.startIndex).toBe(5); | |
| expect(requests[0].updateTextStyle!.range!.endIndex).toBe(10); | |
| expect(requests[0].updateTextStyle!.textStyle!.bold).toBe(true); | |
| expect(requests[0].updateTextStyle!.fields).toBe('bold'); | |
| }); | |
| }); | |
| describe('replace + format mode', () => { | |
| it('should adjust the format range to match the newly inserted text length', () => { | |
| const requests = buildModifyTextRequests({ | |
| startIndex: 5, | |
| endIndex: 10, | |
| text: 'hi', | |
| style: { bold: true }, | |
| }); | |
| expect(requests).toHaveLength(3); | |
| // Delete original range | |
| expect(requests[0]).toHaveProperty('deleteContentRange'); | |
| expect(requests[0].deleteContentRange!.range!.startIndex).toBe(5); | |
| expect(requests[0].deleteContentRange!.range!.endIndex).toBe(10); | |
| // Insert replacement text | |
| expect(requests[1]).toHaveProperty('insertText'); | |
| expect(requests[1].insertText!.location!.index).toBe(5); | |
| expect(requests[1].insertText!.text).toBe('hi'); | |
| // Format the NEW range [5, 7), NOT the original [5, 10) | |
| expect(requests[2]).toHaveProperty('updateTextStyle'); | |
| expect(requests[2].updateTextStyle!.range!.startIndex).toBe(5); | |
| expect(requests[2].updateTextStyle!.range!.endIndex).toBe(7); // 5 + "hi".length | |
| expect(requests[2].updateTextStyle!.textStyle!.bold).toBe(true); | |
| }); | |
| }); | |
| describe('insert + format mode', () => { | |
| it('should insert text and format the inserted range', () => { | |
| const requests = buildModifyTextRequests({ | |
| startIndex: 5, | |
| text: 'hi', | |
| style: { italic: true }, | |
| }); | |
| expect(requests).toHaveLength(2); | |
| // No delete — this is insert-only | |
| expect(requests[0]).toHaveProperty('insertText'); | |
| expect(requests[0]).not.toHaveProperty('deleteContentRange'); | |
| expect(requests[0].insertText!.location!.index).toBe(5); | |
| expect(requests[0].insertText!.text).toBe('hi'); | |
| // Format the inserted range [5, 7) | |
| expect(requests[1]).toHaveProperty('updateTextStyle'); | |
| expect(requests[1].updateTextStyle!.range!.startIndex).toBe(5); | |
| expect(requests[1].updateTextStyle!.range!.endIndex).toBe(7); // 5 + "hi".length | |
| expect(requests[1].updateTextStyle!.textStyle!.italic).toBe(true); | |
| expect(requests[1].updateTextStyle!.fields).toBe('italic'); | |
| }); | |
| }); | |
| describe('tabId propagation', () => { | |
| it('should include tabId on all request locations/ranges when provided', () => { | |
| const requests = buildModifyTextRequests({ | |
| startIndex: 5, | |
| endIndex: 10, | |
| text: 'hi', | |
| style: { bold: true }, | |
| tabId: 't1', | |
| }); | |
| expect(requests).toHaveLength(3); | |
| expect((requests[0].deleteContentRange!.range as any).tabId).toBe('t1'); | |
| expect((requests[1].insertText!.location as any).tabId).toBe('t1'); | |
| expect(requests[2].updateTextStyle!.range!.tabId).toBe('t1'); | |
| }); | |
| it('should not include tabId when not provided', () => { | |
| const requests = buildModifyTextRequests({ | |
| startIndex: 5, | |
| endIndex: 10, | |
| text: 'hi', | |
| style: { bold: true }, | |
| }); | |
| expect(requests).toHaveLength(3); | |
| expect((requests[0].deleteContentRange!.range as any).tabId).toBeUndefined(); | |
| expect((requests[1].insertText!.location as any).tabId).toBeUndefined(); | |
| expect(requests[2].updateTextStyle!.range!.tabId).toBeUndefined(); | |
| }); | |
| }); | |
| describe('edge cases', () => { | |
| it('should return empty array when neither text nor style is provided', () => { | |
| const requests = buildModifyTextRequests({ startIndex: 5, endIndex: 10 }); | |
| expect(requests).toHaveLength(0); | |
| }); | |
| it('should not generate delete when endIndex is provided but text is not', () => { | |
| const requests = buildModifyTextRequests({ | |
| startIndex: 5, | |
| endIndex: 10, | |
| style: { underline: true }, | |
| }); | |
| expect(requests).toHaveLength(1); | |
| expect(requests[0]).toHaveProperty('updateTextStyle'); | |
| expect(requests[0]).not.toHaveProperty('deleteContentRange'); | |
| }); | |
| it('should handle multiple style fields correctly', () => { | |
| const requests = buildModifyTextRequests({ | |
| startIndex: 1, | |
| endIndex: 5, | |
| style: { bold: true, italic: true, fontSize: 14 }, | |
| }); | |
| expect(requests).toHaveLength(1); | |
| const style = requests[0].updateTextStyle!; | |
| expect(style.textStyle!.bold).toBe(true); | |
| expect(style.textStyle!.italic).toBe(true); | |
| expect(style.textStyle!.fontSize).toEqual({ magnitude: 14, unit: 'PT' }); | |
| expect(style.fields).toBe('bold,italic,fontSize'); | |
| }); | |
| }); | |
| }); | |