| 
									
										
										
										
											2017-08-15 13:54:22 +02:00
										 |  |  | // @flow
 | 
					
						
							|  |  |  | import o from "ospec/ospec.js" | 
					
						
							| 
									
										
										
										
											2018-10-12 10:50:17 +02:00
										 |  |  | import type {BrowserData} from "../../src/misc/ClientConstants" | 
					
						
							|  |  |  | import type {Db} from "../../src/api/worker/search/SearchTypes" | 
					
						
							|  |  |  | import {aes256RandomKey} from "../../src/api/worker/crypto/Aes" | 
					
						
							|  |  |  | import {IndexerCore} from "../../src/api/worker/search/IndexerCore" | 
					
						
							|  |  |  | import {EventQueue} from "../../src/api/worker/search/EventQueue" | 
					
						
							|  |  |  | import {DbTransaction} from "../../src/api/worker/search/DbFacade" | 
					
						
							| 
									
										
										
										
											2019-10-01 10:14:01 +02:00
										 |  |  | import {fixedIv} from "../../src/api/worker/crypto/CryptoUtils" | 
					
						
							| 
									
										
										
										
											2020-01-30 18:58:00 +01:00
										 |  |  | import {defer} from "../../src/api/common/utils/Utils" | 
					
						
							| 
									
										
										
										
											2017-08-15 13:54:22 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Mocks an attribute (function or object) on an object and makes sure that it can be restored to the original attribute by calling unmockAttribute() later. | 
					
						
							|  |  |  |  * Additionally creates a spy for the attribute if the attribute is a function. | 
					
						
							|  |  |  |  * @param object The object on which the attribute exists. | 
					
						
							|  |  |  |  * @param attributeOnObject The attribute to mock. | 
					
						
							|  |  |  |  * @param attributeMock The attribute mock. | 
					
						
							|  |  |  |  * @returns An object to be passed to unmockAttribute() in order to restore the original attribute. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-09-21 11:17:16 +02:00
										 |  |  | export function mockAttribute(object: Object, attributeOnObject: Function | Object, attributeMock: Function | Object): Object { | 
					
						
							| 
									
										
										
										
											2017-08-15 13:54:22 +02:00
										 |  |  | 	if (attributeOnObject == null) throw new Error("attributeOnObject is undefined") | 
					
						
							|  |  |  | 	let attributeName = Object.getOwnPropertyNames(object).find(key => object[key] === attributeOnObject) | 
					
						
							|  |  |  | 	if (!attributeName) { | 
					
						
							| 
									
										
										
										
											2018-09-21 11:17:16 +02:00
										 |  |  | 		attributeName = Object.getOwnPropertyNames(Object.getPrototypeOf(object)) | 
					
						
							|  |  |  | 		                      .find(key => object[key] === attributeOnObject) | 
					
						
							| 
									
										
										
										
											2017-08-15 13:54:22 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (!attributeName) { | 
					
						
							|  |  |  | 		throw new Error("attribute not found on object") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	object[attributeName] = (typeof attributeOnObject == "function") ? o.spy(attributeMock) : attributeMock | 
					
						
							|  |  |  | 	return { | 
					
						
							|  |  |  | 		_originalObject: object, | 
					
						
							|  |  |  | 		_originalAttribute: attributeOnObject, | 
					
						
							|  |  |  | 		_attributeName: attributeName | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function unmockAttribute(mock: Object) { | 
					
						
							|  |  |  | 	mock._originalObject[mock._attributeName] = mock._originalAttribute | 
					
						
							| 
									
										
										
										
											2018-09-21 11:17:16 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-22 12:02:13 +02:00
										 |  |  | export type Spy = ((...any) => any) & {invocations: any[]} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function spy(producer?: (...any) => any): Spy { | 
					
						
							| 
									
										
										
										
											2018-09-21 11:17:16 +02:00
										 |  |  | 	const invocations = [] | 
					
						
							|  |  |  | 	const s = (...args: any[]) => { | 
					
						
							|  |  |  | 		invocations.push(args) | 
					
						
							|  |  |  | 		return producer && producer(...args) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	s.invocations = invocations | 
					
						
							|  |  |  | 	return s | 
					
						
							| 
									
										
										
										
											2018-10-01 13:29:32 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 13:15:55 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Create partial mock, i.e. allows mocking attributes or functions on actual instances | 
					
						
							|  |  |  |  * @param obj The base mock object on which mocker may overwrite attributes or functions | 
					
						
							|  |  |  |  * @param mocker This function receives obj and can overwrite attributes or functions. | 
					
						
							|  |  |  |  * @returns {T} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-01 13:29:32 +02:00
										 |  |  | export const mock = <T>(obj: T, mocker: any => any): T => { | 
					
						
							|  |  |  | 	mocker(obj) | 
					
						
							|  |  |  | 	return obj | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function mapToObject<K, V>(map: Map<K, V>): {[K]: V} { | 
					
						
							|  |  |  | 	const obj: {[K]: V} = {} | 
					
						
							|  |  |  | 	map.forEach((value, key) => { | 
					
						
							|  |  |  | 		obj[key] = value | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return obj | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function mapObject<K, V, R>(mapper: (V) => R, obj: {[K]: V}): {[K]: R} { | 
					
						
							|  |  |  | 	const newObj = {} | 
					
						
							|  |  |  | 	for (let key of Object.keys(obj)) { | 
					
						
							|  |  |  | 		newObj[key] = mapper(obj[key]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return newObj | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function replaceAllMaps(toReplace: any): any { | 
					
						
							|  |  |  | 	return toReplace instanceof Map | 
					
						
							|  |  |  | 		? replaceAllMaps(mapToObject(toReplace)) | 
					
						
							|  |  |  | 		: toReplace instanceof Array | 
					
						
							|  |  |  | 			? toReplace.map(replaceAllMaps) | 
					
						
							|  |  |  | 			: toReplace != null && Object.getPrototypeOf(toReplace) === (Object: any).prototype // plain object
 | 
					
						
							|  |  |  | 				? mapObject(replaceAllMaps, toReplace) | 
					
						
							|  |  |  | 				: toReplace | 
					
						
							| 
									
										
										
										
											2018-10-12 10:50:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-12 16:16:17 +02:00
										 |  |  | export const browserDataStub: BrowserData = {needsMicrotaskHack: false, needsExplicitIDBIds: false, indexedDbSupported: true} | 
					
						
							| 
									
										
										
										
											2018-10-12 10:50:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | export function makeCore(args?: { | 
					
						
							|  |  |  | 	db?: Db, | 
					
						
							|  |  |  | 	queue?: EventQueue, | 
					
						
							|  |  |  | 	browserData?: BrowserData, | 
					
						
							|  |  |  | 	transaction?: DbTransaction | 
					
						
							|  |  |  | }, mocker?: (any) => void): IndexerCore { | 
					
						
							|  |  |  | 	const safeArgs = args || {} | 
					
						
							|  |  |  | 	const {transaction = (null: any)} = safeArgs | 
					
						
							|  |  |  | 	const defaultDb = { | 
					
						
							|  |  |  | 		key: aes256RandomKey(), | 
					
						
							|  |  |  | 		iv: fixedIv, | 
					
						
							| 
									
										
										
										
											2019-10-02 13:59:34 +02:00
										 |  |  | 		dbFacade: ({createTransaction: () => Promise.resolve(transaction)}: any), | 
					
						
							| 
									
										
										
										
											2018-10-12 10:50:17 +02:00
										 |  |  | 		initialized: Promise.resolve() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	const {db = defaultDb, queue = (null: any), browserData = browserDataStub} = safeArgs | 
					
						
							|  |  |  | 	const core = new IndexerCore(db, queue, browserData) | 
					
						
							|  |  |  | 	mocker && mock(core, mocker) | 
					
						
							|  |  |  | 	return core | 
					
						
							| 
									
										
										
										
											2019-03-12 11:58:31 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-01-30 18:58:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | export function makeTimeoutMock() { | 
					
						
							|  |  |  | 	let deferred = defer() | 
					
						
							|  |  |  | 	let timeoutId = 1 | 
					
						
							|  |  |  | 	const timeoutMock = function (fn: () => any) { | 
					
						
							|  |  |  | 		deferred.promise.finally(() => { | 
					
						
							|  |  |  | 			deferred = defer() | 
					
						
							|  |  |  | 			fn() | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		timeoutId++ | 
					
						
							|  |  |  | 		return timeoutId | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	timeoutMock.next = function () { | 
					
						
							|  |  |  | 		return deferred.resolve() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return timeoutMock | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-22 18:24:32 +02:00
										 |  |  | /** Catch error and return either value or error */ | 
					
						
							|  |  |  | export async function asResult<T>(p: Promise<T>): Promise<T | Error> { | 
					
						
							|  |  |  | 	return p.catch((e) => e) | 
					
						
							|  |  |  | } |