mirror of
https://github.com/chatwoot/chatwoot.git
synced 2026-06-13 21:01:16 +08:00
# Pull Request Template ## Description FE code for document sync Adds: - UI to show counts (stats) of available web pages, stale and synced documents and last synced at - Bulk action and manual ways to sync web documents - index to stats related columns ## Type of change Please delete options that are not relevant. - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. https://linear.app/chatwoot/issue/AI-153/fe-document-auto-sync Documents dashboard: <img width="2160" height="986" alt="CleanShot 2026-05-11 at 17 57 09@2x" src="https://github.com/user-attachments/assets/6d934764-964c-4656-b005-1b4f0329e553" /> Filters: <img width="1138" height="564" alt="CleanShot 2026-05-11 at 17 58 13@2x" src="https://github.com/user-attachments/assets/cee780e6-eb8f-4aed-8cc5-b674244a821b" /> Needs update: <img width="2222" height="966" alt="CleanShot 2026-05-11 at 17 57 53@2x" src="https://github.com/user-attachments/assets/70c85ddd-7eb1-4328-ba14-7929e67e7b36" /> pdfs: <img width="2180" height="558" alt="CleanShot 2026-05-11 at 17 58 30@2x" src="https://github.com/user-attachments/assets/975b5c9f-bd1c-4979-9870-8f926d7f6e11" /> bulk actions: <img width="2244" height="992" alt="CleanShot 2026-05-11 at 17 58 57@2x" src="https://github.com/user-attachments/assets/bdb3c63f-d2de-41dc-a6d5-8821d3303be0" /> single url sync: <img width="2264" height="722" alt="CleanShot 2026-05-11 at 17 59 19@2x" src="https://github.com/user-attachments/assets/7d7323a5-0fcb-4be9-8635-55e56964999b" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sivin Varghese <[email protected]> Co-authored-by: iamsivin <[email protected]> Co-authored-by: Muhsin Keloth <[email protected]> Co-authored-by: Sony Mathew <[email protected]> Co-authored-by: Vishnu Narayanan <[email protected]>
140 lines
5.0 KiB
JavaScript
140 lines
5.0 KiB
JavaScript
import {
|
|
isPdfDocument,
|
|
isSafeHttpLink,
|
|
formatDocumentLink,
|
|
} from 'shared/helpers/documentHelper';
|
|
|
|
describe('documentHelper', () => {
|
|
describe('#isPdfDocument', () => {
|
|
it('returns true for PDF documents', () => {
|
|
expect(isPdfDocument('PDF:document.pdf')).toBe(true);
|
|
expect(isPdfDocument('PDF:my-file_20241227123045.pdf')).toBe(true);
|
|
expect(isPdfDocument('PDF:report with spaces_20241227123045.pdf')).toBe(
|
|
true
|
|
);
|
|
});
|
|
|
|
it('returns false for regular URLs', () => {
|
|
expect(isPdfDocument('https://example.com')).toBe(false);
|
|
expect(isPdfDocument('http://docs.example.com/file.pdf')).toBe(false);
|
|
expect(isPdfDocument('ftp://files.example.com/document.pdf')).toBe(false);
|
|
});
|
|
|
|
it('returns false for empty or null values', () => {
|
|
expect(isPdfDocument('')).toBe(false);
|
|
expect(isPdfDocument(null)).toBe(false);
|
|
expect(isPdfDocument(undefined)).toBe(false);
|
|
});
|
|
|
|
it('returns false for strings that contain PDF but do not start with PDF:', () => {
|
|
expect(isPdfDocument('document PDF:file.pdf')).toBe(false);
|
|
expect(isPdfDocument('My PDF:file.pdf')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('#isSafeHttpLink', () => {
|
|
it('returns true for http and https URLs', () => {
|
|
expect(isSafeHttpLink('http://example.com')).toBe(true);
|
|
expect(isSafeHttpLink('https://example.com/path?q=1#x')).toBe(true);
|
|
expect(isSafeHttpLink('HTTPS://EXAMPLE.COM')).toBe(true);
|
|
});
|
|
|
|
/* eslint-disable no-script-url */
|
|
it('returns false for javascript: and other dangerous schemes', () => {
|
|
expect(isSafeHttpLink('javascript:alert(1)')).toBe(false);
|
|
expect(isSafeHttpLink('JavaScript:alert(1)')).toBe(false);
|
|
expect(isSafeHttpLink('data:text/html,<script>alert(1)</script>')).toBe(
|
|
false
|
|
);
|
|
expect(isSafeHttpLink('vbscript:msgbox(1)')).toBe(false);
|
|
expect(isSafeHttpLink('file:///etc/passwd')).toBe(false);
|
|
expect(isSafeHttpLink('ftp://files.example.com/doc.pdf')).toBe(false);
|
|
});
|
|
/* eslint-enable no-script-url */
|
|
|
|
it('returns false for invalid or empty values', () => {
|
|
expect(isSafeHttpLink('')).toBe(false);
|
|
expect(isSafeHttpLink(null)).toBe(false);
|
|
expect(isSafeHttpLink(undefined)).toBe(false);
|
|
expect(isSafeHttpLink('not a url')).toBe(false);
|
|
expect(isSafeHttpLink('//example.com')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('#formatDocumentLink', () => {
|
|
describe('PDF documents', () => {
|
|
it('removes PDF: prefix from PDF documents', () => {
|
|
expect(formatDocumentLink('PDF:document.pdf')).toBe('document.pdf');
|
|
expect(formatDocumentLink('PDF:my-file.pdf')).toBe('my-file.pdf');
|
|
});
|
|
|
|
it('removes timestamp suffix from PDF documents', () => {
|
|
expect(formatDocumentLink('PDF:document_20241227123045.pdf')).toBe(
|
|
'document.pdf'
|
|
);
|
|
expect(formatDocumentLink('PDF:report_20231215094530.pdf')).toBe(
|
|
'report.pdf'
|
|
);
|
|
});
|
|
|
|
it('handles PDF documents with spaces in filename', () => {
|
|
expect(formatDocumentLink('PDF:my document_20241227123045.pdf')).toBe(
|
|
'my document.pdf'
|
|
);
|
|
expect(
|
|
formatDocumentLink('PDF:Annual Report 2024_20241227123045.pdf')
|
|
).toBe('Annual Report 2024.pdf');
|
|
});
|
|
|
|
it('handles PDF documents without timestamp suffix', () => {
|
|
expect(formatDocumentLink('PDF:document.pdf')).toBe('document.pdf');
|
|
expect(formatDocumentLink('PDF:simple-file.pdf')).toBe(
|
|
'simple-file.pdf'
|
|
);
|
|
});
|
|
|
|
it('handles PDF documents with partial timestamp patterns', () => {
|
|
expect(formatDocumentLink('PDF:document_202412.pdf')).toBe(
|
|
'document_202412.pdf'
|
|
);
|
|
expect(formatDocumentLink('PDF:file_123.pdf')).toBe('file_123.pdf');
|
|
});
|
|
|
|
it('handles edge cases with timestamp pattern', () => {
|
|
expect(
|
|
formatDocumentLink('PDF:doc_20241227123045_final_20241227123045.pdf')
|
|
).toBe('doc_20241227123045_final.pdf');
|
|
});
|
|
});
|
|
|
|
describe('Regular URLs', () => {
|
|
it('removes http(s) and www prefixes for compact display', () => {
|
|
expect(formatDocumentLink('https://example.com')).toBe('example.com');
|
|
expect(formatDocumentLink('http://docs.example.com/api')).toBe(
|
|
'docs.example.com/api'
|
|
);
|
|
expect(formatDocumentLink('https://www.github.com/user/repo')).toBe(
|
|
'github.com/user/repo'
|
|
);
|
|
});
|
|
|
|
it('handles URLs with query parameters', () => {
|
|
expect(formatDocumentLink('https://example.com?param=value')).toBe(
|
|
'example.com?param=value'
|
|
);
|
|
expect(
|
|
formatDocumentLink(
|
|
'https://api.example.com/docs?version=v1&format=json'
|
|
)
|
|
).toBe('api.example.com/docs?version=v1&format=json');
|
|
});
|
|
|
|
it('handles URLs with fragments', () => {
|
|
expect(formatDocumentLink('https://example.com/docs#section1')).toBe(
|
|
'example.com/docs#section1'
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|