From d8ac56fb24eb5477c3153dbfa3e6a52b3accfd9d Mon Sep 17 00:00:00 2001 From: Avi Vahl Date: Tue, 28 Oct 2025 03:13:25 +0200 Subject: [PATCH] refactor: use native promises and drop q migrated away from q, and used native promises. unhandled rejections in unit tests now cause the test runner to fail. ensured all rejections are ignored with no-op fn. found and fixed several broken tests that did not report failures correctly. fixed wait() not used correctly outside .then, as it's returning a higher order fn rather than the promise (to chain values in .then callbacks) implemented a tiny test polyfill for Promise.allSettled to support older Node versions. worked around a timezone issue and a test failing when running it in ~1am locally, heh. added ability to use .only/skip on the test proxy of mocha's describe fn --- lib/api_client/execute_request.js | 3 +- lib/uploader.js | 3 +- lib/utils/index.js | 16 + package.json | 3 +- test/integration/api/admin/api_spec.js | 183 ++++++----- test/integration/api/admin/config_spec.js | 9 +- .../integration/api/admin/folders_api_spec.js | 5 +- .../api/admin/related_assets_spec.js | 33 +- .../api/admin/structured_metadata_spec.js | 63 ++-- test/integration/api/analysis/analyze_spec.js | 11 +- .../authorization/oAuth_authorization_spec.js | 43 +-- test/integration/api/search/search_spec.js | 14 +- .../api/search/visual_search_spec.js | 13 +- test/integration/api/uploader/archivespec.js | 7 +- .../api/uploader/auto_chaptering_spec.js | 9 +- .../api/uploader/auto_transcription_spec.js | 17 +- .../api/uploader/custom_region_spec.js | 25 +- .../api/uploader/slideshow_spec.js | 4 +- .../integration/api/uploader/uploader_spec.js | 305 +++++++++--------- test/integration/streaming_profiles_spec.js | 6 +- test/spechelper.js | 27 +- test/testUtils/helpers/allSettled.js | 8 + test/testUtils/helpers/retry.js | 2 +- test/testUtils/helpers/wait.js | 4 +- .../reusableTests/api/toAcceptNextCursor.js | 7 +- .../reusableTests/api/toBeACursor.js | 13 +- test/testUtils/suite.js | 3 + test/unit/api_restore_spec.js | 13 +- 28 files changed, 432 insertions(+), 417 deletions(-) create mode 100644 test/testUtils/helpers/allSettled.js diff --git a/lib/api_client/execute_request.js b/lib/api_client/execute_request.js index 9c0418d9..cd53969d 100644 --- a/lib/api_client/execute_request.js +++ b/lib/api_client/execute_request.js @@ -2,7 +2,6 @@ const config = require("../config"); const https = /^http:/.test(config().upload_prefix) ? require('http') : require('https'); const querystring = require("querystring"); -const Q = require('q'); const url = require('url'); const utils = require("../utils"); const ensureOption = require('../utils/ensureOption').defaults(config()); @@ -13,7 +12,7 @@ const agent = config.api_proxy ? new https.Agent(config.api_proxy) : null; function execute_request(method, params, auth, api_url, callback, options = {}) { method = method.toUpperCase(); - const deferred = Q.defer(); + const deferred = utils.deferredPromise(); let query_params, handle_response; // declare to user later let key = auth.key; diff --git a/lib/uploader.js b/lib/uploader.js index 93318f02..627267c9 100644 --- a/lib/uploader.js +++ b/lib/uploader.js @@ -1,6 +1,5 @@ const fs = require('fs'); const { extname, basename } = require('path'); -const Q = require('q'); const Writable = require("stream").Writable; const urlLib = require('url'); @@ -466,7 +465,7 @@ function call_api(action, callback, options, get_params) { const USE_PROMISES = !options.disable_promises; - let deferred = Q.defer(); + const deferred = utils.deferredPromise(); if (options == null) { options = {}; } diff --git a/lib/utils/index.js b/lib/utils/index.js index e775a14a..ab3e83b3 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -1652,6 +1652,22 @@ function jsonArrayParam(data, modifier) { */ exports.NOP = function () { }; + +function deferredPromise() { + let resolve, reject + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + return { + resolve, + reject, + promise + }; +} + +exports.deferredPromise = deferredPromise; + exports.generate_auth_token = generate_auth_token; exports.getUserAgent = getUserAgent; exports.build_upload_params = build_upload_params; diff --git a/package.json b/package.json index 4d505c8c..5179e07c 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,7 @@ }, "main": "cloudinary.js", "dependencies": { - "lodash": "^4.17.21", - "q": "^1.5.1" + "lodash": "^4.17.21" }, "devDependencies": { "@types/expect.js": "^0.3.29", diff --git a/test/integration/api/admin/api_spec.js b/test/integration/api/admin/api_spec.js index 28c9ad84..11934575 100644 --- a/test/integration/api/admin/api_spec.js +++ b/test/integration/api/admin/api_spec.js @@ -4,7 +4,6 @@ const formatDate = require("date-fns").format; const subDate = require("date-fns").sub; const https = require('https'); const ClientRequest = require('_http_client').ClientRequest; -const Q = require('q'); const cloudinary = require("../../../../cloudinary"); const helper = require("../../../spechelper"); const describe = require('../../../testUtils/suite'); @@ -19,7 +18,8 @@ const {shouldTestFeature} = require("../../../spechelper"); const API_V2 = cloudinary.v2.api; const DYNAMIC_FOLDERS = helper.DYNAMIC_FOLDERS; const assert = require('assert'); -const {only} = require("../../../../lib/utils"); +const {only, NOP} = require("../../../../lib/utils"); +const allSettled = require('../../../testUtils/helpers/allSettled'); const { TIMEOUT, @@ -103,7 +103,7 @@ describe("api", function () { default_value: METADATA_DEFAULT_VALUE }); - await Q.all([ + await Promise.all([ uploadImage({ public_id: PUBLIC_ID, tags: UPLOAD_TAGS, @@ -139,7 +139,7 @@ describe("api", function () { if (!(config.api_key && config.api_secret)) { expect().fail("Missing key and secret. Please set CLOUDINARY_URL."); } - return Q.allSettled([ + return allSettled([ cloudinary.v2.api.delete_metadata_field(METADATA_EXTERNAL_ID), cloudinary.v2.api.delete_resources_by_tag(TEST_TAG), cloudinary.v2.api.delete_upload_preset(API_TEST_UPLOAD_PRESET1), @@ -351,9 +351,9 @@ describe("api", function () { expect(result.resources.map(e => e.context.custom.key)).to.contain("value"); }); }); - it("should allow listing resources specifying direction", function () { + it("should allow listing resources specifying direction", async function () { this.timeout(TIMEOUT.LONG); - Q.all( + await Promise.all([ cloudinary.v2.api.resources_by_tag(TEST_TAG, { type: "upload", max_results: 500, @@ -364,15 +364,15 @@ describe("api", function () { max_results: 500, direction: "desc" }) - ).then(([resultAsc, resultDesc]) => [ + ]).then(([resultAsc, resultDesc]) => [ resultAsc.resources.map(r => r.public_id), resultDesc.resources.map(r => r.public_id) ]).then(([asc, desc]) => expect(asc.reverse()).to.eql(desc)); }); it("should allow listing resources by start_at", function () { let start_at = new Date().toString(); - helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.resources({ + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.resources({ type: "upload", start_at: start_at, direction: "asc" @@ -411,9 +411,9 @@ describe("api", function () { }); describe("derived pagination", function () { it("should send the derived_next_cursor to the server", function () { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.resource(PUBLIC_ID, {derived_next_cursor: 'aaa'}); - return sinon.assert.calledWith( + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.resource(PUBLIC_ID, {derived_next_cursor: 'aaa'}); + sinon.assert.calledWith( requestSpy, sinon.match(sinon.match({ query: sinon.match('derived_next_cursor=aaa') }, 'derived_next_cursor=aaa'))); @@ -421,9 +421,9 @@ describe("api", function () { }); }); it("should send `accessibility_analysis` param to the server", function () { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.resource(PUBLIC_ID, {accessibility_analysis: true}); - return sinon.assert.calledWith(requestSpy, sinon.match({ + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.resource(PUBLIC_ID, {accessibility_analysis: true}); + sinon.assert.calledWith(requestSpy, sinon.match({ query: sinon.match(helper.apiParamMatcher("accessibility_analysis", "true")) })); }); @@ -533,7 +533,7 @@ describe("api", function () { }); it("should allow deleting derived resources by transformations", function () { this.timeout(TIMEOUT.LARGE); - return Q.all([ + return Promise.all([ uploadImage({ public_id: PUBLIC_ID_1, tags: UPLOAD_TAGS, @@ -592,7 +592,7 @@ describe("api", function () { () => cloudinary.v2.api.resource(PUBLIC_ID_3) ).then(function (resource) { expect(resource).not.to.eql(void 0); - console.log(resource); + // console.log(resource); return cloudinary.v2.api.delete_resources_by_asset_ids([resource.asset_id]); }).then( () => cloudinary.v2.api.resource(PUBLIC_ID_3) @@ -692,14 +692,11 @@ describe("api", function () { callReusableTest("a list with a cursor", cloudinary.v2.api.transformations); transformationName = "api_test_transformation3" + UNIQUE_JOB_SUFFIX_ID; after(function () { - return Q.allSettled( - [ - cloudinary.v2.api.delete_transformation(transformationName), - cloudinary.v2.api.delete_transformation(NAMED_TRANSFORMATION), - cloudinary.v2.api.delete_transformation(NAMED_TRANSFORMATION2) - ] - ).finally(function () { - }); + return allSettled([ + cloudinary.v2.api.delete_transformation(transformationName), + cloudinary.v2.api.delete_transformation(NAMED_TRANSFORMATION), + cloudinary.v2.api.delete_transformation(NAMED_TRANSFORMATION2) + ]); }); it("should allow listing transformations", function () { this.timeout(TIMEOUT.MEDIUM); @@ -782,11 +779,11 @@ describe("api", function () { }); }); it("should allow listing of named transformations", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.api.transformations({ + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.api.transformations({ named: true }); - return sinon.assert.calledWith(requestSpy, sinon.match({ + sinon.assert.calledWith(requestSpy, sinon.match({ query: sinon.match('named=true') }, "named=true")); }); @@ -837,43 +834,43 @@ describe("api", function () { describe("upload_preset", function () { callReusableTest("a list with a cursor", cloudinary.v2.api.upload_presets); it("should allow listing upload_presets", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.api.upload_presets(); - return sinon.assert.calledWith(requestSpy, sinon.match({ + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.api.upload_presets(); + sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(/.*\/upload_presets$/) }, "upload_presets")); }); }); it("should allow getting a single upload_preset", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.api.upload_preset(API_TEST_UPLOAD_PRESET1); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.api.upload_preset(API_TEST_UPLOAD_PRESET1).catch(NOP); var expectedPath = "/.*\/upload_presets/" + API_TEST_UPLOAD_PRESET1 + "$"; - return sinon.assert.calledWith(requestSpy, sinon.match({ + sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), method: sinon.match("GET") })); }); }); it("should allow deleting upload_presets", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.api.delete_upload_preset(API_TEST_UPLOAD_PRESET2); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.api.delete_upload_preset(API_TEST_UPLOAD_PRESET2).catch(NOP); var expectedPath = "/.*\/upload_presets/" + API_TEST_UPLOAD_PRESET2 + "$"; - return sinon.assert.calledWith(requestSpy, sinon.match({ + sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), method: sinon.match("DELETE") })) }); }); it("should allow updating upload_presets", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.api.update_upload_preset(API_TEST_UPLOAD_PRESET3, + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.api.update_upload_preset(API_TEST_UPLOAD_PRESET3, { colors: true, unsigned: true, disallow_public_id: true, live: true, eval: TEST_EVAL_STR - }); + }).catch(NOP); var expectedPath = "/.*\/upload_presets/" + API_TEST_UPLOAD_PRESET3 + "$"; sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), @@ -887,15 +884,15 @@ describe("api", function () { }); }); it("should allow creating upload_presets", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.api.create_upload_preset({ + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.api.create_upload_preset({ folder: "upload_folder", unsigned: true, tags: UPLOAD_TAGS, live: true, eval: TEST_EVAL_STR }).then((preset) => { - cloudinary.v2.api.delete_upload_preset(preset.name).catch((err) => { + return cloudinary.v2.api.delete_upload_preset(preset.name).catch((err) => { console.log(err); // we don't fail the test if the delete fails }); @@ -916,8 +913,8 @@ describe("api", function () { }); }); it("should return usage values for a specific date", function () { - const yesterday = formatDate(subDate(new Date(), {days: 1}), "dd-MM-yyyy"); - return cloudinary.v2.api.usage({date: yesterday}) + const twoDaysAgo = formatDate(subDate(new Date(), {days: 2}), "dd-MM-yyyy"); + return cloudinary.v2.api.usage({date: twoDaysAgo}) .then(usage => { expect(usage).to.be.an("object"); expect(usage).to.have.keys("plan", "last_updated", "transformations", "objects", "bandwidth", "storage", "requests", "resources", "derived_resources", "media_limits"); @@ -929,11 +926,11 @@ describe("api", function () { callReusableTest("accepts next_cursor", cloudinary.v2.api.delete_all_resources); describe("keep_original: yes", function () { it("should allow deleting all derived resources", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { let options = { keep_original: true }; - cloudinary.v2.api.delete_all_resources(options); + await cloudinary.v2.api.delete_all_resources(options).catch(NOP); sinon.assert.calledWith(requestSpy, sinon.match(arg => new RegExp("/resources/image/upload$").test(arg.pathname), "/resources/image/upload")); sinon.assert.calledWith(requestSpy, sinon.match(arg => arg.method === "DELETE", "DELETE")); sinon.assert.calledWith(writeSpy, sinon.match(helper.apiParamMatcher('keep_original', 'true'), "keep_original=true")); @@ -1050,8 +1047,8 @@ describe("api", function () { const mocked = helper.mockTest(); const qualityValues = ["auto:advanced", "auto:best", "80:420", "none"]; qualityValues.forEach(quality => { - it("should support '" + quality + "' in update", function () { - cloudinary.v2.api.update("sample", {quality_override: quality}); + it("should support '" + quality + "' in update", async function () { + await cloudinary.v2.api.update("sample", {quality_override: quality}).catch(NOP); sinon.assert.calledWith(mocked.write, sinon.match(helper.apiParamMatcher("quality_override", quality))); }); }); @@ -1152,40 +1149,43 @@ describe("api", function () { tags: [...UPLOAD_TAGS, 'access_control_test'] }; it("should allow the user to define ACL in the update parameters2", function () { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { options.access_control = [acl]; - cloudinary.v2.api.update("id", options); - return sinon.assert.calledWith( + await cloudinary.v2.api.update("id", options).catch(NOP); + sinon.assert.calledWith( writeSpy, sinon.match(arg => helper.apiParamMatcher('access_control', `[${acl_string}]`)(arg)) ); }); }); }); }); - it("should support listing by moderation kind and value", function () { - callReusableTest("a list with a cursor", cloudinary.v2.api.resources_by_moderation, "manual", "approved"); - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => ["approved", "pending", "rejected"].forEach((stat) => { - var status, status2; - status = stat; - status2 = status; - requestSpy.resetHistory(); - cloudinary.v2.api.resources_by_moderation("manual", status2, { - moderations: true - }); - sinon.assert.calledWith(requestSpy, sinon.match( - arg => new RegExp(`/resources/image/moderations/manual/${status2}$`).test(arg != null ? arg.pathname : void 0), `/resources/image/moderations/manual/${status}` - )); - sinon.assert.calledWith(requestSpy, sinon.match( - arg => (arg != null ? arg.query : void 0) === "moderations=true", "moderations=true" - )); - })); + callReusableTest("a list with a cursor", cloudinary.v2.api.resources_by_moderation, "manual", "approved"); + it("should support listing by moderation kind and value", async function () { + for (const stat of ["approved", "pending", "rejected"]) { + // eslint-disable-next-line no-await-in-loop + await helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + var status, status2; + status = stat; + status2 = status; + requestSpy.resetHistory(); + await cloudinary.v2.api.resources_by_moderation("manual", status2, { + moderations: true + }); + sinon.assert.calledWith(requestSpy, sinon.match( + arg => new RegExp(`/resources/image/moderations/manual/${status2}$`).test(arg != null ? arg.pathname : void 0), `/resources/image/moderations/manual/${status}` + )); + sinon.assert.calledWith(requestSpy, sinon.match( + arg => (arg != null ? arg.query : void 0) === "moderations=true", "moderations=true" + )); + }); + } }); describe("folders", function () { // For this test to work, "Auto-create folders" should be enabled in the Upload Settings. // Replace `it` with `it.skip` below if you want to disable it. it("should list folders in cloudinary", function () { this.timeout(TIMEOUT.LONG); - return Q.all([ + return Promise.all([ uploadImage({ public_id: 'test_folder1/item', tags: UPLOAD_TAGS @@ -1207,8 +1207,8 @@ describe("api", function () { tags: UPLOAD_TAGS }) ]).then(wait(TIMEOUT.SHORT)) - .then(function (results) { - return Q.all([cloudinary.v2.api.root_folders(), cloudinary.v2.api.sub_folders('test_folder1')]); + .then(function () { + return Promise.all([cloudinary.v2.api.root_folders(), cloudinary.v2.api.sub_folders('test_folder1')]); }).then(function (results) { var folder, root, root_folders, sub_1; root = results[0]; @@ -1228,7 +1228,7 @@ describe("api", function () { expect(sub_1.folders[0].path).to.eql('test_folder1/test_subfolder1'); expect(sub_1.folders[1].path).to.eql('test_folder1/test_subfolder2'); return cloudinary.v2.api.sub_folders('test_folder_not_exists'); - }).then(wait(TIMEOUT.LONG)).then((result) => { + }).then(wait(TIMEOUT.LONG)).then(() => { console.log('error test_folder_not_exists should not pass to "then" handler but "catch"'); expect().fail('error test_folder_not_exists should not pass to "then" handler but "catch"'); }).catch(({error}) => expect(error.message).to.eql('Can\'t find folder with path test_folder_not_exists')); @@ -1237,8 +1237,8 @@ describe("api", function () { it("should create a new folder", function () { const folderPath = `${UNIQUE_TEST_FOLDER}`; const expectedPath = `folders/${folderPath}`; - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.api.create_folder(folderPath); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.api.create_folder(folderPath); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(expectedPath), method: sinon.match("POST") @@ -1253,7 +1253,7 @@ describe("api", function () { return uploadImage({ folder: folderPath, tags: UPLOAD_TAGS - }).delay(2 * 1000).then(function () { + }).then(wait(2 * 1000)).then(function () { return cloudinary.v2.api.delete_resources_by_prefix(folderPath) .then(() => cloudinary.v2.api.sub_folders(folderPath).then(folder => { expect(folder).not.to.be(null); @@ -1266,7 +1266,7 @@ describe("api", function () { this.timeout(TIMEOUT.MEDIUM); return cloudinary.v2.api.delete_folder( folderPath - ).delay(2 * 1000).then(() => cloudinary.v2.api.sub_folders(folderPath) + ).then(wait(2 * 1000)).then(() => cloudinary.v2.api.sub_folders(folderPath) ).then(() => expect().fail() ).catch(({error}) => expect(error.message).to.contain("Can't find folder with path")); }); @@ -1323,16 +1323,13 @@ describe("api", function () { }); }); - it('should update asset_folder with unique_display_name', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - uploadImage().then(result => { - cloudinary.v2.api.update(result.public_id, { - unique_display_name: true - }) - return sinon.assert.calledWith(requestSpy, sinon.match({ - query: sinon.match(helper.apiParamMatcher("unique_display_name", "true")) - })); - }); + it('should update asset_folder with unique_display_name', async () => { + const result = await uploadImage() + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.update(result.public_id, { + unique_display_name: true + }) + sinon.assert.calledWith(writeSpy, sinon.match(helper.apiParamMatcher('unique_display_name', true, "unique_display_name=true"))); }); }); @@ -1508,24 +1505,24 @@ describe("api", function () { }); describe("proxy support", function () { const mocked = helper.mockTest(); - it("should support proxy for api calls", function () { + it("should support proxy for api calls", async function () { cloudinary.config({api_proxy: "https://myuser:mypass@example.com"}); - cloudinary.v2.api.resources({}); + await cloudinary.v2.api.resources({}).catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match( arg => arg.agent instanceof https.Agent )); }); - it("should prioritize custom agent", function () { + it("should prioritize custom agent", async function () { cloudinary.config({api_proxy: "https://myuser:mypass@example.com"}); const custom_agent = https.Agent() - cloudinary.v2.api.resources({agent: custom_agent}); + await cloudinary.v2.api.resources({agent: custom_agent}).catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match( arg => arg.agent === custom_agent )); }); - it("should support api_proxy as options key", function () { + it("should support api_proxy as options key", async function () { cloudinary.config({}); - cloudinary.v2.api.resources({api_proxy: "https://myuser:mypass@example.com"}); + await cloudinary.v2.api.resources({api_proxy: "https://myuser:mypass@example.com"}).catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match( arg => arg.agent instanceof https.Agent )); diff --git a/test/integration/api/admin/config_spec.js b/test/integration/api/admin/config_spec.js index b0c2ecc8..b99d0511 100644 --- a/test/integration/api/admin/config_spec.js +++ b/test/integration/api/admin/config_spec.js @@ -2,6 +2,7 @@ const sinon = require('sinon'); const cloudinary = require('../../../../lib/cloudinary'); const api_http = require("https"); +const { NOP } = require('../../../../lib/utils'); const ClientRequest = require('_http_client').ClientRequest; describe('Admin API - Config', () => { @@ -20,8 +21,8 @@ describe('Admin API - Config', () => { }); describe('config', () => { - it('should send a request to config endpoint', () => { - cloudinary.v2.api.config(); + it('should send a request to config endpoint', async () => { + await cloudinary.v2.api.config().catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match({ pathname: sinon.match('config'), @@ -30,8 +31,8 @@ describe('Admin API - Config', () => { })); }); - it('should send a request to config endpoint with optional parameters', () => { - cloudinary.v2.api.config({ settings: true }); + it('should send a request to config endpoint with optional parameters', async () => { + await cloudinary.v2.api.config({ settings: true }).catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match({ pathname: sinon.match('config'), diff --git a/test/integration/api/admin/folders_api_spec.js b/test/integration/api/admin/folders_api_spec.js index c514ddde..e1e7b1fb 100644 --- a/test/integration/api/admin/folders_api_spec.js +++ b/test/integration/api/admin/folders_api_spec.js @@ -5,6 +5,7 @@ const cloudinary = require('../../../../lib/cloudinary'); const createTestConfig = require('../../../testUtils/createTestConfig'); const helper = require('../../../spechelper'); const api_http = require("https"); +const { NOP } = require('../../../../lib/utils'); const ClientRequest = require('_http_client').ClientRequest; describe('Admin API - Folders', () => { @@ -23,8 +24,8 @@ describe('Admin API - Folders', () => { }); describe('rename_folder', () => { - it('should send a request to update folder endpoint with correct parameters', () => { - cloudinary.v2.api.rename_folder('old/path', 'new/path'); + it('should send a request to update folder endpoint with correct parameters', async () => { + await cloudinary.v2.api.rename_folder('old/path', 'new/path').catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match({ pathname: sinon.match('old%2Fpath'), diff --git a/test/integration/api/admin/related_assets_spec.js b/test/integration/api/admin/related_assets_spec.js index 9f1ba589..f734f3c0 100644 --- a/test/integration/api/admin/related_assets_spec.js +++ b/test/integration/api/admin/related_assets_spec.js @@ -5,6 +5,7 @@ const { deepStrictEqual } = require('assert'); const {TEST_CLOUD_NAME} = require('../../../testUtils/testConstants'); +const { NOP } = require('../../../../lib/utils'); describe('Asset relations API', () => { const testPublicId = 'test-public-id'; @@ -15,8 +16,8 @@ describe('Asset relations API', () => { describe('when creating new relation', () => { describe('using public id', () => { it('should allow passing a single public id to create a relation', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.add_related_assets(testPublicId, singleRelatedPublicId); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.add_related_assets(testPublicId, singleRelatedPublicId).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'POST'); @@ -27,8 +28,8 @@ describe('Asset relations API', () => { }); it('should allow passing multiple public ids to create a relation', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.add_related_assets(testPublicId, multipleRelatedPublicId); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.add_related_assets(testPublicId, multipleRelatedPublicId).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'POST'); @@ -41,8 +42,8 @@ describe('Asset relations API', () => { describe('using asset id', () => { it('should allow passing a single public id to create a relation', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.add_related_assets_by_asset_id(testAssetId, singleRelatedPublicId); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.add_related_assets_by_asset_id(testAssetId, singleRelatedPublicId).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'POST'); @@ -53,8 +54,8 @@ describe('Asset relations API', () => { }); it('should allow passing multiple public ids to create a relation', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.add_related_assets_by_asset_id(testAssetId, multipleRelatedPublicId); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.add_related_assets_by_asset_id(testAssetId, multipleRelatedPublicId).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'POST'); @@ -69,8 +70,8 @@ describe('Asset relations API', () => { describe('when deleting existing relation', () => { describe('using public id', () => { it('should allow passing a single public id to delete a relation', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.delete_related_assets(testPublicId, singleRelatedPublicId); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.delete_related_assets(testPublicId, singleRelatedPublicId).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'DELETE'); @@ -81,8 +82,8 @@ describe('Asset relations API', () => { }); it('should allow passing multiple public ids to delete a relation', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.delete_related_assets(testPublicId, multipleRelatedPublicId); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.delete_related_assets(testPublicId, multipleRelatedPublicId).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'DELETE'); @@ -95,8 +96,8 @@ describe('Asset relations API', () => { describe('and using asset id', () => { it('should allow passing a single public id to delete a relation', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.delete_related_assets_by_asset_id(testAssetId, singleRelatedPublicId); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.delete_related_assets_by_asset_id(testAssetId, singleRelatedPublicId).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'DELETE'); @@ -107,8 +108,8 @@ describe('Asset relations API', () => { }); it('should allow passing multiple public ids to delete a relation', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.delete_related_assets_by_asset_id(testAssetId, multipleRelatedPublicId); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.delete_related_assets_by_asset_id(testAssetId, multipleRelatedPublicId).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'DELETE'); diff --git a/test/integration/api/admin/structured_metadata_spec.js b/test/integration/api/admin/structured_metadata_spec.js index 587d5cc6..4cc48690 100644 --- a/test/integration/api/admin/structured_metadata_spec.js +++ b/test/integration/api/admin/structured_metadata_spec.js @@ -1,9 +1,10 @@ const assert = require('assert'); -const Q = require('q'); const sinon = require('sinon'); const cloudinary = require("../../../../cloudinary"); const helper = require("../../../spechelper"); const TIMEOUT = require('../../../testUtils/testConstants').TIMEOUT; +const { NOP } = require('../../../../lib/utils'); +const allSettled = require('../../../testUtils/helpers/allSettled'); const TEST_ID = Date.now(); @@ -87,7 +88,7 @@ function createMetadataFieldForTest(field) { if (!field.label) { field.label = field.external_id; } - return api.add_metadata_field(field); + return api.add_metadata_field(field).catch(NOP); } describe("structured metadata api", function () { @@ -95,25 +96,23 @@ describe("structured metadata api", function () { before(function () { // Create the metadata fields required for the tests - return Q.allSettled( + return allSettled( metadata_fields_to_create.map(field => createMetadataFieldForTest(field)) - ).finally(function () { - }); + ); }); after(function () { // Delete all metadata fields created during testing - return Q.allSettled( + return allSettled( metadata_fields_external_ids.map(field => api.delete_metadata_field(field)) - ).finally(function () { - }); + ); }); describe("list_metadata_fields", function () { it("should return all metadata field definitions", function () { const expectedPath = `/metadata_fields$`; - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - api.list_metadata_fields(); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await api.list_metadata_fields(); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), method: sinon.match("GET") @@ -134,13 +133,13 @@ describe("structured metadata api", function () { describe("add_metadata_field", function () { const expectedPath = "/metadata_fields$"; it("should create string metadata field", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { const metadata = { external_id: EXTERNAL_ID_STRING, label: EXTERNAL_ID_STRING, type: 'string' }; - api.add_metadata_field(metadata); + await api.add_metadata_field(metadata); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), method: sinon.match("POST") @@ -151,13 +150,13 @@ describe("structured metadata api", function () { }); }); it("should create integer metadata field", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { const metadata = { external_id: EXTERNAL_ID_INT, label: EXTERNAL_ID_INT, type: 'integer' }; - api.add_metadata_field(metadata); + await api.add_metadata_field(metadata); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), method: sinon.match("POST") @@ -184,7 +183,7 @@ describe("structured metadata api", function () { }); }); it("should create enum metadata field", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { const metadata = { datasource: { values: datasource_single @@ -193,7 +192,7 @@ describe("structured metadata api", function () { label: EXTERNAL_ID_ENUM, type: 'enum' }; - api.add_metadata_field(metadata); + await api.add_metadata_field(metadata); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), method: sinon.match("POST") @@ -268,9 +267,9 @@ describe("structured metadata api", function () { describe("delete_metadata_field", function () { it("should delete metadata field by external id", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { const expectedPath = `/metadata_fields/${EXTERNAL_ID_DELETE}$`; - api.delete_metadata_field(EXTERNAL_ID_DELETE); + await api.delete_metadata_field(EXTERNAL_ID_DELETE).catch(NOP); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), method: sinon.match("DELETE") @@ -434,8 +433,8 @@ describe("structured metadata api", function () { const method = /^PUT$/ it("should reorder the metadata fields for label order by asc", function () { - helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - api.reorder_metadata_fields("label", "asc"); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await api.reorder_metadata_fields("label", "asc"); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(pathname), @@ -448,8 +447,8 @@ describe("structured metadata api", function () { }); it("should reorder the metadata fields for external_id order by desc", function () { - helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - api.reorder_metadata_fields("external_id", "desc"); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await api.reorder_metadata_fields("external_id", "desc"); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(pathname), @@ -462,8 +461,8 @@ describe("structured metadata api", function () { }); it("should reorder the metadata fields for for created_at order by asc", function () { - helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - api.reorder_metadata_fields("created_at", "asc"); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await api.reorder_metadata_fields("created_at", "asc"); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(pathname), @@ -519,8 +518,8 @@ describe("structured metadata api", function () { describe('rules', () => { it('should allow listing metadata rules', () => { const expectedPath = '/metadata_rules'; - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - api.list_metadata_rules(); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await api.list_metadata_rules().catch(NOP); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), method: sinon.match('GET') @@ -530,14 +529,14 @@ describe("structured metadata api", function () { it('should allow adding new metadata rules', () => { const expectedPath = '/metadata_rules'; - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { const newMetadataRule = { metadata_field_id: 'field_id', name: 'rule_name', condition: {}, result: {} }; - api.add_metadata_rule(newMetadataRule); + await api.add_metadata_rule(newMetadataRule).catch(NOP); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), @@ -558,7 +557,7 @@ describe("structured metadata api", function () { it('should allow editing metadata rules', () => { const expectedPath = '/metadata_rules/some-metadata-rule-id'; - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { const ruleUpdate = { metadata_field_id: 'new_field_id', name: 'new_rule_name', @@ -566,7 +565,7 @@ describe("structured metadata api", function () { result: {}, state: 'inactive' }; - api.update_metadata_rule('some-metadata-rule-id', ruleUpdate); + await api.update_metadata_rule('some-metadata-rule-id', ruleUpdate).catch(NOP); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), @@ -588,8 +587,8 @@ describe("structured metadata api", function () { it('should allow removing existing metadata rules', () => { const expectedPath = '/metadata_rules/some-metadata-rule-id'; - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - api.delete_metadata_rule('some-metadata-rule-id'); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await api.delete_metadata_rule('some-metadata-rule-id').catch(NOP); sinon.assert.calledWith(requestSpy, sinon.match({ pathname: sinon.match(new RegExp(expectedPath)), method: sinon.match('DELETE') diff --git a/test/integration/api/analysis/analyze_spec.js b/test/integration/api/analysis/analyze_spec.js index 644b382d..b28b0e4a 100644 --- a/test/integration/api/analysis/analyze_spec.js +++ b/test/integration/api/analysis/analyze_spec.js @@ -5,6 +5,7 @@ const api_http = require('https'); const cloudinary = require('../../../../cloudinary'); const helper = require('../../../spechelper'); +const { NOP } = require('../../../../lib/utils'); describe('Analyze API', () => { describe('uri analysis', () => { @@ -25,8 +26,8 @@ describe('Analyze API', () => { mocked.xhr.restore(); }); - it('should call analyze endpoint with non-custom analysis_type', () => { - cloudinary.analysis.analyze_uri('https://example.com', 'captioning'); + it('should call analyze endpoint with non-custom analysis_type', async () => { + await cloudinary.analysis.analyze_uri('https://example.com', 'captioning').catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match({ pathname: sinon.match(new RegExp(`/v2/${config.cloud_name}/analysis/analyze/uri`)), @@ -36,11 +37,11 @@ describe('Analyze API', () => { sinon.assert.calledWith(mocked.write, sinon.match(helper.apiJsonParamMatcher('analysis_type', 'captioning'))); }); - it('should call analyze endpoint with custom analysis_type', () => { - cloudinary.analysis.analyze_uri('https://example.com', 'custom', { + it('should call analyze endpoint with custom analysis_type', async () => { + await cloudinary.analysis.analyze_uri('https://example.com', 'custom', { model_name: 'my_model', model_version: 1 - }); + }).catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match({ pathname: sinon.match(new RegExp(`/v2/${config.cloud_name}/analysis/analyze/uri`)), diff --git a/test/integration/api/authorization/oAuth_authorization_spec.js b/test/integration/api/authorization/oAuth_authorization_spec.js index 6cc2283a..ec5b12d7 100644 --- a/test/integration/api/authorization/oAuth_authorization_spec.js +++ b/test/integration/api/authorization/oAuth_authorization_spec.js @@ -3,14 +3,15 @@ const cloudinary = require("../../../../cloudinary"); const helper = require("../../../spechelper"); const describe = require('../../../testUtils/suite'); const testConstants = require('../../../testUtils/testConstants'); +const { NOP } = require('../../../../lib/utils'); const { PUBLIC_IDS } = testConstants; const { PUBLIC_ID } = PUBLIC_IDS; describe("oauth_token", function(){ it("should send the oauth_token option to the server (admin_api)", function() { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.resource(PUBLIC_ID, { oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4' }); - return sinon.assert.calledWith(requestSpy, + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.resource(PUBLIC_ID, { oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4' }).catch(NOP); + sinon.assert.calledWith(requestSpy, sinon.match.has("headers", sinon.match.has("Authorization", "Bearer MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4") )); @@ -18,14 +19,14 @@ describe("oauth_token", function(){ }); it("should send the oauth_token config to the server (admin_api)", function() { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { cloudinary.config({ api_key: undefined, api_secret: undefined, oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4' }); - cloudinary.v2.api.resource(PUBLIC_ID); - return sinon.assert.calledWith(requestSpy, + await cloudinary.v2.api.resource(PUBLIC_ID).catch(NOP); + sinon.assert.calledWith(requestSpy, sinon.match.has("headers", sinon.match.has("Authorization", "Bearer MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4") )); @@ -38,9 +39,9 @@ describe("oauth_token", function(){ api_secret: "1234", oauth_token: undefined }); - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.resource(PUBLIC_ID); - return sinon.assert.calledWith(requestSpy, sinon.match({ auth: "1234:1234" })); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.resource(PUBLIC_ID).catch(NOP); + sinon.assert.calledWith(requestSpy, sinon.match({ auth: "1234:1234" })); }); }); @@ -67,9 +68,9 @@ describe("oauth_token", function(){ }); it("should send the oauth_token option to the server (upload_api)", function() { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.uploader.upload(PUBLIC_ID, { oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4' }); - return sinon.assert.calledWith(requestSpy, + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.uploader.upload(PUBLIC_ID, { oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4' }).catch(NOP); + sinon.assert.calledWith(requestSpy, sinon.match.has("headers", sinon.match.has("Authorization", "Bearer MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4") )); @@ -77,14 +78,14 @@ describe("oauth_token", function(){ }); it("should send the oauth_token config to the server (upload_api)", function() { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { cloudinary.config({ api_key: undefined, api_secret: undefined, oauth_token: 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4' }); - cloudinary.v2.uploader.upload(PUBLIC_ID); - return sinon.assert.calledWith(requestSpy, + await cloudinary.v2.uploader.upload(PUBLIC_ID).catch(NOP); + sinon.assert.calledWith(requestSpy, sinon.match.has("headers", sinon.match.has("Authorization", "Bearer MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI4") )); @@ -97,9 +98,9 @@ describe("oauth_token", function(){ api_secret: "1234", oauth_token: undefined }); - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.uploader.upload(PUBLIC_ID) - return sinon.assert.calledWith(requestSpy, sinon.match({ auth: null })); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.uploader.upload(PUBLIC_ID).catch(NOP); + sinon.assert.calledWith(requestSpy, sinon.match({ auth: null })); }); }); @@ -120,9 +121,9 @@ describe("oauth_token", function(){ api_secret: undefined, oauth_token: undefined }); - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.uploader.unsigned_upload(PUBLIC_ID, 'preset') - return sinon.assert.calledWith(requestSpy, sinon.match({ auth: null })); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.uploader.unsigned_upload(PUBLIC_ID, 'preset').catch(NOP); + sinon.assert.calledWith(requestSpy, sinon.match({ auth: null })); }); }); }); diff --git a/test/integration/api/search/search_spec.js b/test/integration/api/search/search_spec.js index 58528854..0167a01e 100644 --- a/test/integration/api/search/search_spec.js +++ b/test/integration/api/search/search_spec.js @@ -1,11 +1,10 @@ -const Q = require('q'); const cloudinary = require('../../../../cloudinary'); const helper = require("../../../spechelper"); const testConstants = require('../../../testUtils/testConstants'); const describe = require('../../../testUtils/suite'); -const exp = require("constants"); -const cluster = require("cluster"); const assert = require("assert"); +const allSettled = require('../../../testUtils/helpers/allSettled'); +const wait = require('../../../testUtils/helpers/wait'); const { TIMEOUT, TAGS, @@ -30,7 +29,7 @@ describe("search_api", function () { describe("integration", function () { this.timeout(TIMEOUT.LONG); before(function () { - return Q.allSettled([ + return allSettled([ cloudinary.v2.uploader.upload(helper.IMAGE_FILE, { public_id: PUBLIC_ID_1, @@ -52,7 +51,8 @@ describe("search_api", function () { SEARCH_TAG], context: "stage=validated" }) - ]).delay(10000) + ]) + .then(wait(10000)) .then((uploadResults) => { uploadResults.forEach(({value}) => { ASSET_IDS.push(value.asset_id); @@ -60,11 +60,11 @@ describe("search_api", function () { }); }); - after(function () { + after(async function () { if (!cloudinary.config().keep_test_products) { let config = cloudinary.config(); - cloudinary.v2.api.delete_resources_by_tag(SEARCH_TAG); + await cloudinary.v2.api.delete_resources_by_tag(SEARCH_TAG); } }); it(`should return all images tagged with ${SEARCH_TAG}`, function () { diff --git a/test/integration/api/search/visual_search_spec.js b/test/integration/api/search/visual_search_spec.js index 6bf388a4..44bb85f3 100644 --- a/test/integration/api/search/visual_search_spec.js +++ b/test/integration/api/search/visual_search_spec.js @@ -5,11 +5,12 @@ const { deepStrictEqual } = require('assert'); const {TEST_CLOUD_NAME} = require('../../../testUtils/testConstants'); +const { NOP } = require('../../../../lib/utils'); describe('Visual search', () => { it('should pass the image_url parameter to the api call', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.visual_search({image_url: 'test-image-url'}); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.visual_search({image_url: 'test-image-url'}).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'GET'); @@ -18,8 +19,8 @@ describe('Visual search', () => { }); it('should pass the image_url parameter to the api call', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.visual_search({image_asset_id: 'image-asset-id'}); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.visual_search({image_asset_id: 'image-asset-id'}).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'GET'); @@ -28,8 +29,8 @@ describe('Visual search', () => { }); it('should pass the image_url parameter to the api call', () => { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.api.visual_search({text: 'visual-search-input'}); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.api.visual_search({text: 'visual-search-input'}).catch(NOP); const [calledWithUrl] = requestSpy.firstCall.args; strictEqual(calledWithUrl.method, 'GET'); diff --git a/test/integration/api/uploader/archivespec.js b/test/integration/api/uploader/archivespec.js index 4dec4d3b..5e9681f4 100644 --- a/test/integration/api/uploader/archivespec.js +++ b/test/integration/api/uploader/archivespec.js @@ -2,7 +2,6 @@ const https = require('https'); const last = require('lodash/last'); const sinon = require("sinon"); const execSync = require('child_process').execSync; -const Q = require('q'); const fs = require('fs'); const os = require('os'); const describe = require('../../../testUtils/suite'); @@ -43,7 +42,7 @@ describe("archive", function () { this.timeout(TIMEOUT.LONG); before(function () { - return Q.all([ + return Promise.all([ uploader.upload(IMAGE_URL, { public_id: PUBLIC_ID1, @@ -150,8 +149,8 @@ describe("archive", function () { describe('.create_zip', function () { this.timeout(TIMEOUT.LONG); it('should call create_archive with "zip" format and ignore missing resources', function () { - helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - uploader.create_zip({ + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await uploader.create_zip({ tags: TEST_TAG, public_ids: [PUBLIC_ID_RAW, "non-existing-resource"], resource_type: "raw", diff --git a/test/integration/api/uploader/auto_chaptering_spec.js b/test/integration/api/uploader/auto_chaptering_spec.js index 54ad7e02..3091c19b 100644 --- a/test/integration/api/uploader/auto_chaptering_spec.js +++ b/test/integration/api/uploader/auto_chaptering_spec.js @@ -4,6 +4,7 @@ const sinon = require('sinon'); const cloudinary = require('../../../../lib/cloudinary'); const createTestConfig = require('../../../testUtils/createTestConfig'); const helper = require('../../../spechelper'); +const { NOP } = require('../../../../lib/utils'); const ClientRequest = require('_http_client').ClientRequest; describe('Uploader', () => { @@ -21,15 +22,15 @@ describe('Uploader', () => { }); describe('upload', () => { - it('should send a request with auto_chaptering set to true if requested', () => { - cloudinary.v2.uploader.upload('irrelevant', { auto_chaptering: true }); + it('should send a request with auto_chaptering set to true if requested', async () => { + await cloudinary.v2.uploader.upload('irrelevant', { auto_chaptering: true }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('auto_chaptering', '1'))); }); }); describe('explicit', () => { - it('should send a request with auto_chaptering set to true if requested', () => { - cloudinary.v2.uploader.explicit('irrelevant', { auto_chaptering: true }); + it('should send a request with auto_chaptering set to true if requested', async () => { + await cloudinary.v2.uploader.explicit('irrelevant', { auto_chaptering: true }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('auto_chaptering', '1'))); }); }); diff --git a/test/integration/api/uploader/auto_transcription_spec.js b/test/integration/api/uploader/auto_transcription_spec.js index 2f7522c9..09bda434 100644 --- a/test/integration/api/uploader/auto_transcription_spec.js +++ b/test/integration/api/uploader/auto_transcription_spec.js @@ -1,6 +1,7 @@ const sinon = require('sinon'); const cloudinary = require('../../../../lib/cloudinary'); const helper = require('../../../spechelper'); +const { NOP } = require('../../../../lib/utils'); const ClientRequest = require('_http_client').ClientRequest; describe('Uploader', () => { @@ -18,25 +19,25 @@ describe('Uploader', () => { }); describe('upload', () => { - it('should send a request with auto_transcription set to true if requested', () => { - cloudinary.v2.uploader.upload('irrelevant', { auto_transcription: true }); + it('should send a request with auto_transcription set to true if requested', async () => { + await cloudinary.v2.uploader.upload('irrelevant', { auto_transcription: true }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('auto_transcription', '1'))); }); - it('should send a request with auto_transcription config if requested', () => { - cloudinary.v2.uploader.upload('irrelevant', { auto_transcription: { translate: ['pl'] } }); + it('should send a request with auto_transcription config if requested', async () => { + await cloudinary.v2.uploader.upload('irrelevant', { auto_transcription: { translate: ['pl'] } }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('auto_transcription', '{"translate":["pl"]}'))); }); }); describe('explicit', () => { - it('should send a request with auto_transcription set to true if requested', () => { - cloudinary.v2.uploader.explicit('irrelevant', { auto_transcription: true }); + it('should send a request with auto_transcription set to true if requested', async () => { + await cloudinary.v2.uploader.explicit('irrelevant', { auto_transcription: true }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('auto_transcription', '1'))); }); - it('should send a request with auto_transcription config if requested', () => { - cloudinary.v2.uploader.explicit('irrelevant', { auto_transcription: { translate: ['pl'] } }); + it('should send a request with auto_transcription config if requested', async () => { + await cloudinary.v2.uploader.explicit('irrelevant', { auto_transcription: { translate: ['pl'] } }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('auto_transcription', '{"translate":["pl"]}'))); }); }); diff --git a/test/integration/api/uploader/custom_region_spec.js b/test/integration/api/uploader/custom_region_spec.js index 7e7e61c5..aeb3cdc0 100644 --- a/test/integration/api/uploader/custom_region_spec.js +++ b/test/integration/api/uploader/custom_region_spec.js @@ -4,6 +4,7 @@ const sinon = require('sinon'); const cloudinary = require('../../../../lib/cloudinary'); const createTestConfig = require('../../../testUtils/createTestConfig'); const helper = require('../../../spechelper'); +const { NOP } = require('../../../../lib/utils'); const ClientRequest = require('_http_client').ClientRequest; describe('Uploader', () => { @@ -21,13 +22,13 @@ describe('Uploader', () => { }); describe('upload', () => { - it('should send a request with encoded custom region gravity that represents a box', () => { - cloudinary.v2.uploader.upload('irrelevant', { + it('should send a request with encoded custom region gravity that represents a box', async () => { + await cloudinary.v2.uploader.upload('irrelevant', { regions: { 'box_1': [[1, 2], [3, 4]], 'box_2': [[5, 6], [7, 8]] } - }); + }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('regions', JSON.stringify({ 'box_1': [[1, 2], [3, 4]], @@ -35,13 +36,13 @@ describe('Uploader', () => { })))); }); - it('should send a request with encoded custom region gravity that represents a custom shape', () => { - cloudinary.v2.uploader.upload('irrelevant', { + it('should send a request with encoded custom region gravity that represents a custom shape', async () => { + await cloudinary.v2.uploader.upload('irrelevant', { regions: { 'custom_1': [[1, 2], [3, 4], [5, 6], [7, 8]], 'custom_2': [[10, 11], [12, 13], [14, 15]] } - }); + }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('regions', JSON.stringify({ 'custom_1': [[1, 2], [3, 4], [5, 6], [7, 8]], @@ -51,13 +52,13 @@ describe('Uploader', () => { }); describe('explicit', () => { - it('should send a request with encoded custom region gravity that represents a box', () => { - cloudinary.v2.uploader.explicit('irrelevant', { + it('should send a request with encoded custom region gravity that represents a box', async () => { + await cloudinary.v2.uploader.explicit('irrelevant', { regions: { 'box_1': [[1, 2], [3, 4]], 'box_2': [[5, 6], [7, 8]] } - }); + }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('regions', JSON.stringify({ 'box_1': [[1, 2], [3, 4]], @@ -65,13 +66,13 @@ describe('Uploader', () => { })))); }); - it('should send a request with encoded custom region gravity that represents a custom shape', () => { - cloudinary.v2.uploader.explicit('irrelevant', { + it('should send a request with encoded custom region gravity that represents a custom shape', async () => { + await cloudinary.v2.uploader.explicit('irrelevant', { regions: { 'custom_1': [[1, 2], [3, 4], [5, 6], [7, 8]], 'custom_2': [[10, 11], [12, 13], [14, 15]] } - }); + }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('regions', JSON.stringify({ 'custom_1': [[1, 2], [3, 4], [5, 6], [7, 8]], diff --git a/test/integration/api/uploader/slideshow_spec.js b/test/integration/api/uploader/slideshow_spec.js index 1a82cf3b..8278cbdf 100644 --- a/test/integration/api/uploader/slideshow_spec.js +++ b/test/integration/api/uploader/slideshow_spec.js @@ -1,4 +1,3 @@ -const Q = require('q'); const cloudinary = require("../../../../cloudinary"); const describe = require('../../../testUtils/suite'); const TEST_ID = Date.now(); @@ -6,6 +5,7 @@ const TEST_ID = Date.now(); const createTestConfig = require('../../../testUtils/createTestConfig'); const testConstants = require('../../../testUtils/testConstants'); +const allSettled = require("../../../testUtils/helpers/allSettled"); const UPLOADER_V2 = cloudinary.v2.uploader; const { @@ -26,7 +26,7 @@ describe("create slideshow tests", function () { if (!(config.api_key && config.api_secret)) { expect().fail("Missing key and secret. Please set CLOUDINARY_URL."); } - return Q.allSettled([ + return allSettled([ !cloudinary.config().keep_test_products ? cloudinary.v2.api.delete_resources_by_tag(TEST_TAG) : void 0, !cloudinary.config().keep_test_products ? cloudinary.v2.api.delete_resources_by_tag(TEST_TAG, { diff --git a/test/integration/api/uploader/uploader_spec.js b/test/integration/api/uploader/uploader_spec.js index ae105ce7..5e9f92d7 100644 --- a/test/integration/api/uploader/uploader_spec.js +++ b/test/integration/api/uploader/uploader_spec.js @@ -2,7 +2,6 @@ const https = require('https'); const http = require('http'); const sinon = require('sinon'); const fs = require('fs'); -const Q = require('q'); const path = require('path'); const at = require('lodash/at'); const uniq = require('lodash/uniq'); @@ -32,6 +31,8 @@ const createTestConfig = require('../../../testUtils/createTestConfig'); const testConstants = require('../../../testUtils/testConstants'); const { shouldTestFeature, DYNAMIC_FOLDERS } = require("../../../spechelper"); +const { NOP } = require('../../../../lib/utils'); +const allSettled = require('../../../testUtils/helpers/allSettled'); const UPLOADER_V2 = cloudinary.v2.uploader; const { @@ -58,7 +59,7 @@ describe("uploader", function () { if (!(config.api_key && config.api_secret)) { expect().fail("Missing key and secret. Please set CLOUDINARY_URL."); } - return Q.allSettled([ + return allSettled([ !cloudinary.config().keep_test_products ? cloudinary.v2.api.delete_resources_by_tag(TEST_TAG) : void 0, !cloudinary.config().keep_test_products ? cloudinary.v2.api.delete_resources_by_tag(TEST_TAG, { @@ -84,8 +85,8 @@ describe("uploader", function () { }); }); it("should successfully upload with metadata", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - uploadImage({ metadata: METADATA_SAMPLE_DATA }); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await uploadImage({ metadata: METADATA_SAMPLE_DATA }).catch(NOP); sinon.assert.calledWith(requestSpy, sinon.match({ method: sinon.match("POST") })); @@ -93,18 +94,15 @@ describe("uploader", function () { }); }); it('should upload a file with correctly encoded transformation string', () => { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - const uploadResult = cloudinary.v2.uploader.upload('irrelevant', { transformation: { overlay: { text: 'test / 火' } } }); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.uploader.upload('irrelevant', { transformation: { overlay: { text: 'test / 火' } } }).catch(NOP); sinon.assert.calledWith(writeSpy, sinon.match(helper.uploadParamMatcher('transformation', 'l_text:test %2F 火'))); }); }); it('should upload a file with correctly encoded transformation string incl 4bytes characters', () => { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.uploader.upload('irrelevant', { transformation: { overlay: { text: 'test 𩸽 🍺' } } }) - .then((uploadResult) => { - sinon.assert.calledWith(writeSpy, sinon.match(helper.uploadParamMatcher('transformation', 'l_text:test 𩸽 🍺'))); - expect(uploadResult).to.have.key("created_at"); - }); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.uploader.upload('irrelevant', { transformation: { overlay: { text: 'test 𩸽 🍺' } } }).catch(NOP); + sinon.assert.calledWith(writeSpy, sinon.match(helper.uploadParamMatcher('transformation', 'l_text:test 𩸽 🍺'))); }); }); it("should successfully upload url", function () { @@ -160,23 +158,23 @@ describe("uploader", function () { }); describe("remote urls ", function () { const mocked = helper.mockTest(); - it("should send s3:// URLs to server", function () { - cloudinary.v2.uploader.upload("s3://test/1.jpg", { + it("should send s3:// URLs to server", async function () { + await cloudinary.v2.uploader.upload("s3://test/1.jpg", { tags: UPLOAD_TAGS - }); + }).catch(NOP); sinon.assert.calledWith(mocked.write, sinon.match(helper.uploadParamMatcher('file', "s3://test/1.jpg"))); }); - it("should send gs:// URLs to server", function () { - cloudinary.v2.uploader.upload("gs://test/1.jpg", { + it("should send gs:// URLs to server", async function () { + await cloudinary.v2.uploader.upload("gs://test/1.jpg", { tags: UPLOAD_TAGS - }); + }).catch(NOP); sinon.assert.calledWith(mocked.write, sinon.match(helper.uploadParamMatcher('file', "gs://test/1.jpg"))); }); - it("should send ftp:// URLs to server", function () { - cloudinary.v2.uploader.upload("ftp://example.com/1.jpg", { + it("should send ftp:// URLs to server", async function () { + await cloudinary.v2.uploader.upload("ftp://test/1.jpg", { tags: UPLOAD_TAGS - }); - sinon.assert.calledWith(mocked.write, sinon.match(helper.uploadParamMatcher('file', "ftp://example.com/1.jpg"))); + }).catch(NOP); + sinon.assert.calledWith(mocked.write, sinon.match(helper.uploadParamMatcher('file', "ftp://test/1.jpg"))); }); }); describe("rename", function () { @@ -219,8 +217,8 @@ describe("uploader", function () { expect(renameResult).to.have.property('context'); }); it('should include notification_url in rename response if included in the request', async () => { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - const renameResult = cloudinary.v2.uploader.rename('irrelevant', 'irrelevant', { notification_url: 'https://notification-url.com' }); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.uploader.rename('irrelevant', 'irrelevant', { notification_url: 'https://notification-url.com' }).catch(NOP); sinon.assert.calledWith(writeSpy, sinon.match(helper.uploadParamMatcher('notification_url', 'https://notification-url.com'))); }); }); @@ -236,10 +234,10 @@ describe("uploader", function () { spy.restore(); return xhr.restore(); }); - it("should pass the invalidate value in rename to the server", function () { - cloudinary.v2.uploader.rename("first_id", "second_id", { + it("should pass the invalidate value in rename to the server", async function () { + await cloudinary.v2.uploader.rename("first_id", "second_id", { invalidate: true - }); + }).catch(NOP); expect(spy.calledWith(sinon.match(function (arg) { return arg.toString().match(/name="invalidate"/); }))).to.be.ok(); @@ -263,8 +261,8 @@ describe("uploader", function () { }); }); it('should pass notification_url', async () => { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - const renameResult = cloudinary.v2.uploader.destroy('irrelevant', { notification_url: 'https://notification-url.com' }); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.uploader.destroy('irrelevant', { notification_url: 'https://notification-url.com' }).catch(NOP); sinon.assert.calledWith(writeSpy, sinon.match(helper.uploadParamMatcher('notification_url', 'https://notification-url.com'))); }); }); @@ -320,12 +318,12 @@ describe("uploader", function () { }); describe("extra headers", function () { it("should support extra headers in object format e.g. {Link: \"1\"}", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.uploader.upload(IMAGE_FILE, { + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.uploader.upload(IMAGE_FILE, { extra_headers: { Link: "1" } - }); + }).catch(NOP); assert.ok(requestSpy.args[0][0].headers.Link); assert.equal(requestSpy.args[0][0].headers.Link, "1"); }); @@ -437,11 +435,10 @@ describe("uploader", function () { }); describe("context", function () { this.timeout(TIMEOUT.MEDIUM); - before(function () { - return Q.all([uploadImage(), uploadImage()]).spread((result1, result2) => { - this.first_id = result1.public_id; - this.second_id = result2.public_id; - }); + before(async function () { + const [result1, result2] = await Promise.all([uploadImage(), uploadImage()]); + this.first_id = result1.public_id; + this.second_id = result2.public_id; }); it("should add context to existing resources", function () { return cloudinary.v2.uploader @@ -704,10 +701,10 @@ describe("uploader", function () { return xhr.restore(); }); - it('should pass its value to the upload api', () => { - cloudinary.v2.uploader.upload(IMAGE_FILE, { + it('should pass its value to the upload api', async () => { + await cloudinary.v2.uploader.upload(IMAGE_FILE, { on_success: 'current_asset.update({tags: ["autocaption"]});' - }); + }).catch(NOP); expect(spy.calledWith(sinon.match((arg) => { return arg.toString().match(/on_success='current_asset.update({tags: ["autocaption"]});'/); @@ -737,7 +734,8 @@ describe("uploader", function () { fs.stat(LARGE_RAW_FILE, function (err, stat) { cloudinary.v2.uploader.upload_large(LARGE_RAW_FILE, { chunk_size: 40000, - tags: UPLOAD_TAGS + tags: UPLOAD_TAGS, + disable_promises: true }, function (error, result) { expect(error.message).to.eql("All parts except EOF-chunk must be larger than 5mb"); done(); @@ -798,42 +796,45 @@ describe("uploader", function () { }); }); }); - it("should support uploading large video files", function () { + it("should support uploading large video files", function (done) { var stat, writeSpy; this.timeout(TIMEOUT.LONG * 10); writeSpy = sinon.spy(ClientRequest.prototype, 'write'); stat = fs.statSync(LARGE_VIDEO); expect(stat).to.be.ok(); - return Q.denodeify(cloudinary.v2.uploader.upload_chunked)(LARGE_VIDEO, { + cloudinary.v2.uploader.upload_chunked(LARGE_VIDEO, { chunk_size: 6000000, resource_type: 'video', timeout: TIMEOUT.LONG * 10, tags: UPLOAD_TAGS - }).then(function (result) { + }, function (err, result) { var timestamps; - expect(result.bytes).to.eql(stat.size); - expect(result.etag).to.eql("ff6c391d26be0837ee5229885b5bd571"); - timestamps = writeSpy.args.map(function (a) { - return a[0].toString(); - }).filter(function (p) { - return p.match(/timestamp/); - }).map(function (p) { - return p.match(/"timestamp"\s+(\d+)/)[1]; - }); - expect(timestamps.length).to.be.greaterThan(1); - expect(uniq(timestamps)).to.eql(uniq(timestamps)); // uniq b/c last timestamp may be duplicated - }).finally(function () { - writeSpy.restore(); + try { + expect(result.bytes).to.eql(stat.size); + expect(result.etag).to.eql("ff6c391d26be0837ee5229885b5bd571"); + timestamps = writeSpy.args.map(function (a) { + return a[0].toString(); + }).filter(function (p) { + return p.match(/timestamp/); + }).map(function (p) { + return p.match(/"timestamp"\s+(\d+)/)[1]; + }); + expect(timestamps.length).to.be.greaterThan(1); + expect(uniq(timestamps)).to.eql(uniq(timestamps)); // uniq b/c last timestamp may be duplicated + } finally { + writeSpy.restore(); + done(); + } }); }); - it("should update timestamp for each chunk", function () { + it("should update timestamp for each chunk", function (done) { var writeSpy = sinon.spy(ClientRequest.prototype, 'write'); - return Q.denodeify(cloudinary.v2.uploader.upload_chunked)(LARGE_VIDEO, { + cloudinary.v2.uploader.upload_chunked(LARGE_VIDEO, { chunk_size: 6000000, resource_type: 'video', timeout: TIMEOUT.LONG * 10, tags: UPLOAD_TAGS - }).then(function () { + }, function () { var timestamps = writeSpy.args.map(function (a) { return a[0].toString(); }).filter(function (p) { @@ -841,10 +842,13 @@ describe("uploader", function () { }).map(function (p) { return p.match(/"timestamp"\s+(\d+)/)[1]; }); - expect(timestamps.length).to.be.greaterThan(1); - expect(uniq(timestamps)).to.eql(uniq(timestamps)); - }).finally(function () { - writeSpy.restore(); + try { + expect(timestamps.length).to.be.greaterThan(1); + expect(uniq(timestamps)).to.eql(uniq(timestamps)); + } finally { + writeSpy.restore(); + done(); + } }); }); it("should support uploading based on a url", function (done) { @@ -862,19 +866,19 @@ describe("uploader", function () { }); describe("dynamic folders", () => { const mocked = helper.mockTest(); - it('should pass dynamic folder params', () => { + it('should pass dynamic folder params', async () => { const public_id_prefix = "fd_public_id_prefix"; const asset_folder = "asset_folder"; const display_name = "display_name"; const use_filename_as_display_name = true; const folder = "folder/test"; - UPLOADER_V2.upload(IMAGE_FILE, { + await UPLOADER_V2.upload(IMAGE_FILE, { public_id_prefix, asset_folder, display_name, use_filename_as_display_name, folder - }); + }).catch(NOP); sinon.assert.calledWithMatch(mocked.write, helper.uploadParamMatcher("public_id_prefix", public_id_prefix)); sinon.assert.calledWithMatch(mocked.write, helper.uploadParamMatcher("asset_folder", asset_folder)); sinon.assert.calledWithMatch(mocked.write, helper.uploadParamMatcher("display_name", display_name)); @@ -951,55 +955,46 @@ describe("uploader", function () { }); it("should reject with promise rejection if disable_promises: false", function (done) { - const spy = sinon.spy(); + const unhandledRejectionSpy = sinon.spy(); + process.on('unhandledRejection', unhandledRejectionSpy); + cloudinary.v2.uploader.upload_large(EMPTY_IMAGE, { disable_promises: false }, () => { }); - function unhandledRejection() { - spy(); - } - process.on('unhandledRejection', unhandledRejection); - // Promises are not disabled meaning we should throw unhandledRejection setTimeout(() => { - expect(sinon.assert.called(spy)); - process.removeListener('unhandledRejection', unhandledRejection); + expect(sinon.assert.called(unhandledRejectionSpy)); + process.removeListener('unhandledRejection', unhandledRejectionSpy); done(); }, 2000); }); it("should reject with promise rejection by default", function (done) { - const spy = sinon.spy(); + const unhandledRejectionSpy = sinon.spy(); - cloudinary.v2.uploader.upload_large(EMPTY_IMAGE, () => { }); + + process.on('unhandledRejection', unhandledRejectionSpy); - function unhandledRejection() { - spy(); - } - process.on('unhandledRejection', unhandledRejection); + cloudinary.v2.uploader.upload_large(EMPTY_IMAGE, () => { }); // Promises are not disabled meaning we should throw unhandledRejection setTimeout(() => { - expect(sinon.assert.called(spy)); - process.removeListener('unhandledRejection', unhandledRejection); + expect(sinon.assert.called(unhandledRejectionSpy)); + process.removeListener('unhandledRejection', unhandledRejectionSpy); done(); }, 2000); }); it("should reject without promise rejection if disable_promises: true", function (done) { - const spy = sinon.spy(); - + const unhandledRejectionSpy = sinon.spy(); + + process.on('unhandledRejection', unhandledRejectionSpy); cloudinary.v2.uploader.upload_large(EMPTY_IMAGE, { disable_promises: true }, () => { }); - function unhandledRejection() { - spy(); - } - process.on('unhandledRejection', unhandledRejection); - // Promises are disabled meaning unhandledRejection was not called setTimeout(() => { - expect(sinon.assert.notCalled(spy)); - process.removeListener('unhandledRejection', unhandledRejection); + expect(sinon.assert.notCalled(unhandledRejectionSpy)); + process.removeListener('unhandledRejection', unhandledRejectionSpy); done(); }, 2000); }); @@ -1125,8 +1120,8 @@ describe("uploader", function () { }); describe("async upload", function () { var mocked = helper.mockTest(); - it("should pass `async` value to the server", function () { - cloudinary.v2.uploader.upload(IMAGE_FILE, { + it("should pass `async` value to the server", async function () { + await cloudinary.v2.uploader.upload(IMAGE_FILE, { async: true, transformation: { effect: "sepia" @@ -1136,9 +1131,9 @@ describe("uploader", function () { }); }); it("should pass `accessibility_analysis` option to the server", function () { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - cloudinary.v2.uploader.upload(IMAGE_FILE, { accessibility_analysis: true }); - return sinon.assert.calledWith(writeSpy, sinon.match(helper.uploadParamMatcher("accessibility_analysis", 1))); + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await cloudinary.v2.uploader.upload(IMAGE_FILE, { accessibility_analysis: true }); + sinon.assert.calledWith(writeSpy, sinon.match(helper.uploadParamMatcher("accessibility_analysis", 1))); }); }); describe("explicit", function () { @@ -1154,8 +1149,8 @@ describe("uploader", function () { xhr.restore(); }); describe(":invalidate", function () { - it("should pass the invalidate value to the server", function () { - cloudinary.v2.uploader.explicit("cloudinary", { + it("should pass the invalidate value to the server", async function () { + await cloudinary.v2.uploader.explicit("cloudinary", { type: "twitter_name", eager: [ { @@ -1166,24 +1161,24 @@ describe("uploader", function () { invalidate: true, quality_analysis: true, tags: [TEST_TAG] - }); + }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('invalidate', 1))); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('quality_analysis', 1))); }); }); - it("should support metadata", function () { - cloudinary.v2.uploader.explicit("cloudinary", { metadata: METADATA_SAMPLE_DATA }); + it("should support metadata", async function () { + await cloudinary.v2.uploader.explicit("cloudinary", { metadata: METADATA_SAMPLE_DATA }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher("metadata", METADATA_SAMPLE_DATA_ENCODED))); }); - it("should support raw_convert", function () { - cloudinary.v2.uploader.explicit("cloudinary", { + it("should support raw_convert", async function () { + await cloudinary.v2.uploader.explicit("cloudinary", { raw_convert: "google_speech", tags: [TEST_TAG] - }); + }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('raw_convert', 'google_speech'))); }); - it("should pass `accessibility_analysis` to server", function () { - cloudinary.v2.uploader.explicit("cloudinary", { accessibility_analysis: true }); + it("should pass `accessibility_analysis` to server", async function () { + await cloudinary.v2.uploader.explicit("cloudinary", { accessibility_analysis: true }).catch(NOP); sinon.assert.calledWith(spy, sinon.match(helper.uploadParamMatcher('accessibility_analysis', 1))); }); }); @@ -1211,18 +1206,18 @@ describe("uploader", function () { const mocked = helper.mockTest(); const qualityValues = ["auto:advanced", "auto:best", "80:420", "none"]; function testValue(quality) { - return it("should pass '" + quality + "'", function () { - cloudinary.v2.uploader.upload(IMAGE_FILE, { + return it("should pass '" + quality + "'", async function () { + await cloudinary.v2.uploader.upload(IMAGE_FILE, { "quality_override": quality - }); + }).catch(NOP); sinon.assert.calledWithMatch(mocked.write, helper.uploadParamMatcher("quality_override", quality)); }); } qualityValues.forEach(value => testValue(value)); - it("should be supported by explicit api", function () { - cloudinary.v2.uploader.explicit("cloudinary", { + it("should be supported by explicit api", async function () { + await cloudinary.v2.uploader.explicit("cloudinary", { "quality_override": "auto:best" - }); + }).catch(NOP); sinon.assert.calledWithMatch(mocked.write, helper.uploadParamMatcher("quality_override", "auto:best")); }); }); @@ -1230,8 +1225,8 @@ describe("uploader", function () { it("should update metadata of existing resources", function () { const metadata_fields = { metadata_color: "red", metadata_shape: "" }; const public_ids = ["test_id_1", "test_id_2"]; - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.uploader.update_metadata(metadata_fields, public_ids); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.uploader.update_metadata(metadata_fields, public_ids).catch(NOP); sinon.assert.calledWith(requestSpy, sinon.match({ method: sinon.match("POST") })); @@ -1243,8 +1238,8 @@ describe("uploader", function () { it("should support updating metadata with clear_invalid", function () { const metadata_fields = { metadata_color: "red" }; const public_ids = ["test_id_1"]; - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - cloudinary.v2.uploader.update_metadata(metadata_fields, public_ids, { clear_invalid: true }); + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await cloudinary.v2.uploader.update_metadata(metadata_fields, public_ids, { clear_invalid: true }).catch(NOP); sinon.assert.calledWith(requestSpy, sinon.match({ method: sinon.match("POST") })); @@ -1337,11 +1332,10 @@ describe("uploader", function () { external_id: METADATA_FIELD_UNIQUE_EXTERNAL_ID, label: METADATA_FIELD_UNIQUE_EXTERNAL_ID, type: "string" - }).finally(function () { }); + }); }); after(function () { - return cloudinary.v2.api.delete_metadata_field(METADATA_FIELD_UNIQUE_EXTERNAL_ID) - .finally(function () { }); + return cloudinary.v2.api.delete_metadata_field(METADATA_FIELD_UNIQUE_EXTERNAL_ID); }); it("should be set when calling upload with metadata", function () { return uploadImage({ @@ -1363,31 +1357,30 @@ describe("uploader", function () { expect(result.metadata[METADATA_FIELD_UNIQUE_EXTERNAL_ID]).to.eql(METADATA_FIELD_VALUE); }); }); - it('should allow passing both string and a number for a number smd field', () => { + it('should allow passing both string and a number for a number smd field', async () => { const smdNumberField = 'smd_number_field'; - cloudinary.v2.api.add_metadata_field({ + await cloudinary.v2.api.add_metadata_field({ external_id: smdNumberField, label: smdNumberField, - type: 'number' - }).then(() => { - return Promise.all([ - uploadImage({ - tags: UPLOAD_TAGS, - metadata: { - [smdNumberField]: 123 - } - }), - uploadImage({ - tags: UPLOAD_TAGS, - metadata: { - [smdNumberField]: '123' - } - }) - ]); - }).then(([firstUpload, secondUpload]) => { - expect(firstUpload.metadata[smdNumberField]).to.eql(123); - expect(secondUpload.metadata[smdNumberField]).to.eql(123); - }); + type: 'integer' + }) + + const [firstUpload, secondUpload] = await Promise.all([ + uploadImage({ + tags: UPLOAD_TAGS, + metadata: { + [smdNumberField]: 123 + } + }), + uploadImage({ + tags: UPLOAD_TAGS, + metadata: { + [smdNumberField]: '123' + } + }) + ]); + expect(firstUpload.metadata[smdNumberField]).to.eql(123); + expect(secondUpload.metadata[smdNumberField]).to.eql(123); }); it("should be updatable with uploader.update_metadata on an existing resource", function () { let publicId; @@ -1408,18 +1401,16 @@ describe("uploader", function () { let resource_1; let resource_2; - return Q.allSettled( - [ - uploadImage({ - tags: UPLOAD_TAGS - }), - uploadImage({ - tags: UPLOAD_TAGS - }) - ] - ).then(function ([result_1, result_2]) { - resource_1 = result_1.value; - resource_2 = result_2.value; + return Promise.all([ + uploadImage({ + tags: UPLOAD_TAGS + }), + uploadImage({ + tags: UPLOAD_TAGS + }) + ]).then(function ([result_1, result_2]) { + resource_1 = result_1; + resource_2 = result_2; return cloudinary.v2.uploader.update_metadata(metadata_fields, [resource_1.public_id, resource_2.public_id]); }) .then((result) => { @@ -1460,13 +1451,13 @@ describe("uploader", function () { cloudinary.config(configBck2); writeSpy.restore(); }); - it("should allow a signature and timestamp parameter on uploads", function () { - cloudinary.v2.uploader.upload(IMAGE_FILE, { + it("should allow a signature and timestamp parameter on uploads", async function () { + await cloudinary.v2.uploader.upload(IMAGE_FILE, { public_id: 'folder/file', version: '1234', timestamp: 1569707219, signature: 'b77fc0b0dffbf7e74bdad36b615225fb6daff81e' - }); + }).catch(NOP); sinon.assert.calledWith(writeSpy, sinon.match(helper.uploadParamMatcher('signature', "b77fc0b0dffbf7e74bdad36b615225fb6daff81e"))); sinon.assert.calledWith(writeSpy, sinon.match(helper.uploadParamMatcher('timestamp', '1569707219'))); }); diff --git a/test/integration/streaming_profiles_spec.js b/test/integration/streaming_profiles_spec.js index fd4fde3a..d4b63f67 100644 --- a/test/integration/streaming_profiles_spec.js +++ b/test/integration/streaming_profiles_spec.js @@ -1,9 +1,9 @@ let describe = require('../testUtils/suite'); const keys = require('lodash/keys'); -const Q = require('q'); const cloudinary = require("../../cloudinary"); const helper = require("../spechelper"); const TIMEOUT = require('../testUtils/testConstants').TIMEOUT; +const allSettled = require('../testUtils/helpers/allSettled'); const api = cloudinary.v2.api; describe('Cloudinary::Api', function () { @@ -17,9 +17,9 @@ describe('Cloudinary::Api', function () { after(function () { cloudinary.config(true); if (cloudinary.config().keep_test_products) { - return Q.resolve(); + return; } - return Q.allSettled([ + return allSettled([ cloudinary.v2.api.delete_streaming_profile(test_id_1), cloudinary.v2.api.delete_streaming_profile(test_id_1 + 'a'), cloudinary.v2.api.delete_streaming_profile(test_id_3) diff --git a/test/spechelper.js b/test/spechelper.js index fd4c3a0d..c41f4b99 100644 --- a/test/spechelper.js +++ b/test/spechelper.js @@ -2,7 +2,6 @@ const isFunction = require('lodash/isFunction'); const querystring = require('querystring'); const sinon = require('sinon'); const ClientRequest = require('_http_client').ClientRequest; -const Q = require('q'); const http = require('http'); const https = require('https'); // Load all our custom assertions @@ -233,29 +232,23 @@ A test block @param {function} providedFunction test function, accepting (mockXHR, writeSpy, requestSpy) @return {Promise} */ -exports.provideMockObjects = function (providedFunction) { +exports.provideMockObjects = async function (providedFunction) { let requestSpy, writeSpy, mockXHR; - return Q.Promise(function (resolve, reject, notify) { - var result; + var result; - mockXHR = sinon.useFakeXMLHttpRequest(); - writeSpy = sinon.spy(ClientRequest.prototype, 'write'); - requestSpy = sinon.spy(api_http, 'request'); + mockXHR = sinon.useFakeXMLHttpRequest(); + writeSpy = sinon.spy(ClientRequest.prototype, 'write'); + requestSpy = sinon.spy(api_http, 'request'); - result = providedFunction(mockXHR, writeSpy, requestSpy); - - - if (result && isFunction(result.then)) { - return result.then(resolve); - } else { - return resolve(result); - } - }).finally(function () { + try { + result = await providedFunction(mockXHR, writeSpy, requestSpy); + } finally { requestSpy.restore(); writeSpy.restore(); mockXHR.restore(); - }).done(); + } + return result; }; exports.setupCache = function () { diff --git a/test/testUtils/helpers/allSettled.js b/test/testUtils/helpers/allSettled.js new file mode 100644 index 00000000..dce308ae --- /dev/null +++ b/test/testUtils/helpers/allSettled.js @@ -0,0 +1,8 @@ +function allSettled(promises) { + return Promise.all( + promises.map((p = Promise.resolve()) => p.then((value) => ({ status: "fulfilled", value })).catch((reason) => ({ status: "rejected", reason })) + ) + ); +} + +module.exports = allSettled; diff --git a/test/testUtils/helpers/retry.js b/test/testUtils/helpers/retry.js index 2b8de893..5b61d426 100644 --- a/test/testUtils/helpers/retry.js +++ b/test/testUtils/helpers/retry.js @@ -24,6 +24,6 @@ module.exports = async function retry(fn, limit = RETRY.LIMIT, delay= RETRY.DELA } // eslint-disable-next-line no-await-in-loop - await wait(delay) + await wait(delay)(); } } diff --git a/test/testUtils/helpers/wait.js b/test/testUtils/helpers/wait.js index 48f9b7a3..bc36bc67 100644 --- a/test/testUtils/helpers/wait.js +++ b/test/testUtils/helpers/wait.js @@ -4,10 +4,10 @@ * @return {function(...item): Promise<...item>} */ function wait(ms = 0) { - return (...rest) => { + return (value) => { return new Promise((resolve) => { setTimeout(() => { - resolve(...rest); + resolve(value); }, ms); }); } diff --git a/test/testUtils/reusableTests/api/toAcceptNextCursor.js b/test/testUtils/reusableTests/api/toAcceptNextCursor.js index 415fc3fc..deadb9ec 100644 --- a/test/testUtils/reusableTests/api/toAcceptNextCursor.js +++ b/test/testUtils/reusableTests/api/toAcceptNextCursor.js @@ -1,13 +1,14 @@ const registerReusableTest = require('../reusableTests').registerReusableTest; const sinon = require('sinon'); const helper = require("../../../spechelper"); +const { NOP } = require('../../../../lib/utils'); registerReusableTest("accepts next_cursor", function (testFunc, ...args) { it("Has a next cursor", function () { - return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => { - testFunc(...args, { + return helper.provideMockObjects(async (mockXHR, writeSpy, requestSpy) => { + await testFunc(...args, { next_cursor: 23452342 - }); + }).catch(NOP); // TODO Why aren't we sure what's called here? if (writeSpy.called) { diff --git a/test/testUtils/reusableTests/api/toBeACursor.js b/test/testUtils/reusableTests/api/toBeACursor.js index 1a1fe9bf..1f088684 100644 --- a/test/testUtils/reusableTests/api/toBeACursor.js +++ b/test/testUtils/reusableTests/api/toBeACursor.js @@ -1,13 +1,14 @@ const registerReusableTest = require('../reusableTests').registerReusableTest; const sinon = require('sinon'); const helper = require("../../../spechelper"); +const { NOP } = require('../../../../lib/utils'); registerReusableTest("a list with a cursor", function (testFunc, ...args) { it("Cursor has max results", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - testFunc(...args, { + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await testFunc(...args, { max_results: 10 - }); + }).catch(NOP); // TODO why don't we know what is used? if (writeSpy.called) { @@ -20,10 +21,10 @@ registerReusableTest("a list with a cursor", function (testFunc, ...args) { }); }); it("Cursor has next cursor", function () { - return helper.provideMockObjects(function (mockXHR, writeSpy, requestSpy) { - testFunc(...args, { + return helper.provideMockObjects(async function (mockXHR, writeSpy, requestSpy) { + await testFunc(...args, { next_cursor: 23452342 - }); + }).catch(NOP); // TODO why don't we know what is used? if (writeSpy.called) { diff --git a/test/testUtils/suite.js b/test/testUtils/suite.js index a1b2bfcb..f49c0e94 100644 --- a/test/testUtils/suite.js +++ b/test/testUtils/suite.js @@ -12,4 +12,7 @@ function makeSuite(name, tests) { }); } +makeSuite.only = describe.only; +makeSuite.skip = describe.skip; + module.exports = makeSuite; diff --git a/test/unit/api_restore_spec.js b/test/unit/api_restore_spec.js index e6fd097b..f878368f 100644 --- a/test/unit/api_restore_spec.js +++ b/test/unit/api_restore_spec.js @@ -5,6 +5,7 @@ const cloudinary = require('../../lib/cloudinary'); const createTestConfig = require('../testUtils/createTestConfig'); const helper = require('../spechelper'); const api_http = require("https"); +const { NOP } = require('../../lib/utils'); const ClientRequest = require('_http_client').ClientRequest; describe('api restore handlers', function () { @@ -24,14 +25,14 @@ describe('api restore handlers', function () { }); describe('.restore', function () { - it('sends public_ids and versions with JSON payload', function () { + it('sends public_ids and versions with JSON payload', async function () { const options = { resource_type: 'video', type: 'authenticated', versions: ['ver-1', 'ver-2'] }; - cloudinary.v2.api.restore(['pub-1', 'pub-2'], options); + await cloudinary.v2.api.restore(['pub-1', 'pub-2'], options).catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match({ pathname: sinon.match('resources/video/authenticated/restore'), @@ -44,11 +45,11 @@ describe('api restore handlers', function () { }); describe('.restore_by_asset_ids', function () { - it('sends asset_ids and versions with JSON payload', function () { + it('sends asset_ids and versions with JSON payload', async function () { const options = { versions: ['ver-3'] }; const assetIds = ['asset-1', 'asset-2']; - cloudinary.v2.api.restore_by_asset_ids(assetIds, options); + await cloudinary.v2.api.restore_by_asset_ids(assetIds, options).catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match({ pathname: sinon.match('resources/restore'), @@ -59,8 +60,8 @@ describe('api restore handlers', function () { sinon.assert.calledWith(mocked.write, sinon.match(helper.apiJsonParamMatcher('versions', ['ver-3']))); }); - it('wraps a single asset id into an array before calling the API', function () { - cloudinary.v2.api.restore_by_asset_ids('single-asset-id'); + it('wraps a single asset id into an array before calling the API', async function () { + await cloudinary.v2.api.restore_by_asset_ids('single-asset-id').catch(NOP); sinon.assert.calledWith(mocked.request, sinon.match({ pathname: sinon.match('resources/restore'),