From 62d520ada011a67dd740a3e5bfaa73807295f80d Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 4 Sep 2018 15:48:46 -0700 Subject: [PATCH] [ftr/asyncInstance] fix error thrown for undefined provider instances (#22689) When a FTR service is created async the promise created by its provider is wrapped in a [`Proxy`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Proxy) that checks before each property access if the instance has finished initializing. This breaks if the service provider returns undefined, which is the case for the `failureDebugging` service, because our truthy check will fail and we throw the error claiming the service is uninitialized, which is probably incorrect. This PR updates the proxy to use a `Symbol` to indicate when a service instance is not available yet and throws a different error when the proxy receives any request (get, set, etc.) and the service instance is not an object, as required by the Reflect APIs. --- .../lib/providers/async_instance.js | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/functional_test_runner/lib/providers/async_instance.js b/src/functional_test_runner/lib/providers/async_instance.js index f6118656ae0c42..aead3291efc914 100644 --- a/src/functional_test_runner/lib/providers/async_instance.js +++ b/src/functional_test_runner/lib/providers/async_instance.js @@ -18,95 +18,102 @@ */ const createdInstanceProxies = new WeakSet(); +const INITIALIZING = Symbol('async instance initializing'); export const isAsyncInstance = val =>( createdInstanceProxies.has(val) ); export const createAsyncInstance = (type, name, promiseForValue) => { - let finalValue; + let instance = INITIALIZING; - const initPromise = promiseForValue.then(v => finalValue = v); + const initPromise = promiseForValue.then(v => instance = v); const initFn = () => initPromise; const assertReady = desc => { - if (!finalValue) { + if (instance === INITIALIZING) { throw new Error(` ${type} \`${desc}\` is loaded asynchronously but isn't available yet. Either await the promise returned from ${name}.init(), or move this access into a test hook like \`before()\` or \`beforeEach()\`. `); } + + if (typeof instance !== 'object') { + throw new TypeError(` + ${type} \`${desc}\` is not supported because ${name} is ${typeof instance} + `); + } }; const proxy = new Proxy({}, { apply(target, context, args) { assertReady(`${name}()`); - return Reflect.apply(finalValue, context, args); + return Reflect.apply(instance, context, args); }, construct(target, args, newTarget) { assertReady(`new ${name}()`); - return Reflect.construct(finalValue, args, newTarget); + return Reflect.construct(instance, args, newTarget); }, defineProperty(target, prop, descriptor) { assertReady(`${name}.${prop}`); - return Reflect.defineProperty(finalValue, prop, descriptor); + return Reflect.defineProperty(instance, prop, descriptor); }, deleteProperty(target, prop) { assertReady(`${name}.${prop}`); - return Reflect.deleteProperty(finalValue, prop); + return Reflect.deleteProperty(instance, prop); }, get(target, prop, receiver) { if (prop === 'init') return initFn; assertReady(`${name}.${prop}`); - return Reflect.get(finalValue, prop, receiver); + return Reflect.get(instance, prop, receiver); }, getOwnPropertyDescriptor(target, prop) { assertReady(`${name}.${prop}`); - return Reflect.getOwnPropertyDescriptor(finalValue, prop); + return Reflect.getOwnPropertyDescriptor(instance, prop); }, getPrototypeOf() { assertReady(`${name}`); - return Reflect.getPrototypeOf(finalValue); + return Reflect.getPrototypeOf(instance); }, has(target, prop) { if (prop === 'init') return true; assertReady(`${name}.${prop}`); - return Reflect.has(finalValue, prop); + return Reflect.has(instance, prop); }, isExtensible() { assertReady(`${name}`); - return Reflect.isExtensible(finalValue); + return Reflect.isExtensible(instance); }, ownKeys() { assertReady(`${name}`); - return Reflect.ownKeys(finalValue); + return Reflect.ownKeys(instance); }, preventExtensions() { assertReady(`${name}`); - return Reflect.preventExtensions(finalValue); + return Reflect.preventExtensions(instance); }, set(target, prop, value, receiver) { assertReady(`${name}.${prop}`); - return Reflect.set(finalValue, prop, value, receiver); + return Reflect.set(instance, prop, value, receiver); }, setPrototypeOf(target, prototype) { assertReady(`${name}`); - return Reflect.setPrototypeOf(finalValue, prototype); + return Reflect.setPrototypeOf(instance, prototype); } });