UNPKG

cozy-dataproxy-lib

Version:

Library meant to be by Cozy Cloud's DataProxy apps for data manipulation

930 lines (874 loc) 34.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _cozyClient = require("cozy-client"); var _SearchEngine = require("./SearchEngine"); var consts = _interopRequireWildcard(require("./consts")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } jest.mock('flexsearch'); jest.mock('flexsearch/dist/module/lang/latin/simple'); jest.mock("./helpers/client", function () { return { getPouchLink: jest.fn() }; }); jest.mock("./helpers/getSearchEncoder", function () { return { getSearchEncoder: jest.fn() }; }); jest.mock("./helpers/normalizeSearchResult", function () { return { enrichResultsWithDocs: jest.fn(), normalizeSearchResult: jest.fn() }; }); jest.mock("./storage", function () { return { getExportDate: jest.fn(), importSearchIndexes: jest.fn(), exportSearchIndexes: jest.fn() }; }); jest.mock("./indexDocs", function () { return { indexAllDocs: jest.fn(), indexOnChanges: jest.fn(), indexSingleDoc: jest.fn(), initDoctypeAfterIndexImport: jest.fn(), initSearchIndex: jest.fn() }; }); jest.mock("./queries", function () { return { queryLocalOrRemoteDocs: jest.fn() }; }); jest.mock("./consts", function () { return { LIMIT_DOCTYPE_SEARCH: 3, SEARCH_SCHEMA: { 'io.cozy.files': ['name', 'path'], 'io.cozy.contacts': ['displayName', 'fullname'], 'io.cozy.apps': ['slug', 'name'] }, DOCTYPE_DEFAULT_ORDER: { 'io.cozy.apps': 0, 'io.cozy.contacts': 1, 'io.cozy.files': 2 }, FILES_DOCTYPE: 'io.cozy.files', CONTACTS_DOCTYPE: 'io.cozy.contacts', APPS_DOCTYPE: 'io.cozy.apps', SHARED_DRIVE_FILES_DOCTYPE: 'io.cozy.files.shareddrives', SHARED_DRIVES_DIR_ID: 'io.cozy.files.shared-drives-dir', SEARCHABLE_DOCTYPES: ['io.cozy.files', 'io.cozy.contacts', 'io.cozy.apps'] }; }); // Mock realtime dependencies jest.mock('cozy-realtime', function () { return { RealtimePlugin: { pluginName: 'realtime' }, __esModule: true, default: jest.fn() }; }); describe('sortSearchResults', function () { var searchEngine; beforeEach(function () { var client = (0, _cozyClient.createMockClient)(); searchEngine = new _SearchEngine.SearchEngine(client); }); afterEach(function () { jest.clearAllMocks(); }); it('should sort results by doctype order', function () { var searchResults = [{ doctype: consts.FILES_DOCTYPE, doc: { _type: consts.FILES_DOCTYPE } }, { doctype: consts.APPS_DOCTYPE, doc: { _type: consts.APPS_DOCTYPE } }, { doctype: consts.CONTACTS_DOCTYPE, doc: { _type: consts.CONTACTS_DOCTYPE } }]; var sortedResults = searchEngine.sortSearchResults(searchResults); expect(sortedResults[0].doctype).toBe(consts.APPS_DOCTYPE); expect(sortedResults[1].doctype).toBe(consts.CONTACTS_DOCTYPE); expect(sortedResults[2].doctype).toBe(consts.FILES_DOCTYPE); }); it('should sort apps by slug', function () { var searchResults = [{ doctype: consts.APPS_DOCTYPE, doc: { slug: 'appB', _type: consts.APPS_DOCTYPE } }, { doctype: consts.APPS_DOCTYPE, doc: { slug: 'appA', _type: consts.APPS_DOCTYPE } }]; var sortedResults = searchEngine.sortSearchResults(searchResults); expect(sortedResults[0].doc.slug).toBe('appA'); expect(sortedResults[1].doc.slug).toBe('appB'); }); it('should sort contacts by displayName', function () { var searchResults = [{ doctype: consts.CONTACTS_DOCTYPE, doc: { displayName: 'June', _type: consts.CONTACTS_DOCTYPE } }, { doctype: consts.CONTACTS_DOCTYPE, doc: { displayName: 'Alice', _type: consts.CONTACTS_DOCTYPE } }]; var sortedResults = searchEngine.sortSearchResults(searchResults); expect(sortedResults[0].doc.displayName).toBe('Alice'); expect(sortedResults[1].doc.displayName).toBe('June'); }); it('should sort files by type and name', function () { var searchResults = [{ doctype: consts.FILES_DOCTYPE, doc: { name: 'fileB', type: 'file', _type: consts.FILES_DOCTYPE }, fields: ['name'] }, { doctype: consts.FILES_DOCTYPE, doc: { name: 'fileA', type: 'file', _type: consts.FILES_DOCTYPE }, fields: ['name'] }, { doctype: consts.FILES_DOCTYPE, doc: { name: 'folderA', type: 'directory', _type: consts.FILES_DOCTYPE }, fields: ['name'] }]; var sortedResults = searchEngine.sortSearchResults(searchResults); expect(sortedResults[0].doc.type).toBe('directory'); // Folders should come first expect(sortedResults[1].doc.name).toBe('fileA'); expect(sortedResults[2].doc.name).toBe('fileB'); }); it('should sort files first if they match on name, then path', function () { var searchResults = [{ doctype: consts.FILES_DOCTYPE, doc: { name: 'test11', path: 'test/test11', type: 'file', _type: consts.FILES_DOCTYPE }, fields: ['name'] }, { doctype: consts.FILES_DOCTYPE, doc: { name: 'test1', path: 'test/test1', type: 'file', _type: consts.FILES_DOCTYPE }, fields: ['name'] }, { doctype: consts.FILES_DOCTYPE, doc: { name: 'DirName1', path: 'test1/path', type: 'directory', _type: consts.FILES_DOCTYPE }, fields: ['path'] }, { doctype: consts.FILES_DOCTYPE, doc: { name: 'DirName2', path: 'test1/path', type: 'directory', _type: consts.FILES_DOCTYPE }, fields: ['name'] }]; var sortedResults = searchEngine.sortSearchResults(searchResults); expect(sortedResults[0].doc.name).toBe('DirName2'); // Dir match on name expect(sortedResults[1].doc.name).toBe('test1'); // File match on name expect(sortedResults[2].doc.name).toBe('test11'); // File match on name expect(sortedResults[3].doc.name).toBe('DirName1'); // Directory }); }); describe('limitSearchResults', function () { var searchEngine; beforeEach(function () { var client = (0, _cozyClient.createMockClient)(); searchEngine = new _SearchEngine.SearchEngine(client); }); afterEach(function () { jest.clearAllMocks(); }); it('should return all results if doctype count is below or equal the limit', function () { var searchResults = [{ doctype: consts.FILES_DOCTYPE, id: 1 }, { doctype: consts.FILES_DOCTYPE, id: 2 }, { doctype: consts.FILES_DOCTYPE, id: 3 }]; var filteredResults = searchEngine.limitSearchResults(searchResults); expect(filteredResults).toEqual(searchResults); }); it('should filter results exceeding the limit for a specific doctype', function () { var searchResults = [{ doctype: consts.FILES_DOCTYPE, id: 1 }, { doctype: consts.FILES_DOCTYPE, id: 2 }, { doctype: consts.FILES_DOCTYPE, id: 3 }, { doctype: consts.FILES_DOCTYPE, id: 4 }, { doctype: consts.CONTACTS_DOCTYPE, id: 5 }]; var filteredResults = searchEngine.limitSearchResults(searchResults); expect(filteredResults).toEqual([{ doctype: consts.FILES_DOCTYPE, id: 1 }, { doctype: consts.FILES_DOCTYPE, id: 2 }, { doctype: consts.FILES_DOCTYPE, id: 3 }, { doctype: consts.CONTACTS_DOCTYPE, id: 5 }]); }); it('should return an empty array if input is empty', function () { var searchResults = []; var filteredResults = searchEngine.limitSearchResults(searchResults); expect(filteredResults).toEqual([]); }); }); describe('getSharedDrivesDoctypes', function () { var searchEngine; var mockClient; var mockPouchLink; beforeEach(function () { mockClient = (0, _cozyClient.createMockClient)(); mockPouchLink = { doctypes: ['io.cozy.files', 'io.cozy.contacts', 'io.cozy.apps', 'io.cozy.files.shareddrives.drive1', 'io.cozy.files.shareddrives.drive2'] }; var _require = require("./helpers/client"), getPouchLink = _require.getPouchLink; getPouchLink.mockReturnValue(mockPouchLink); searchEngine = new _SearchEngine.SearchEngine(mockClient, {}, undefined, { shouldInit: false }); }); afterEach(function () { jest.clearAllMocks(); }); it('should return shared drives doctypes when pouch link exists', function () { var sharedDrivesDoctypes = searchEngine.getSharedDrivesDoctypes(); expect(sharedDrivesDoctypes).toEqual(['io.cozy.files.shareddrives.drive1', 'io.cozy.files.shareddrives.drive2']); }); it('should return empty array when pouch link does not exist', function () { var _require2 = require("./helpers/client"), getPouchLink = _require2.getPouchLink; getPouchLink.mockReturnValue(null); var sharedDrivesDoctypes = searchEngine.getSharedDrivesDoctypes(); expect(sharedDrivesDoctypes).toEqual([]); }); it('should return empty array when no shared drives doctypes exist', function () { mockPouchLink.doctypes = ['io.cozy.files', 'io.cozy.contacts', 'io.cozy.apps']; var sharedDrivesDoctypes = searchEngine.getSharedDrivesDoctypes(); expect(sharedDrivesDoctypes).toEqual([]); }); }); describe('search method with shared drives integration', function () { var searchEngine; var mockClient; var mockPouchLink; var mockStorage; beforeEach(function () { mockClient = (0, _cozyClient.createMockClient)(); mockStorage = { storeData: jest.fn(), getData: jest.fn() }; mockPouchLink = { doctypes: ['io.cozy.files', 'io.cozy.contacts', 'io.cozy.apps', 'io.cozy.files.shareddrives.drive1', 'io.cozy.files.shareddrives.drive2'] }; var _require3 = require("./helpers/client"), getPouchLink = _require3.getPouchLink; getPouchLink.mockReturnValue(mockPouchLink); searchEngine = new _SearchEngine.SearchEngine(mockClient, mockStorage, undefined, { shouldInit: false }); // Mock search indexes with shared drives searchEngine.searchIndexes = { 'io.cozy.files': { index: { search: jest.fn().mockReturnValue([]) } }, 'io.cozy.files.shareddrives.drive1': { index: { search: jest.fn().mockReturnValue([]) } }, 'io.cozy.files.shareddrives.drive2': { index: { search: jest.fn().mockReturnValue([]) } } }; }); afterEach(function () { jest.clearAllMocks(); }); it('should include shared drives doctypes when searching files without specific doctypes', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() { var _require4, enrichResultsWithDocs, normalizeSearchResult, searchOnIndexesSpy; return _regenerator.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _require4 = require("./helpers/normalizeSearchResult"), enrichResultsWithDocs = _require4.enrichResultsWithDocs, normalizeSearchResult = _require4.normalizeSearchResult; enrichResultsWithDocs.mockResolvedValue([]); normalizeSearchResult.mockReturnValue({ title: 'test', doc: {} }); searchOnIndexesSpy = jest.spyOn(searchEngine, 'searchOnIndexes'); searchOnIndexesSpy.mockReturnValue([]); _context.next = 7; return searchEngine.search('test query', { doctypes: [consts.FILES_DOCTYPE] }); case 7: expect(searchOnIndexesSpy).toHaveBeenCalledWith('test query', [consts.FILES_DOCTYPE, 'io.cozy.files.shareddrives.drive1', 'io.cozy.files.shareddrives.drive2']); case 8: case "end": return _context.stop(); } } }, _callee); }))); it('should include shared drives doctypes when searching without specific doctypes', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() { var _require5, enrichResultsWithDocs, normalizeSearchResult, searchOnIndexesSpy; return _regenerator.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: _require5 = require("./helpers/normalizeSearchResult"), enrichResultsWithDocs = _require5.enrichResultsWithDocs, normalizeSearchResult = _require5.normalizeSearchResult; enrichResultsWithDocs.mockResolvedValue([]); normalizeSearchResult.mockReturnValue({ title: 'test', doc: {} }); searchOnIndexesSpy = jest.spyOn(searchEngine, 'searchOnIndexes'); searchOnIndexesSpy.mockReturnValue([]); _context2.next = 7; return searchEngine.search('test query', {}); case 7: expect(searchOnIndexesSpy).toHaveBeenCalledWith('test query', ['io.cozy.files.shareddrives.drive1', 'io.cozy.files.shareddrives.drive2']); case 8: case "end": return _context2.stop(); } } }, _callee2); }))); it('should not include shared drives doctypes when searching specific non-files doctypes', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() { var _require6, enrichResultsWithDocs, normalizeSearchResult, searchOnIndexesSpy; return _regenerator.default.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: _require6 = require("./helpers/normalizeSearchResult"), enrichResultsWithDocs = _require6.enrichResultsWithDocs, normalizeSearchResult = _require6.normalizeSearchResult; enrichResultsWithDocs.mockResolvedValue([]); normalizeSearchResult.mockReturnValue({ title: 'test', doc: {} }); searchOnIndexesSpy = jest.spyOn(searchEngine, 'searchOnIndexes'); searchOnIndexesSpy.mockReturnValue([]); _context3.next = 7; return searchEngine.search('test query', { doctypes: [consts.CONTACTS_DOCTYPE] }); case 7: expect(searchOnIndexesSpy).toHaveBeenCalledWith('test query', [consts.CONTACTS_DOCTYPE]); case 8: case "end": return _context3.stop(); } } }, _callee3); }))); it('should clean up non-existing doctypes from search indexes', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() { var _require7, enrichResultsWithDocs, normalizeSearchResult, searchOnIndexesSpy; return _regenerator.default.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: // Set up search indexes with doctypes that don't exist in pouch link searchEngine.searchIndexes = { 'io.cozy.files': { index: { search: jest.fn().mockReturnValue([]) } }, 'io.cozy.files.shareddrives.drive1': { index: { search: jest.fn().mockReturnValue([]) } }, 'non.existing.doctype': { index: { search: jest.fn().mockReturnValue([]) } } }; _require7 = require("./helpers/normalizeSearchResult"), enrichResultsWithDocs = _require7.enrichResultsWithDocs, normalizeSearchResult = _require7.normalizeSearchResult; enrichResultsWithDocs.mockResolvedValue([]); normalizeSearchResult.mockReturnValue({ title: 'test', doc: {} }); searchOnIndexesSpy = jest.spyOn(searchEngine, 'searchOnIndexes'); searchOnIndexesSpy.mockReturnValue([]); _context4.next = 8; return searchEngine.search('test query', {}); case 8: // Check that non-existing doctype was removed expect(searchEngine.searchIndexes['non.existing.doctype']).toBeUndefined(); expect(searchEngine.searchIndexes['io.cozy.files']).toBeDefined(); expect(searchEngine.searchIndexes['io.cozy.files.shareddrives.drive1']).toBeDefined(); case 11: case "end": return _context4.stop(); } } }, _callee4); }))); }); describe('indexDocumentsAtInit with shared drives', function () { var searchEngine; var mockClient; var mockStorage; var mockPouchLink; beforeEach(function () { mockClient = (0, _cozyClient.createMockClient)(); mockStorage = { storeData: jest.fn(), getData: jest.fn().mockResolvedValue(null) // No persisted index }; mockPouchLink = { doctypes: ['io.cozy.files', 'io.cozy.contacts', 'io.cozy.apps', 'io.cozy.files.shareddrives.drive1', 'io.cozy.files.shareddrives.drive2'] }; var _require8 = require("./helpers/client"), getPouchLink = _require8.getPouchLink; getPouchLink.mockReturnValue(mockPouchLink); searchEngine = new _SearchEngine.SearchEngine(mockClient, mockStorage, undefined, { shouldInit: false }); searchEngine.isLocalSearch = true; }); afterEach(function () { jest.clearAllMocks(); }); it('should index shared drives doctypes during initialization', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5() { var _require9, getExportDate, _require10, queryLocalOrRemoteDocs, _require11, initSearchIndex, indexAllDocs, indexDocsForSearchSpy; return _regenerator.default.wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: _require9 = require("./storage"), getExportDate = _require9.getExportDate; _require10 = require("./queries"), queryLocalOrRemoteDocs = _require10.queryLocalOrRemoteDocs; _require11 = require("./indexDocs"), initSearchIndex = _require11.initSearchIndex, indexAllDocs = _require11.indexAllDocs; getExportDate.mockResolvedValue(null); // No persisted index queryLocalOrRemoteDocs.mockResolvedValue([]); initSearchIndex.mockReturnValue({ search: jest.fn() }); indexAllDocs.mockImplementation(function () {}); indexDocsForSearchSpy = jest.spyOn(searchEngine, 'indexDocsForSearch'); indexDocsForSearchSpy.mockResolvedValue({ index: { search: jest.fn() }, lastSeq: 1, lastUpdated: new Date().toISOString() }); _context5.next = 11; return searchEngine.indexDocumentsAtInit(); case 11: // Should be called for standard doctypes + shared drives doctypes expect(indexDocsForSearchSpy).toHaveBeenCalledWith('io.cozy.files'); expect(indexDocsForSearchSpy).toHaveBeenCalledWith('io.cozy.contacts'); expect(indexDocsForSearchSpy).toHaveBeenCalledWith('io.cozy.apps'); expect(indexDocsForSearchSpy).toHaveBeenCalledWith('io.cozy.files.shareddrives.drive1'); expect(indexDocsForSearchSpy).toHaveBeenCalledWith('io.cozy.files.shareddrives.drive2'); case 16: case "end": return _context5.stop(); } } }, _callee5); }))); }); // Realtime features tests describe('Realtime features', function () { var searchEngine; var mockClient; var mockStorage; var mockPouchLink; var mockRealtimePlugin; var mockCozyRealtime; beforeEach(function () { mockClient = (0, _cozyClient.createMockClient)(); mockStorage = { storeData: jest.fn(), getData: jest.fn() }; mockPouchLink = { doctypes: ['io.cozy.files', 'io.cozy.contacts', 'io.cozy.apps'], startReplicationWithDebounce: jest.fn(), getDbInfo: jest.fn().mockResolvedValue({ update_seq: 1 }), getSharedDriveDoctypes: jest.fn().mockReturnValue([]) }; mockRealtimePlugin = { subscribe: jest.fn() }; mockCozyRealtime = jest.fn().mockImplementation(function () { return { subscribe: jest.fn(), stop: jest.fn() }; }); var _require12 = require("./helpers/client"), getPouchLink = _require12.getPouchLink; getPouchLink.mockReturnValue(mockPouchLink); var CozyRealtime = require('cozy-realtime').default; CozyRealtime.mockImplementation(mockCozyRealtime); // Mock client plugins mockClient.plugins = { realtime: mockRealtimePlugin }; mockClient.registerPlugin = jest.fn(); mockClient.on = jest.fn(); mockClient.isLogged = true; searchEngine = new _SearchEngine.SearchEngine(mockClient, mockStorage, undefined, { shouldInit: false }); searchEngine.isLocalSearch = true; }); afterEach(function () { jest.clearAllMocks(); }); describe('init method - realtime setup', function () { it('should setup shared drives realtime if pouch link has shared drives doctypes', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6() { return _regenerator.default.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: mockPouchLink.getSharedDriveDoctypes.mockReturnValue(['io.cozy.files.shareddrives-drive1', 'io.cozy.files.shareddrives-drive2']); _context6.next = 3; return searchEngine.init(); case 3: expect(mockCozyRealtime).toHaveBeenCalledWith({ client: mockClient, sharedDriveId: 'drive1', background: true }); expect(mockCozyRealtime).toHaveBeenCalledWith({ client: mockClient, sharedDriveId: 'drive2', background: true }); case 5: case "end": return _context6.stop(); } } }, _callee6); }))); }); describe('handleUpdatedOrCreatedDoc method', function () { beforeEach(function () { searchEngine.searchIndexes = { 'io.cozy.files': { index: { search: jest.fn() } } }; }); it('should return early if doctype is not a searched doctype', function () { var _require13 = require("./indexDocs"), indexSingleDoc = _require13.indexSingleDoc; var doc = { _type: 'io.cozy.notsearched', _id: 'test-id' }; searchEngine.handleUpdatedOrCreatedDoc(doc); expect(indexSingleDoc).not.toHaveBeenCalled(); }); it('should return early if no search index exists for doctype', function () { var _require14 = require("./indexDocs"), indexSingleDoc = _require14.indexSingleDoc; var doc = { _type: 'io.cozy.contacts', _id: 'test-id' }; searchEngine.handleUpdatedOrCreatedDoc(doc); expect(indexSingleDoc).not.toHaveBeenCalled(); }); it('should call indexSingleDoc for valid doctype with existing index', function () { var _require15 = require("./indexDocs"), indexSingleDoc = _require15.indexSingleDoc; var doc = { _type: 'io.cozy.files', _id: 'test-id' }; searchEngine.handleUpdatedOrCreatedDoc(doc); expect(indexSingleDoc).toHaveBeenCalledWith(searchEngine.searchIndexes['io.cozy.files'].index, doc); }); it('should trigger debounced replication for local search', function () { var doc = { _type: 'io.cozy.files', _id: 'test-id' }; var debouncedReplicationSpy = jest.spyOn(searchEngine, 'debouncedReplication'); searchEngine.handleUpdatedOrCreatedDoc(doc); expect(debouncedReplicationSpy).toHaveBeenCalled(); }); it('should not trigger debounced replication for non-local search', function () { searchEngine.isLocalSearch = false; var doc = { _type: 'io.cozy.files', _id: 'test-id' }; var debouncedReplicationSpy = jest.spyOn(searchEngine, 'debouncedReplication'); searchEngine.handleUpdatedOrCreatedDoc(doc); expect(debouncedReplicationSpy).not.toHaveBeenCalled(); }); }); describe('handleDeletedDoc method', function () { beforeEach(function () { var mockIndex = { remove: jest.fn() }; searchEngine.searchIndexes = { 'io.cozy.files': { index: mockIndex } }; }); it('should return early if doctype is not a searched doctype', function () { var doc = { _type: 'io.cozy.notsearched', _id: 'test-id' }; searchEngine.handleDeletedDoc(doc); expect(searchEngine.searchIndexes['io.cozy.files'].index.remove).not.toHaveBeenCalled(); }); it('should return early if no search index exists for doctype', function () { var doc = { _type: 'io.cozy.contacts', _id: 'test-id' }; searchEngine.handleDeletedDoc(doc); expect(searchEngine.searchIndexes['io.cozy.files'].index.remove).not.toHaveBeenCalled(); }); it('should remove document from search index for valid doctype', function () { var doc = { _type: 'io.cozy.files', _id: 'test-id' }; searchEngine.handleDeletedDoc(doc); expect(searchEngine.searchIndexes['io.cozy.files'].index.remove).toHaveBeenCalledWith('test-id'); }); it('should trigger debounced replication for local search', function () { var doc = { _type: 'io.cozy.files', _id: 'test-id' }; var debouncedReplicationSpy = jest.spyOn(searchEngine, 'debouncedReplication'); searchEngine.handleDeletedDoc(doc); expect(debouncedReplicationSpy).toHaveBeenCalled(); }); it('should not trigger debounced replication for non-local search', function () { searchEngine.isLocalSearch = false; var doc = { _type: 'io.cozy.files', _id: 'test-id' }; var debouncedReplicationSpy = jest.spyOn(searchEngine, 'debouncedReplication'); searchEngine.handleDeletedDoc(doc); expect(debouncedReplicationSpy).not.toHaveBeenCalled(); }); }); describe('Shared drives realtime functionality', function () { beforeEach(function () { var mockRealtimeInstance = { subscribe: jest.fn(), stop: jest.fn() }; mockCozyRealtime.mockReturnValue(mockRealtimeInstance); }); describe('addSharedDrive method', function () { it('should seed an index, add shared drive realtime and trigger replication', function () { var _require16 = require("./indexDocs"), initSearchIndex = _require16.initSearchIndex; initSearchIndex.mockReturnValue({ search: jest.fn() }); var addSharedDriveRealtimeSpy = jest.spyOn(searchEngine, 'addSharedDriveRealtime'); var debouncedReplicationSpy = jest.spyOn(searchEngine, 'debouncedReplication'); var indexDocsForSearchSpy = jest.spyOn(searchEngine, 'indexDocsForSearch').mockResolvedValue(undefined); searchEngine.addSharedDrive('drive1'); // The index is seeded immediately so realtime events are not dropped // while the first replication is still running. expect(searchEngine.searchIndexes['io.cozy.files.shareddrives-drive1']).toBeDefined(); expect(addSharedDriveRealtimeSpy).toHaveBeenCalledWith('drive1'); expect(debouncedReplicationSpy).toHaveBeenCalled(); // Existing replicated documents are indexed without waiting for a refresh. expect(indexDocsForSearchSpy).toHaveBeenCalledWith('io.cozy.files.shareddrives-drive1'); }); it('should not trigger replication for non-local search', function () { searchEngine.isLocalSearch = false; var debouncedReplicationSpy = jest.spyOn(searchEngine, 'debouncedReplication'); searchEngine.addSharedDrive('drive1'); expect(debouncedReplicationSpy).not.toHaveBeenCalled(); }); }); describe('removeSharedDrive method', function () { beforeEach(function () { var mockRealtimeInstance = { subscribe: jest.fn(), stop: jest.fn() }; searchEngine.sharedDrivesRealtimes = { drive1: mockRealtimeInstance }; searchEngine.searchIndexes = { 'io.cozy.files.shareddrives-drive1': { index: { search: jest.fn() } } }; }); it('should stop realtime, remove from indexes and trigger replication', function () { var mockRealtimeInstance = searchEngine.sharedDrivesRealtimes.drive1; searchEngine.removeSharedDrive('drive1'); expect(mockRealtimeInstance.stop).toHaveBeenCalled(); expect(searchEngine.sharedDrivesRealtimes.drive1).toBeUndefined(); expect(searchEngine.searchIndexes['io.cozy.files.shareddrives-drive1']).toBeUndefined(); }); it('should not trigger replication for non-local search', function () { searchEngine.isLocalSearch = false; var debouncedReplicationSpy = jest.spyOn(searchEngine, 'debouncedReplication'); searchEngine.removeSharedDrive('drive1'); expect(debouncedReplicationSpy).not.toHaveBeenCalled(); }); }); describe('addSharedDriveRealtime private method', function () { it('should create CozyRealtime instance and subscribe to files doctype', function () { var mockRealtimeInstance = { subscribe: jest.fn(), stop: jest.fn() }; mockCozyRealtime.mockReturnValue(mockRealtimeInstance); searchEngine.addSharedDriveRealtime('drive1'); expect(mockCozyRealtime).toHaveBeenCalledWith({ client: mockClient, sharedDriveId: 'drive1', background: true }); expect(mockRealtimeInstance.subscribe).toHaveBeenCalledWith('created', consts.FILES_DOCTYPE, expect.any(Function)); expect(mockRealtimeInstance.subscribe).toHaveBeenCalledWith('updated', consts.FILES_DOCTYPE, expect.any(Function)); expect(mockRealtimeInstance.subscribe).toHaveBeenCalledWith('deleted', consts.FILES_DOCTYPE, expect.any(Function)); expect(searchEngine.sharedDrivesRealtimes.drive1).toBe(mockRealtimeInstance); }); }); describe('shared drives realtime lifecycle', function () { it('stops the previous realtime when re-subscribing the same drive', function () { var firstInstance = { subscribe: jest.fn(), stop: jest.fn() }; var secondInstance = { subscribe: jest.fn(), stop: jest.fn() }; mockCozyRealtime.mockReturnValueOnce(firstInstance).mockReturnValueOnce(secondInstance); searchEngine.addSharedDriveRealtime('drive1'); searchEngine.addSharedDriveRealtime('drive1'); expect(firstInstance.stop).toHaveBeenCalledTimes(1); expect(searchEngine.sharedDrivesRealtimes.drive1).toBe(secondInstance); }); it('stopSharedDrivesRealtimes stops every socket and clears the map', function () { var driveA = { subscribe: jest.fn(), stop: jest.fn() }; var driveB = { subscribe: jest.fn(), stop: jest.fn() }; searchEngine.sharedDrivesRealtimes = { driveA: driveA, driveB: driveB }; searchEngine.stopSharedDrivesRealtimes(); expect(driveA.stop).toHaveBeenCalledTimes(1); expect(driveB.stop).toHaveBeenCalledTimes(1); expect(searchEngine.sharedDrivesRealtimes).toEqual({}); }); it('stops all shared drives realtimes when the client logs out', function () { var _mockClient$on$mock$c; var stopSpy = jest.spyOn(searchEngine, 'stopSharedDrivesRealtimes'); var logoutHandler = (_mockClient$on$mock$c = mockClient.on.mock.calls.find(function (_ref7) { var _ref8 = (0, _slicedToArray2.default)(_ref7, 1), event = _ref8[0]; return event === 'logout'; })) === null || _mockClient$on$mock$c === void 0 ? void 0 : _mockClient$on$mock$c[1]; expect(logoutHandler).toBeDefined(); logoutHandler(); expect(stopSpy).toHaveBeenCalled(); }); }); }); });