You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Tests: Integration harness fixes and refactors. (#2569)
* Test harness fixes and integration test refactor and addition. * Clean up. * Address review comments. * Improve method names.
This commit is contained in:
		
							
								
								
									
										93
									
								
								CliClient/tests/integration_ShowAllNotes.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								CliClient/tests/integration_ShowAllNotes.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| /* eslint-disable no-unused-vars */ | ||||
| require('app-module-path').addPath(__dirname); | ||||
| const { setupDatabaseAndSynchronizer, switchClient, asyncTest, id, ids, sortedIds, at, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('test-utils.js'); | ||||
| const Setting = require('lib/models/Setting.js'); | ||||
| const Folder = require('lib/models/Folder.js'); | ||||
| const Note = require('lib/models/Note.js'); | ||||
| const Tag = require('lib/models/Tag.js'); | ||||
| const { time } = require('lib/time-utils.js'); | ||||
| const { ALL_NOTES_FILTER_ID } = require('lib/reserved-ids.js'); | ||||
|  | ||||
| // | ||||
| // The integration tests are to test the integration of the core system, comprising the | ||||
| // base application with middleware, reducer and models in response to dispatched events. | ||||
| // | ||||
| // The general strategy for each integration test is: | ||||
| //  - create a starting application state, | ||||
| //  - inject the event to be tested | ||||
| //  - check the resulting application state | ||||
| // | ||||
| // Important: sleep must be used after TestApp dispatch to allow the async processing | ||||
| //  to complete | ||||
| // | ||||
|  | ||||
| // use this until Javascript arr.flat() function works in Travis | ||||
| function flatten(arr) { | ||||
| 	return (arr.reduce((acc, val) => acc.concat(val), [])); | ||||
| } | ||||
|  | ||||
| let testApp = null; | ||||
|  | ||||
| describe('integration_ShowAllNotes', function() { | ||||
|  | ||||
| 	beforeEach(async (done) => { | ||||
| 		testApp = new TestApp(); | ||||
| 		await testApp.start(['--no-welcome']); | ||||
| 		done(); | ||||
| 	}); | ||||
|  | ||||
| 	afterEach(async (done) => { | ||||
| 		if (testApp !== null) await testApp.destroy(); | ||||
| 		testApp = null; | ||||
| 		done(); | ||||
| 	}); | ||||
|  | ||||
| 	it('should show all notes', asyncTest(async () => { | ||||
| 		// setup | ||||
| 		let folders = await createNTestFolders(3); | ||||
| 		Folder.moveToFolder(id(folders[2]), id(folders[1])); // subfolder | ||||
| 		await time.msleep(100); | ||||
| 		let notes0 = await createNTestNotes(3, folders[0]); | ||||
| 		let notes1 = await createNTestNotes(3, folders[1]); | ||||
| 		let notes2 = await createNTestNotes(3, folders[2]); | ||||
|  | ||||
| 		// TEST ACTION: View all-notes | ||||
| 		testApp.dispatch({ type: 'SMART_FILTER_SELECT', id: ALL_NOTES_FILTER_ID }); | ||||
| 		await time.msleep(100); | ||||
|  | ||||
| 		// check: all the notes are shown | ||||
| 		let state = testApp.store().getState(); | ||||
| 		expect(state.notesParentType).toEqual('SmartFilter'); | ||||
| 		expect(state.selectedSmartFilterId).toEqual(ALL_NOTES_FILTER_ID); | ||||
| 		expect(sortedIds(state.notes)).toEqual(sortedIds(notes0.concat(notes1).concat(notes2))); | ||||
| 	})); | ||||
|  | ||||
| 	it('should show retain note selection when going from a folder to all-notes', asyncTest(async () => { | ||||
| 		// setup | ||||
| 		let folders = await createNTestFolders(2); | ||||
| 		let notes0 = await createNTestNotes(3, folders[0]); | ||||
| 		let notes1 = await createNTestNotes(3, folders[1]); | ||||
| 		testApp.dispatch({ type: 'FOLDER_SELECT', id: id(folders[1]) }); | ||||
| 		await time.msleep(100); | ||||
| 		testApp.dispatch({ type: 'NOTE_SELECT',	id: id(notes1[1]) }); | ||||
| 		await time.msleep(100); | ||||
|  | ||||
| 		// check the state is set up as expected | ||||
| 		let state = testApp.store().getState(); | ||||
| 		expect(state.notesParentType).toEqual('Folder'); | ||||
| 		expect(state.selectedFolderId).toEqual(id(folders[1])); | ||||
| 		expect(sortedIds(state.notes)).toEqual(sortedIds(notes1)); | ||||
| 		expect(state.selectedNoteIds).toEqual(ids([notes1[1]])); | ||||
|  | ||||
| 		// TEST ACTION: View all-notes | ||||
| 		testApp.dispatch({ type: 'SMART_FILTER_SELECT', id: ALL_NOTES_FILTER_ID }); | ||||
| 		await time.msleep(100); | ||||
|  | ||||
| 		// check: all the notes are shown | ||||
| 		state = testApp.store().getState(); | ||||
| 		expect(state.notesParentType).toEqual('SmartFilter'); | ||||
| 		expect(state.selectedSmartFilterId).toEqual(ALL_NOTES_FILTER_ID); | ||||
| 		expect(sortedIds(state.notes)).toEqual(sortedIds(notes0.concat(notes1))); | ||||
| 		expect(state.selectedNoteIds).toEqual(ids([notes1[1]])); | ||||
| 	})); | ||||
| }); | ||||
| @@ -1,117 +0,0 @@ | ||||
| /* eslint-disable no-unused-vars */ | ||||
| require('app-module-path').addPath(__dirname); | ||||
| const { setupDatabaseAndSynchronizer, switchClient, asyncTest, TestApp } = require('test-utils.js'); | ||||
| const Setting = require('lib/models/Setting.js'); | ||||
| const Folder = require('lib/models/Folder.js'); | ||||
| const Note = require('lib/models/Note.js'); | ||||
| const Tag = require('lib/models/Tag.js'); | ||||
| const { time } = require('lib/time-utils.js'); | ||||
| const { ALL_NOTES_FILTER_ID } = require('lib/reserved-ids.js'); | ||||
|  | ||||
| // | ||||
| // The integration tests are to test the integration of the core system, comprising the | ||||
| // base application with middleware, reducer and models in response to dispatched events. | ||||
| // | ||||
| // The general strategy for each integration test is: | ||||
| //  - create a starting application state, | ||||
| //  - inject the event to be tested | ||||
| //  - check the resulting application state | ||||
| // | ||||
| // In particular, this file contains integration tests for smart filter features. | ||||
| // | ||||
|  | ||||
| async function createNTestFolders(n) { | ||||
| 	let folders = []; | ||||
| 	for (let i = 0; i < n; i++) { | ||||
| 		let folder = await Folder.save({ title: 'folder' }); | ||||
| 		folders.push(folder); | ||||
| 	} | ||||
| 	return folders; | ||||
| } | ||||
|  | ||||
| async function createNTestNotes(n, folder) { | ||||
| 	let notes = []; | ||||
| 	for (let i = 0; i < n; i++) { | ||||
| 		let note = await Note.save({ title: 'note', parent_id: folder.id, is_conflict: 0 }); | ||||
| 		notes.push(note); | ||||
| 	} | ||||
| 	return notes; | ||||
| } | ||||
|  | ||||
| async function createNTestTags(n) { | ||||
| 	let tags = []; | ||||
| 	for (let i = 0; i < n; i++) { | ||||
| 		let tag = await Tag.save({ title: 'tag' }); | ||||
| 		tags.push(tag); | ||||
| 	} | ||||
| 	return tags; | ||||
| } | ||||
|  | ||||
| // use this until Javascript arr.flat() function works in Travis | ||||
| function flatten(arr) { | ||||
| 	return (arr.reduce((acc, val) => acc.concat(val), [])); | ||||
| } | ||||
|  | ||||
| let testApp = null; | ||||
|  | ||||
| describe('integration_SmartFilters', function() { | ||||
|  | ||||
| 	beforeEach(async (done) => { | ||||
| 		testApp = new TestApp(); | ||||
| 		await testApp.start(['--no-welcome']); | ||||
| 		done(); | ||||
| 	}); | ||||
|  | ||||
| 	afterEach(async (done) => { | ||||
| 		if (testApp !== null) await testApp.destroy(); | ||||
| 		testApp = null; | ||||
| 		done(); | ||||
| 	}); | ||||
|  | ||||
| 	it('should show notes in a folder', asyncTest(async () => { | ||||
| 		let folders = await createNTestFolders(2); | ||||
| 		let notes = []; | ||||
| 		for (let i = 0; i < folders.length; i++) { | ||||
| 			notes.push(await createNTestNotes(3, folders[i])); | ||||
| 		} | ||||
|  | ||||
| 		testApp.dispatch({ | ||||
| 			type: 'FOLDER_SELECT', | ||||
| 			id: folders[1].id, | ||||
| 		}); | ||||
| 		await time.msleep(100); | ||||
|  | ||||
| 		let state = testApp.store().getState(); | ||||
|  | ||||
| 		expect(state.notesParentType).toEqual('Folder'); | ||||
| 		expect(state.selectedFolderId).toEqual(folders[1].id); | ||||
|  | ||||
| 		let expectedNoteIds = notes[1].map(n => n.id).sort(); | ||||
| 		let noteIds = state.notes.map(n => n.id).sort(); | ||||
| 		expect(noteIds).toEqual(expectedNoteIds); | ||||
| 	})); | ||||
|  | ||||
| 	it('should show all notes', asyncTest(async () => { | ||||
| 		let folders = await createNTestFolders(2); | ||||
| 		let notes = []; | ||||
| 		for (let i = 0; i < folders.length; i++) { | ||||
| 			notes.push(await createNTestNotes(3, folders[i])); | ||||
| 		} | ||||
|  | ||||
| 		testApp.dispatch({ | ||||
| 			type: 'SMART_FILTER_SELECT', | ||||
| 			id: ALL_NOTES_FILTER_ID, | ||||
| 		}); | ||||
| 		await time.msleep(100); | ||||
|  | ||||
| 		let state = testApp.store().getState(); | ||||
|  | ||||
| 		expect(state.notesParentType).toEqual('SmartFilter'); | ||||
| 		expect(state.selectedSmartFilterId).toEqual(ALL_NOTES_FILTER_ID); | ||||
|  | ||||
| 		// let expectedNoteIds = notes.map(n => n.map(o => o.id)).flat().sort(); | ||||
| 		let expectedNoteIds = flatten(notes.map(n => n.map(o => o.id))).sort(); | ||||
| 		let noteIds = state.notes.map(n => n.id).sort(); | ||||
| 		expect(noteIds).toEqual(expectedNoteIds); | ||||
| 	})); | ||||
| }); | ||||
| @@ -1,40 +1,13 @@ | ||||
| /* eslint-disable no-unused-vars */ | ||||
|  | ||||
| require('app-module-path').addPath(__dirname); | ||||
| const { setupDatabaseAndSynchronizer, switchClient, asyncTest } = require('test-utils.js'); | ||||
| const { setupDatabaseAndSynchronizer, switchClient, asyncTest, createNTestNotes, createNTestFolders, createNTestTags } = require('test-utils.js'); | ||||
| const Folder = require('lib/models/Folder.js'); | ||||
| const Note = require('lib/models/Note.js'); | ||||
| const Tag = require('lib/models/Tag.js'); | ||||
| const { reducer, defaultState, stateUtils } = require('lib/reducer.js'); | ||||
|  | ||||
| async function createNTestFolders(n) { | ||||
| 	let folders = []; | ||||
| 	for (let i = 0; i < n; i++) { | ||||
| 		let folder = await Folder.save({ title: 'folder' }); | ||||
| 		folders.push(folder); | ||||
| 	} | ||||
| 	return folders; | ||||
| } | ||||
|  | ||||
| async function createNTestNotes(n, folder) { | ||||
| 	let notes = []; | ||||
| 	for (let i = 0; i < n; i++) { | ||||
| 		let note = await Note.save({ title: 'note', parent_id: folder.id, is_conflict: 0 }); | ||||
| 		notes.push(note); | ||||
| 	} | ||||
| 	return notes; | ||||
| } | ||||
|  | ||||
| async function createNTestTags(n) { | ||||
| 	let tags = []; | ||||
| 	for (let i = 0; i < n; i++) { | ||||
| 		let tag = await Tag.save({ title: 'tag' }); | ||||
| 		tags.push(tag); | ||||
| 	} | ||||
| 	return tags; | ||||
| } | ||||
|  | ||||
| function initTestState(folders, selectedFolderIndex, notes, selectedIndexes, tags=null, selectedTagIndex=null) { | ||||
| function initTestState(folders, selectedFolderIndex, notes, selectedNoteIndexes, tags=null, selectedTagIndex=null) { | ||||
| 	let state = defaultState; | ||||
|  | ||||
| 	if (selectedFolderIndex != null) { | ||||
| @@ -46,10 +19,10 @@ function initTestState(folders, selectedFolderIndex, notes, selectedIndexes, tag | ||||
| 	if (notes != null) { | ||||
| 		state = reducer(state, { type: 'NOTE_UPDATE_ALL', notes: notes, noteSource: 'test' }); | ||||
| 	} | ||||
| 	if (selectedIndexes != null) { | ||||
| 	if (selectedNoteIndexes != null) { | ||||
| 		let selectedIds = []; | ||||
| 		for (let i = 0; i < selectedIndexes.length; i++) { | ||||
| 			selectedIds.push(notes[selectedIndexes[i]].id); | ||||
| 		for (let i = 0; i < selectedNoteIndexes.length; i++) { | ||||
| 			selectedIds.push(notes[selectedNoteIndexes[i]].id); | ||||
| 		} | ||||
| 		state = reducer(state, { type: 'NOTE_SELECT', ids: selectedIds }); | ||||
| 	} | ||||
|   | ||||
| @@ -153,6 +153,7 @@ async function switchClient(id) { | ||||
|  | ||||
| async function clearDatabase(id = null) { | ||||
| 	if (id === null) id = currentClient_; | ||||
| 	if (!databases_[id]) return; | ||||
|  | ||||
| 	await ItemChange.waitForAllSaved(); | ||||
|  | ||||
| @@ -178,7 +179,6 @@ async function clearDatabase(id = null) { | ||||
| 		queries.push(`DELETE FROM ${n}`); | ||||
| 		queries.push(`DELETE FROM sqlite_sequence WHERE name="${n}"`); // Reset autoincremented IDs | ||||
| 	} | ||||
|  | ||||
| 	await databases_[id].transactionExecBatch(queries); | ||||
| } | ||||
|  | ||||
| @@ -413,6 +413,60 @@ async function allSyncTargetItemsEncrypted() { | ||||
| 	return totalCount === encryptedCount; | ||||
| } | ||||
|  | ||||
| function id(a) { | ||||
| 	return a.id; | ||||
| } | ||||
|  | ||||
| function ids(a) { | ||||
| 	return a.map(n => n.id); | ||||
| } | ||||
|  | ||||
| function sortedIds(a) { | ||||
| 	return ids(a).sort(); | ||||
| } | ||||
|  | ||||
| function at(a, indexes) { | ||||
| 	let out = []; | ||||
| 	for (let i = 0; i < indexes.length; i++) { | ||||
| 		out.push(a[indexes[i]]); | ||||
| 	} | ||||
| 	return out; | ||||
| } | ||||
|  | ||||
| async function createNTestFolders(n) { | ||||
| 	let folders = []; | ||||
| 	for (let i = 0; i < n; i++) { | ||||
| 		let folder = await Folder.save({ title: 'folder' }); | ||||
| 		folders.push(folder); | ||||
| 	} | ||||
| 	return folders; | ||||
| } | ||||
|  | ||||
| async function createNTestNotes(n, folder, tagIds = null, title = 'note') { | ||||
| 	let notes = []; | ||||
| 	for (let i = 0; i < n; i++) { | ||||
| 		let title_ = n > 1 ? `${title}${i}` : title; | ||||
| 		let note = await Note.save({ title: title_, parent_id: folder.id, is_conflict: 0 }); | ||||
| 		notes.push(note); | ||||
| 	} | ||||
| 	if (tagIds) { | ||||
| 		for (let i = 0; i < notes.length; i++) { | ||||
| 			await Tag.setNoteTagsByIds(notes[i].id, tagIds); | ||||
| 		} | ||||
| 	} | ||||
| 	return notes; | ||||
| } | ||||
|  | ||||
| async function createNTestTags(n) { | ||||
| 	let tags = []; | ||||
| 	for (let i = 0; i < n; i++) { | ||||
| 		let tag = await Tag.save({ title: 'tag' }); | ||||
| 		tags.push(tag); | ||||
| 	} | ||||
| 	return tags; | ||||
| } | ||||
|  | ||||
| // Integration test application | ||||
| class TestApp extends BaseApplication { | ||||
| 	constructor() { | ||||
| 		super(); | ||||
| @@ -420,10 +474,11 @@ class TestApp extends BaseApplication { | ||||
| 	} | ||||
|  | ||||
| 	async start(argv) { | ||||
| 		argv = await super.start(argv); | ||||
| 		await clearDatabase(); // not sure why we need this as we use our own database | ||||
|  | ||||
| 		argv = argv.concat(['--profile', `tests-build/profile-${uuid.create()}`]); | ||||
| 		argv = await super.start(['',''].concat(argv)); | ||||
| 		this.initRedux(); | ||||
| 		await setupDatabaseAndSynchronizer(1); | ||||
| 		await switchClient(1); | ||||
| 		Setting.dispatchUpdateAll(); | ||||
| 		await time.msleep(100); | ||||
| 	} | ||||
| @@ -449,13 +504,12 @@ class TestApp extends BaseApplication { | ||||
| 	} | ||||
|  | ||||
| 	async destroy() { | ||||
| 		this.deinitRedux(); | ||||
| 		await this.waitForMiddleware_(); | ||||
| 		this.deinitRedux(); | ||||
| 		await super.destroy(); | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| module.exports = { kvStore, resourceService, allSyncTargetItemsEncrypted, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, asyncTest, TestApp }; | ||||
| module.exports = { kvStore, resourceService, allSyncTargetItemsEncrypted, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, asyncTest, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp }; | ||||
|  | ||||
|   | ||||
| @@ -55,11 +55,20 @@ class BaseApplication { | ||||
| 	} | ||||
|  | ||||
| 	async destroy() { | ||||
| 		if (this.scheduleAutoAddResourcesIID_) { | ||||
| 			clearTimeout(this.scheduleAutoAddResourcesIID_); | ||||
| 			this.scheduleAutoAddResourcesIID_ = null; | ||||
| 		} | ||||
| 		await ResourceFetcher.instance().destroy(); | ||||
| 		await SearchEngine.instance().destroy(); | ||||
| 		await DecryptionWorker.instance().destroy(); | ||||
| 		await FoldersScreenUtils.cancelTimers(); | ||||
| 		await SearchEngine.instance().cancelTimers(); | ||||
| 		await DecryptionWorker.instance().cancelTimers(); | ||||
| 		await reg.cancelTimers(); | ||||
|  | ||||
| 		this.eventEmitter_.removeAllListeners(); | ||||
| 		BaseModel.db_ = null; | ||||
| 		reg.setDb(null); | ||||
|  | ||||
| 		this.logger_ = null; | ||||
| 		this.dbLogger_ = null; | ||||
| 		this.eventEmitter_ = null; | ||||
|   | ||||
| @@ -56,7 +56,10 @@ class FoldersScreenUtils { | ||||
| 	} | ||||
|  | ||||
| 	static async cancelTimers() { | ||||
| 		if (this.scheduleRefreshFoldersIID_) clearTimeout(this.scheduleRefreshFoldersIID_); | ||||
| 		if (this.scheduleRefreshFoldersIID_) { | ||||
| 			clearTimeout(this.scheduleRefreshFoldersIID_); | ||||
| 			this.scheduleRefreshFoldersIID_ = null; | ||||
| 		} | ||||
| 		return new Promise((resolve) => { | ||||
| 			const iid = setInterval(() => { | ||||
| 				if (!FoldersScreenUtils.refreshCalls_.length) { | ||||
|   | ||||
| @@ -193,7 +193,10 @@ reg.db = () => { | ||||
| }; | ||||
|  | ||||
| reg.cancelTimers = async () => { | ||||
| 	if (this.recurrentSyncId_) clearTimeout(this.recurrentSyncId_); | ||||
| 	if (this.recurrentSyncId_) { | ||||
| 		clearTimeout(this.recurrentSyncId_); | ||||
| 		this.recurrentSyncId_ = null; | ||||
| 	} | ||||
| 	return new Promise((resolve) => { | ||||
| 		const iid = setInterval(() => { | ||||
| 			if (!reg.syncCalls_.length) { | ||||
|   | ||||
| @@ -37,9 +37,9 @@ class DecryptionWorker { | ||||
| 	} | ||||
|  | ||||
| 	static instance() { | ||||
| 		if (this.instance_) return this.instance_; | ||||
| 		this.instance_ = new DecryptionWorker(); | ||||
| 		return this.instance_; | ||||
| 		if (DecryptionWorker.instance_) return DecryptionWorker.instance_; | ||||
| 		DecryptionWorker.instance_ = new DecryptionWorker(); | ||||
| 		return DecryptionWorker.instance_; | ||||
| 	} | ||||
|  | ||||
| 	setEncryptionService(v) { | ||||
| @@ -250,8 +250,15 @@ class DecryptionWorker { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	async cancelTimers() { | ||||
| 		if (this.scheduleId_) clearTimeout(this.scheduleId_); | ||||
| 	async destroy() { | ||||
| 		this.eventEmitter_.removeAllListeners(); | ||||
| 		if (this.scheduleId_) { | ||||
| 			clearTimeout(this.scheduleId_); | ||||
| 			this.scheduleId_ = null; | ||||
| 		} | ||||
| 		this.eventEmitter_ = null; | ||||
| 		DecryptionWorker.instance_ = null; | ||||
|  | ||||
| 		return new Promise((resolve) => { | ||||
| 			const iid = setInterval(() => { | ||||
| 				if (!this.startCalls_.length) { | ||||
| @@ -263,4 +270,6 @@ class DecryptionWorker { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| DecryptionWorker.instance_ = null; | ||||
|  | ||||
| module.exports = DecryptionWorker; | ||||
|   | ||||
| @@ -24,9 +24,9 @@ class ResourceFetcher extends BaseService { | ||||
| 	} | ||||
|  | ||||
| 	static instance() { | ||||
| 		if (this.instance_) return this.instance_; | ||||
| 		this.instance_ = new ResourceFetcher(); | ||||
| 		return this.instance_; | ||||
| 		if (ResourceFetcher.instance_) return ResourceFetcher.instance_; | ||||
| 		ResourceFetcher.instance_ = new ResourceFetcher(); | ||||
| 		return ResourceFetcher.instance_; | ||||
| 	} | ||||
|  | ||||
| 	on(eventName, callback) { | ||||
| @@ -248,6 +248,20 @@ class ResourceFetcher extends BaseService { | ||||
| 		await Resource.resetStartedFetchStatus(); | ||||
| 		this.autoAddResources(null); | ||||
| 	} | ||||
|  | ||||
| 	async destroy() { | ||||
| 		this.eventEmitter_.removeAllListeners(); | ||||
| 		if (this.scheduleQueueProcessIID_) { | ||||
| 			clearTimeout(this.scheduleQueueProcessIID_); | ||||
| 			this.scheduleQueueProcessIID_ = null; | ||||
| 		} | ||||
| 		this.eventEmitter_ = null; | ||||
| 		ResourceFetcher.instance_ = null; | ||||
|  | ||||
| 		return await this.waitForAllFinished(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ResourceFetcher.instance_ = null; | ||||
|  | ||||
| module.exports = ResourceFetcher; | ||||
|   | ||||
| @@ -18,9 +18,9 @@ class SearchEngine { | ||||
| 	} | ||||
|  | ||||
| 	static instance() { | ||||
| 		if (this.instance_) return this.instance_; | ||||
| 		this.instance_ = new SearchEngine(); | ||||
| 		return this.instance_; | ||||
| 		if (SearchEngine.instance_) return SearchEngine.instance_; | ||||
| 		SearchEngine.instance_ = new SearchEngine(); | ||||
| 		return SearchEngine.instance_; | ||||
| 	} | ||||
|  | ||||
| 	setLogger(logger) { | ||||
| @@ -413,8 +413,13 @@ class SearchEngine { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	async cancelTimers() { | ||||
| 		if (this.scheduleSyncTablesIID_) clearTimeout(this.scheduleSyncTablesIID_); | ||||
| 	async destroy() { | ||||
| 		if (this.scheduleSyncTablesIID_) { | ||||
| 			clearTimeout(this.scheduleSyncTablesIID_); | ||||
| 			this.scheduleSyncTablesIID_ = null; | ||||
| 		} | ||||
| 		SearchEngine.instance_ = null; | ||||
|  | ||||
| 		return new Promise((resolve) => { | ||||
| 			const iid = setInterval(() => { | ||||
| 				if (!this.syncCalls_.length) { | ||||
| @@ -426,4 +431,6 @@ class SearchEngine { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| SearchEngine.instance_ = null; | ||||
|  | ||||
| module.exports = SearchEngine; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user