diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c2d2a56..b2d60931 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed ### Removed +## [2.4.0] - 2020-06-07 + +### Added +- feat(Selector): Add cleanDependenciesCache method. [Issue #79](https://github.com/data-provider/core/issues/79) + +### Changed +- chore(deps): Update dependencies + ## [2.3.0] - 2020-06-06 ### Added diff --git a/jest.config.js b/jest.config.js index ad97de03..4f40515f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -26,7 +26,7 @@ module.exports = { // The glob patterns Jest uses to detect test files testMatch: ["/test/**/*.js"], - // testMatch: ["**/test/storeManager.js"], + // testMatch: ["**/test/selector/cache-dependencies-clean.js"], // The test environment that will be used for testing testEnvironment: "node", diff --git a/package-lock.json b/package-lock.json index cc6053a8..375e7e84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@data-provider/core", - "version": "2.3.0", + "version": "2.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5198,9 +5198,9 @@ } }, "eslint": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.1.0.tgz", - "integrity": "sha512-DfS3b8iHMK5z/YLSme8K5cge168I8j8o1uiVmFCgnnjxZQbCGyraF8bMl7Ju4yfBmCuxD7shOF7eqGkcuIHfsA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.2.0.tgz", + "integrity": "sha512-B3BtEyaDKC5MlfDa2Ha8/D6DsS4fju95zs0hjS3HdGazw+LNayai38A25qMppK37wWGWNYSPOR6oYzlz5MHsRQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -5209,10 +5209,10 @@ "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", + "eslint-scope": "^5.1.0", "eslint-utils": "^2.0.0", - "eslint-visitor-keys": "^1.1.0", - "espree": "^7.0.0", + "eslint-visitor-keys": "^1.2.0", + "espree": "^7.1.0", "esquery": "^1.2.0", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", @@ -5276,6 +5276,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "eslint-visitor-keys": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz", + "integrity": "sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ==", + "dev": true + }, "globals": { "version": "12.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", @@ -5372,9 +5378,9 @@ "dev": true }, "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -5397,14 +5403,22 @@ "dev": true }, "espree": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.0.0.tgz", - "integrity": "sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.1.0.tgz", + "integrity": "sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==", "dev": true, "requires": { - "acorn": "^7.1.1", + "acorn": "^7.2.0", "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^1.2.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz", + "integrity": "sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ==", + "dev": true + } } }, "esprima": { @@ -9903,9 +9917,9 @@ } }, "rollup": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.13.1.tgz", - "integrity": "sha512-EiICynxIO1DTFmFn+/98gfaqCToK2nbjPjHJLuNvpcwc+P035VrXmJxi3JsOhqkdty+0cOEhJ26ceGTY3UPMPQ==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.14.0.tgz", + "integrity": "sha512-SUsFh2bBemQqCXOCBWRayjK3/6kNHVR8PbSKKYWIdI6e4zuGSW5B1hGVkFi40805dUrqosMLLxMuEyJMylC9YA==", "dev": true, "requires": { "fsevents": "~2.1.2" @@ -11230,9 +11244,9 @@ "dev": true }, "v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", "dev": true }, "v8-to-istanbul": { diff --git a/package.json b/package.json index aace5104..aa9b534f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@data-provider/core", - "version": "2.3.0", + "version": "2.4.0", "description": "Async Data Provider agnostic about data origins", "keywords": [ "data", @@ -68,7 +68,7 @@ "babel-eslint": "10.1.0", "babel-polyfill": "6.26.0", "coveralls": "3.0.9", - "eslint": "7.1.0", + "eslint": "7.2.0", "eslint-config-prettier": "6.11.0", "eslint-plugin-prettier": "3.1.3", "eslint-plugin-react": "7.20.0", @@ -78,7 +78,7 @@ "lint-staged": "10.2.9", "prettier": "2.0.5", "redux": "4.0.5", - "rollup": "2.13.1", + "rollup": "2.14.0", "rollup-plugin-terser": "6.1.0", "sinon": "9.0.2" }, diff --git a/sonar-project.properties b/sonar-project.properties index 953c37d8..521f7b48 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,6 +1,6 @@ sonar.organization=data-provider sonar.projectKey=data-provider-core -sonar.projectVersion=2.3.0 +sonar.projectVersion=2.4.0 sonar.sources=src,test sonar.exclusions=node_modules/** diff --git a/src/Selector.js b/src/Selector.js index 4f89551c..c2eeeb22 100644 --- a/src/Selector.js +++ b/src/Selector.js @@ -17,6 +17,10 @@ export function catchDependency(dependency, catchMethod) { return new CatchDependency(dependency, catchMethod); } +const isSelector = (objectToCheck) => { + return objectToCheck && objectToCheck instanceof SelectorBase; +}; + const isDataProvider = (objectToCheck) => { return objectToCheck && objectToCheck instanceof Provider; }; @@ -53,15 +57,19 @@ class SelectorBase extends Provider { constructor(id, options, query) { super(id, options, query); this._dependencies = options._dependencies; + this._resolvedDependencies = []; this._selector = options._selector; + this._inProgressDependencies = null; } + // Private methods + _readAllDependenciesAndSelect() { let dependenciesResults; let dependencies; let dependenciesListeners; let hasToReadAgain = false; - const inProgressDependencies = new Set(); + this._inProgressDependencies = new Set(); const inProgressListeners = []; const markToReadAgain = () => { @@ -70,6 +78,8 @@ class SelectorBase extends Provider { const removeInProgressListenerFuncs = () => { inProgressListeners.forEach((removeListener) => removeListener()); + this._resolvedDependencies = Array.from(this._inProgressDependencies); + this._inProgressDependencies = null; }; const cleanCache = () => { @@ -94,8 +104,8 @@ class SelectorBase extends Provider { return resolveResult(dependencyToRead); } dependencies.push(dependencyToRead); - if (!inProgressDependencies.has(dependencyToRead)) { - inProgressDependencies.add(dependencyToRead); + if (!this._inProgressDependencies.has(dependencyToRead)) { + this._inProgressDependencies.add(dependencyToRead); inProgressListeners.push(dependencyToRead.on(CLEAN_CACHE, markToReadAgain)); } @@ -165,6 +175,8 @@ class SelectorBase extends Provider { return readAndReturn(); } + // Overwrite Provider methods + readMethod() { return this._readAllDependenciesAndSelect(); } @@ -177,6 +189,28 @@ class SelectorBase extends Provider { } } + // Public methods + + _cleanCaches(dependencies) { + dependencies.forEach((dependency) => { + if (isSelector(dependency)) { + dependency.cleanDependenciesCache(); + } else { + dependency.cleanCache(); + } + }); + } + + cleanDependenciesCache() { + if (this._inProgressDependencies) { + this._cleanCaches(Array.from(this._inProgressDependencies)); + } else { + this._cleanCaches(this._resolvedDependencies); + } + } + + // Methods to be used for testing + get dependencies() { return this._dependencies; } @@ -186,6 +220,8 @@ class SelectorBase extends Provider { } } +// Expose a different interface for Selectors the first time, but children are built with the same interface than providers internally + class Selector extends SelectorBase { constructor(...args) { const lastIndex = args.length - 1; @@ -202,6 +238,8 @@ class Selector extends SelectorBase { super(options.id, options, undefined); } + // Overwrite Provider methods + createChildMethod(id, options, query) { return new SelectorBase(id, options, query); } diff --git a/test-e2e/browser/react-app/package-lock.json b/test-e2e/browser/react-app/package-lock.json index f3d0d677..fda2bd0d 100644 --- a/test-e2e/browser/react-app/package-lock.json +++ b/test-e2e/browser/react-app/package-lock.json @@ -13006,9 +13006,9 @@ "dev": true }, "serve": { - "version": "11.3.1", - "resolved": "https://registry.npmjs.org/serve/-/serve-11.3.1.tgz", - "integrity": "sha512-+tcx5eybTZT0scsp1PCb7HYjzBSfRF9fQIwyEU8ZYLioVuhHwywRYBBTF5WYlTXvC62eumK2bloDXAd7+9blGQ==", + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/serve/-/serve-11.3.2.tgz", + "integrity": "sha512-yKWQfI3xbj/f7X1lTBg91fXBP0FqjJ4TEi+ilES5yzH0iKJpN5LjNb1YzIfQg9Rqn4ECUS2SOf2+Kmepogoa5w==", "dev": true, "requires": { "@zeit/schemas": "2.6.0", @@ -13018,7 +13018,7 @@ "chalk": "2.4.1", "clipboardy": "1.2.3", "compression": "1.7.3", - "serve-handler": "6.1.2", + "serve-handler": "6.1.3", "update-check": "1.5.2" }, "dependencies": { @@ -13078,9 +13078,9 @@ } }, "serve-handler": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.2.tgz", - "integrity": "sha512-RFh49wX7zJmmOVDcIjiDSJnMH+ItQEvyuYLYuDBVoA/xmQSCuj+uRmk1cmBB5QQlI3qOiWKp6p4DUGY+Z5AB2A==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", + "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", "dev": true, "requires": { "bytes": "3.0.0", diff --git a/test-e2e/browser/react-app/package.json b/test-e2e/browser/react-app/package.json index 4d2c1889..d6300d01 100644 --- a/test-e2e/browser/react-app/package.json +++ b/test-e2e/browser/react-app/package.json @@ -32,7 +32,7 @@ "devDependencies": { "react-scripts": "3.4.1", "react-app-rewired": "2.1.6", - "serve": "11.3.1", + "serve": "11.3.2", "start-server-and-test": "1.11.0" } } diff --git a/test/selector/cache-dependencies-clean.js b/test/selector/cache-dependencies-clean.js new file mode 100644 index 00000000..786c5d88 --- /dev/null +++ b/test/selector/cache-dependencies-clean.js @@ -0,0 +1,351 @@ +/* +Copyright 2020 Javier Brea + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +*/ + +const sinon = require("sinon"); + +const { Provider, Selector, providers, catchDependency } = require("../../src/index"); + +describe("Selector when cleanDependenciesCache method is called", () => { + let sandbox; + let spies; + let TestProvider; + let dependency1; + let dependency2; + let dependency3; + let dependency4; + let selector; + let getProviderResult; + let resolved; + let results; + let timeouts; + let hasToThrow; + + beforeEach(() => { + hasToThrow = false; + sandbox = sinon.createSandbox(); + results = { + dependency1: [], + dependency2: [], + dependency3: [], + }; + resolved = { + dependency1: -1, + dependency2: -1, + dependency3: -1, + }; + timeouts = { + dependency1: 200, + dependency2: 1000, + dependency3: 1000, + }; + + getProviderResult = (dependencyId) => { + resolved[dependencyId]++; + return results[dependencyId][resolved[dependencyId]]; + }; + + spies = { + dependenciesRead: { + dependency1: sandbox.spy(), + dependency2: sandbox.spy(), + dependency3: sandbox.spy(), + }, + selectorRead: sinon.spy(), + }; + + TestProvider = class extends Provider { + readMethod() { + return new Promise((resolve, reject) => { + spies.dependenciesRead[this.id](); + setTimeout(() => { + if (!hasToThrow) { + // console.log("Resolving", this.id); + resolve(getProviderResult(this.id)); + } else { + // console.log("Rejecting", this.id); + reject(hasToThrow); + } + }, timeouts[this.id] || 50); + }); + } + }; + + dependency1 = new TestProvider("dependency1"); + dependency2 = new TestProvider("dependency2"); + dependency3 = new TestProvider("dependency3"); + dependency4 = new Selector(dependency3, (dependencyResult) => { + return dependencyResult; + }); + + selector = new Selector( + dependency1, + dependency2, + dependency4, + (dependency1Result, dependency2Result, dependency3Result) => { + spies.selectorRead(); + return [dependency1Result, dependency2Result, dependency3Result]; + } + ); + }); + + afterEach(() => { + sandbox.restore(); + providers.clear(); + }); + + it("should return last data returned by all dependencies when method is called", async () => { + expect.assertions(2); + results = { + dependency1: ["foo", "foo2"], + dependency2: ["foo3", "foo4"], + dependency3: ["foo5", "foo6"], + }; + const result = await selector.read(); + expect(result).toEqual(["foo", "foo3", "foo5"]); + selector.cleanDependenciesCache(); + const result2 = await selector.read(); + expect(result2).toEqual(["foo2", "foo4", "foo6"]); + }); + + it("should work with dependencies as functions", async () => { + expect.assertions(2); + selector = new Selector( + () => dependency1, + () => dependency2, + () => dependency4, + (dependency1Result, dependency2Result, dependency3Result) => { + spies.selectorRead(); + return [dependency1Result, dependency2Result, dependency3Result]; + } + ); + results = { + dependency1: ["foo", "foo2"], + dependency2: ["foo3", "foo4"], + dependency3: ["foo5", "foo6"], + }; + const result = await selector.read(); + expect(result).toEqual(["foo", "foo3", "foo5"]); + selector.cleanDependenciesCache(); + const result2 = await selector.read(); + expect(result2).toEqual(["foo2", "foo4", "foo6"]); + }); + + it("should ignore promise dependencies", async () => { + expect.assertions(2); + selector = new Selector( + () => dependency1, + () => dependency2, + () => dependency4, + new Promise((resolve) => resolve(5)), + (dependency1Result, dependency2Result, dependency3Result, dependency4Result) => { + spies.selectorRead(); + return [dependency1Result, dependency2Result, dependency3Result, dependency4Result]; + } + ); + results = { + dependency1: ["foo", "foo2"], + dependency2: ["foo3", "foo4"], + dependency3: ["foo5", "foo6"], + }; + const result = await selector.read(); + expect(result).toEqual(["foo", "foo3", "foo5", 5]); + selector.cleanDependenciesCache(); + const result2 = await selector.read(); + expect(result2).toEqual(["foo2", "foo4", "foo6", 5]); + }); + + it("should ignore value dependencies", async () => { + expect.assertions(2); + selector = new Selector( + () => dependency1, + () => dependency2, + () => dependency4, + 5, + (dependency1Result, dependency2Result, dependency3Result, dependency4Result) => { + spies.selectorRead(); + return [dependency1Result, dependency2Result, dependency3Result, dependency4Result]; + } + ); + results = { + dependency1: ["foo", "foo2"], + dependency2: ["foo3", "foo4"], + dependency3: ["foo5", "foo6"], + }; + const result = await selector.read(); + expect(result).toEqual(["foo", "foo3", "foo5", 5]); + selector.cleanDependenciesCache(); + const result2 = await selector.read(); + expect(result2).toEqual(["foo2", "foo4", "foo6", 5]); + }); + + it("should return last data returned by all dependencies when method is called while reading", () => { + results = { + dependency1: ["foo", "foo2"], + dependency2: ["foo3", "foo4"], + dependency3: ["foo5", "foo6"], + }; + expect.assertions(1); + const promise = selector.read().then(async (result) => { + expect(result).toEqual(["foo2", "foo4", "foo6"]); + }); + setTimeout(() => { + selector.cleanDependenciesCache(); + }, 1400); + return promise; + }); + + it("should return last data returned by all dependencies, even when a dependency cache throwed an error", async () => { + results = { + dependency1: ["foo"], + dependency2: ["foo3", "foo4"], + dependency3: ["foo4", "foo5"], + }; + const error = new Error(); + expect.assertions(1); + const promise = selector.read().catch((err) => { + expect(err).toBe(error); + }); + setTimeout(() => { + hasToThrow = error; + selector.cleanDependenciesCache(); + }, 400); + await promise; + }); + + it("should call to clean cache once", async () => { + sandbox.spy(selector, "cleanCache"); + await selector.read(); + selector.cleanDependenciesCache(); + await selector.read(); + expect(selector.cleanCache.callCount).toEqual(1); + }); + + it("should not call to clean until dependencies have been completely read", async () => { + sandbox.spy(selector, "cleanCache"); + selector.read(); + selector.cleanDependenciesCache(); + await selector.read(); + expect(selector.cleanCache.callCount).toEqual(0); + }); + + it("should not call to clean until dependencies have been completely read even when method is called while reading", async () => { + sandbox.spy(selector, "cleanCache"); + let promise = selector.read().then(() => { + expect(selector.cleanCache.callCount).toEqual(0); + }); + selector.cleanDependenciesCache(); + setTimeout(() => { + selector.cleanDependenciesCache(); + }, 600); + selector.cleanDependenciesCache(); + return promise; + }); + + it("should stop reading dependencies and start again when method is called", async () => { + expect.assertions(3); + sandbox.spy(dependency1, "read"); + sandbox.spy(dependency2, "read"); + sandbox.spy(dependency3, "read"); + let dependency4HasToThrow = true; + timeouts = { + dependency1: 200, + dependency2: 200, + dependency3: 200, + }; + const TestProvider2 = class extends Provider { + readMethod() { + return new Promise((resolve, reject) => { + setTimeout(() => { + if (dependency4HasToThrow) { + reject(new Error()); + } else { + resolve(); + } + }, 1000); + }); + } + }; + const dependency5 = new TestProvider2("dependency-5"); + selector = new Selector( + catchDependency(dependency5, () => { + return [dependency1, dependency2, dependency3]; + }), + (result) => { + spies.selectorRead(); + return result; + } + ); + const promise = selector.read().then(() => { + expect(dependency1.read.callCount).toEqual(1); + expect(dependency2.read.callCount).toEqual(1); + expect(dependency3.read.callCount).toEqual(1); + }); + setTimeout(() => { + selector.cleanDependenciesCache(); + }, 300); + return promise; + }); + + it("should stop reading dependencies and start again", async () => { + expect.assertions(3); + sandbox.spy(dependency1, "read"); + sandbox.spy(dependency2, "read"); + sandbox.spy(dependency3, "read"); + timeouts = { + dependency1: 1000, + dependency2: 1000, + dependency3: 1000, + }; + let promise = selector.read().then(() => { + expect(dependency1.read.callCount).toEqual(2); + expect(dependency2.read.callCount).toEqual(1); // cleanCache was called during dependency1 read, so 2 was not started + expect(dependency3.read.callCount).toEqual(1); + }); + setTimeout(() => { + selector.cleanDependenciesCache(); + }, 200); + return promise; + }); + + it("should return last data returned by all dependencies, even when method is called while reading and dependencies are parallel", async () => { + expect.assertions(3); + let resolveTest; + let testPromise = new Promise((resolve) => { + resolveTest = resolve; + }); + selector = new Selector( + [dependency1, dependency2, dependency3], + ([dependency1Result, dependency2Result, dependency3Result]) => { + spies.selectorRead(); + return [dependency1Result, dependency2Result, dependency3Result]; + } + ); + results = { + dependency1: ["foo", "foo2"], + dependency2: ["foo3", "foo4"], + dependency3: ["foo5", "foo6"], + }; + const result = await selector.read(); + expect(result).toEqual(["foo", "foo3", "foo5"]); + selector.on("cleanCache", () => { + selector.read().then((selectorResult) => { + expect(selectorResult).toEqual(["foo2", "foo4", "foo6"]); + }); + setTimeout(() => { + selector.read().then((selectorResult) => { + expect(selectorResult).toEqual(["foo2", "foo4", "foo6"]); + resolveTest(); + }); + }, 1400); + }); + selector.cleanDependenciesCache(); + return testPromise; + }); +});