diff --git a/workspace-server/src/__tests__/services/DocsService.test.ts b/workspace-server/src/__tests__/services/DocsService.test.ts index b7c9d7a..818b748 100644 --- a/workspace-server/src/__tests__/services/DocsService.test.ts +++ b/workspace-server/src/__tests__/services/DocsService.test.ts @@ -506,6 +506,180 @@ describe('DocsService', () => { index: 1, }); }); + + it('should extract text from smart chips (date, person, rich link)', async () => { + const mockDoc = { + data: { + tabs: [ + { + documentTab: { + body: { + content: [ + { + paragraph: { + elements: [ + { + textRun: { content: 'Meeting on ' }, + }, + { + dateElement: { + dateElementProperties: { + displayText: 'Jan 15, 2025', + timestamp: '1736899200', + }, + }, + }, + { + textRun: { content: ' with ' }, + }, + { + person: { + personProperties: { + name: 'John Doe', + email: 'john@example.com', + }, + }, + }, + { + textRun: { content: ' - see ' }, + }, + { + richLink: { + richLinkProperties: { + title: 'Project Plan', + uri: 'https://docs.google.com/document/d/abc123', + }, + }, + }, + { + textRun: { content: '\n' }, + }, + ], + }, + }, + ], + }, + }, + }, + ], + }, + }; + mockDocsAPI.documents.get.mockResolvedValue(mockDoc); + + const result = await docsService.getText({ documentId: 'test-doc-id' }); + + expect(result.content[0].text).toBe( + 'Meeting on Jan 15, 2025 with [John Doe](mailto:john@example.com) - see [Project Plan](https://docs.google.com/document/d/abc123)\n', + ); + }); + + it('should fall back to email when person name is not available', async () => { + const mockDoc = { + data: { + tabs: [ + { + documentTab: { + body: { + content: [ + { + paragraph: { + elements: [ + { + person: { + personProperties: { + email: 'jane@example.com', + }, + }, + }, + ], + }, + }, + ], + }, + }, + }, + ], + }, + }; + mockDocsAPI.documents.get.mockResolvedValue(mockDoc); + + const result = await docsService.getText({ documentId: 'test-doc-id' }); + + expect(result.content[0].text).toBe('[jane@example.com](mailto:jane@example.com)'); + }); + + it('should render rich link as markdown link', async () => { + const mockDoc = { + data: { + tabs: [ + { + documentTab: { + body: { + content: [ + { + paragraph: { + elements: [ + { + richLink: { + richLinkProperties: { + title: 'Budget Spreadsheet', + uri: 'https://docs.google.com/spreadsheets/d/xyz', + }, + }, + }, + ], + }, + }, + ], + }, + }, + }, + ], + }, + }; + mockDocsAPI.documents.get.mockResolvedValue(mockDoc); + + const result = await docsService.getText({ documentId: 'test-doc-id' }); + + expect(result.content[0].text).toBe( + '[Budget Spreadsheet](https://docs.google.com/spreadsheets/d/xyz)', + ); + }); + + it('should fall back to timestamp when date displayText is not available', async () => { + const mockDoc = { + data: { + tabs: [ + { + documentTab: { + body: { + content: [ + { + paragraph: { + elements: [ + { + dateElement: { + dateElementProperties: { + timestamp: '1736899200', + }, + }, + }, + ], + }, + }, + ], + }, + }, + }, + ], + }, + }; + mockDocsAPI.documents.get.mockResolvedValue(mockDoc); + + const result = await docsService.getText({ documentId: 'test-doc-id' }); + + expect(result.content[0].text).toBe('1736899200'); + }); }); describe('appendText', () => { diff --git a/workspace-server/src/services/DocsService.ts b/workspace-server/src/services/DocsService.ts index b90f66a..ed5cfd8 100644 --- a/workspace-server/src/services/DocsService.ts +++ b/workspace-server/src/services/DocsService.ts @@ -444,6 +444,16 @@ export class DocsService { element.paragraph.elements?.forEach((pElement) => { if (pElement.textRun && pElement.textRun.content) { text += pElement.textRun.content; + } else if (pElement.person?.personProperties) { + const { name, email } = pElement.person.personProperties; + text += `[${name || email}](mailto:${email})`; + } else if (pElement.richLink?.richLinkProperties) { + const { title, uri } = pElement.richLink.richLinkProperties; + text += `[${title}](${uri})`; + } else if (pElement.dateElement?.dateElementProperties) { + const { displayText, timestamp } = + pElement.dateElement.dateElementProperties; + text += displayText || timestamp || ''; } }); } else if (element.table) {