From 690d20dce70abdab5512c8ef651d4805eaa0b6af Mon Sep 17 00:00:00 2001 From: refael-m Date: Tue, 21 Apr 2026 10:36:49 +0300 Subject: [PATCH 1/8] test: e2e add test for upload asset --- test/e2e/package.json | 2 +- test/e2e/specs/loadMlAssets.spec.ts | 20 ++-- test/e2e/specs/uploadFromSideBarView.spec.ts | 39 ++++++++ .../ActivityBarUtils.ts | 0 .../SideBarViewUtils.ts | 18 ++++ .../src/vscodeComponentsUtils/WebViewUtils.ts | 16 ++++ .../src/webViewTabs/UploadToCloudinaryTab.ts | 92 +++++++++++++++++++ 7 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 test/e2e/specs/uploadFromSideBarView.spec.ts rename test/e2e/src/{utils => vscodeComponentsUtils}/ActivityBarUtils.ts (100%) rename test/e2e/src/{utils => vscodeComponentsUtils}/SideBarViewUtils.ts (83%) create mode 100644 test/e2e/src/vscodeComponentsUtils/WebViewUtils.ts create mode 100644 test/e2e/src/webViewTabs/UploadToCloudinaryTab.ts diff --git a/test/e2e/package.json b/test/e2e/package.json index 0242cf8..8a7eded 100644 --- a/test/e2e/package.json +++ b/test/e2e/package.json @@ -16,7 +16,7 @@ "wdio-vscode-service": "^6.1.4" }, "scripts": { - "test:e2e": "wdio run ./wdio.conf.ts", + "test:e2e": "rm -rf allure-report allure-results && wdio run ./wdio.conf.ts", "test:report": "allure generate allure-results -o allure-report --clean && allure open allure-report" } } \ No newline at end of file diff --git a/test/e2e/specs/loadMlAssets.spec.ts b/test/e2e/specs/loadMlAssets.spec.ts index 69fc2e8..a7cec77 100644 --- a/test/e2e/specs/loadMlAssets.spec.ts +++ b/test/e2e/specs/loadMlAssets.spec.ts @@ -1,7 +1,7 @@ import path from 'node:path'; import { CloudinarySDK } from '../src/sdks/cloudinarySDK.js'; -import { activityBarUtils } from '../src/utils/ActivityBarUtils.js' -import { sideBarViewUtils } from '../src/utils/SideBarViewUtils.js' +import { activityBarUtils } from '../src/vscodeComponentsUtils/ActivityBarUtils.js' +import { sideBarViewUtils } from '../src/vscodeComponentsUtils/SideBarViewUtils.js' import crypto from 'node:crypto'; import { pathUtils } from '../src/utils/pathUtils.js'; @@ -15,12 +15,20 @@ describe('Asset Explorer Tetsts', () => { let secondAssetPublicID = `e2e-test-ae-${crypto.randomUUID().substring(0, 8)}`; beforeEach(async () => { - await cloudinarySDK.V2.uploader.upload(path.join(pathUtils.getTestAssetsPath(), 'sample_png.png'), { public_id: firstAssetPublicID }); - await cloudinarySDK.V2.uploader.upload(path.join(pathUtils.getTestAssetsPath(), 'sample_png.png'), { public_id: secondAssetPublicID }); + try { + await cloudinarySDK.V2.uploader.upload(path.join(pathUtils.getTestAssetsPath(), 'sample_png.png'), { public_id: firstAssetPublicID }); + await cloudinarySDK.V2.uploader.upload(path.join(pathUtils.getTestAssetsPath(), 'sample_png.png'), { public_id: secondAssetPublicID }); + } catch (error) { + throw new Error('Error uploading assets:', error); + } }); afterEach(async () => { - await cloudinarySDK.V2.api.delete_resources([firstAssetPublicID, secondAssetPublicID]); + try { + await cloudinarySDK.V2.api.delete_resources([firstAssetPublicID, secondAssetPublicID]); + } catch (error) { + throw new Error('Error deleting assets:', error); + } }); /** @@ -35,6 +43,6 @@ describe('Asset Explorer Tetsts', () => { await sideBarViewUtils.validateSideBarViewTitle(expectedTitle); await sideBarViewUtils.validateContentItemsExist(expectedItems); - }); + }); }); diff --git a/test/e2e/specs/uploadFromSideBarView.spec.ts b/test/e2e/specs/uploadFromSideBarView.spec.ts new file mode 100644 index 0000000..093a94b --- /dev/null +++ b/test/e2e/specs/uploadFromSideBarView.spec.ts @@ -0,0 +1,39 @@ +import path from "path"; +import { CloudinarySDK } from "../src/sdks/cloudinarySDK.js"; +import { pathUtils } from "../src/utils/pathUtils.js"; +import { SideBarViewActions, sideBarViewUtils } from "../src/vscodeComponentsUtils/SideBarViewUtils.js"; +import { activityBarUtils } from "../src/vscodeComponentsUtils/ActivityBarUtils.js"; +import { uploadToCloudinaryTab } from "../src/webViewTabs/UploadToCloudinaryTab.js"; + +describe('Upload asset from side bar Upload button', () => { + + const cloudinarySDK = new CloudinarySDK(); + const filePath = path.join(pathUtils.getTestAssetsPath(), 'sample_png.png'); + const firstAssetPublicID = `e2e-test-ae-${crypto.randomUUID().substring(0, 8)}`; + + afterEach(async () => { + try { + await cloudinarySDK.V2.api.delete_resources([firstAssetPublicID]); + } catch (error) { + throw new Error('Error deleting assets:', error); + } + }); + + it('should upload an asset using the side bar Upload button with custom public ID', async () => { + await activityBarUtils.openView('Cloudinary'); + await sideBarViewUtils.clickAction(SideBarViewActions.UPLOAD); + + await uploadToCloudinaryTab.open(); + await uploadToCloudinaryTab.openAdvancedOptions(); + await uploadToCloudinaryTab.fillCustomPublicId(firstAssetPublicID); + + await uploadToCloudinaryTab.uploadLocalFile(filePath); + + await uploadToCloudinaryTab.close(); + + await activityBarUtils.openView('Cloudinary'); + await sideBarViewUtils.clickAction(SideBarViewActions.REFRESH); + await sideBarViewUtils.validateContentItemsExist([firstAssetPublicID]); + }); +}); + diff --git a/test/e2e/src/utils/ActivityBarUtils.ts b/test/e2e/src/vscodeComponentsUtils/ActivityBarUtils.ts similarity index 100% rename from test/e2e/src/utils/ActivityBarUtils.ts rename to test/e2e/src/vscodeComponentsUtils/ActivityBarUtils.ts diff --git a/test/e2e/src/utils/SideBarViewUtils.ts b/test/e2e/src/vscodeComponentsUtils/SideBarViewUtils.ts similarity index 83% rename from test/e2e/src/utils/SideBarViewUtils.ts rename to test/e2e/src/vscodeComponentsUtils/SideBarViewUtils.ts index 2e571d3..f9785ce 100644 --- a/test/e2e/src/utils/SideBarViewUtils.ts +++ b/test/e2e/src/vscodeComponentsUtils/SideBarViewUtils.ts @@ -2,6 +2,15 @@ import { browser, expect } from "@wdio/globals" import { TreeItem } from "wdio-vscode-service" import allureReporter from '@wdio/allure-reporter' +/** + * Actions available in the Side Bar View. + */ +export enum SideBarViewActions { + UPLOAD = ' Upload', + SEARCH = ' Search', + REFRESH = ' Refresh', +} + /** * Utility class for interacting with the Side Bar View in VS Code. */ @@ -61,6 +70,15 @@ class SideBarViewUtils { /** * Waits for the content of the Side Bar View to load. */ + public async clickAction(action: SideBarViewActions) { + await allureReporter.addStep(`Click the "${action.trim()}" action button`); + const sideBarView = await this.getSideBarView(); + const titlePart = sideBarView.getTitlePart(); + const actionButton = await titlePart.elem.$(`.//*[@title='${action}' or @aria-label='${action}']`); + await actionButton.waitForClickable(); + await actionButton.click(); + } + public async waitContentToLoad() { await allureReporter.addStep('Wait for content to load'); await browser.waitUntil(async () => { diff --git a/test/e2e/src/vscodeComponentsUtils/WebViewUtils.ts b/test/e2e/src/vscodeComponentsUtils/WebViewUtils.ts new file mode 100644 index 0000000..b1ea8a4 --- /dev/null +++ b/test/e2e/src/vscodeComponentsUtils/WebViewUtils.ts @@ -0,0 +1,16 @@ +import { browser } from "@wdio/globals" + + +class WebViewUtils { + /** + * Gets the WebView instance. + */ + public async getWebView(title: string) { + const workbench = await browser.getWorkbench() + return browser.waitUntil(() => workbench.getWebviewByTitle(title), { + timeoutMsg: `WebView with title "${title}" not found`, + }) + } +} + +export const webViewUtils = new WebViewUtils() \ No newline at end of file diff --git a/test/e2e/src/webViewTabs/UploadToCloudinaryTab.ts b/test/e2e/src/webViewTabs/UploadToCloudinaryTab.ts new file mode 100644 index 0000000..ec9958b --- /dev/null +++ b/test/e2e/src/webViewTabs/UploadToCloudinaryTab.ts @@ -0,0 +1,92 @@ +import { browser } from "@wdio/globals" +import allureReporter from '@wdio/allure-reporter' +import { webViewUtils } from "../vscodeComponentsUtils/WebViewUtils.js"; +import { WebView } from "wdio-vscode-service"; + +/** + * Utility class for interacting with the Upload to Cloudinary webview. + */ +class UploadToCloudinaryTab { + + private webview: WebView | null = null; + + /** + * Opens the Upload to Cloudinary webview. + * Must be called before any other interaction methods. + */ + public async open() { + this.webview = await webViewUtils.getWebView('Upload to Cloudinary'); + await this.webview.open(); + } + + /** + * Closes the Upload to Cloudinary webview. + * Uses the stored reference from open() to avoid re-fetching from inside the iframe. + */ + public async close() { + if (!this.webview) { + throw new Error('WebView not opened. Call open() first.'); + } + await this.webview.close(); + this.webview = null; + } + + /** + * Uploads a local file via the upload widget's file input. + * Uses the wdio-vscode-service WebView page object for iframe switching. + */ + public async uploadLocalFile(absoluteFilePath: string) { + await allureReporter.addStep(`Upload file: ${absoluteFilePath}`); + + const fileInput = await browser.$('#fileInput'); + await fileInput.waitForExist(); + await browser.execute((el: any) => { + el.style.display = 'block'; + el.style.opacity = '1'; + }, fileInput as any); + await fileInput.setValue(absoluteFilePath); + + await this.waitForAllUploadsToComplete(); + } + + /** + * Waits for all uploads to complete. + */ + public async waitForAllUploadsToComplete() { + await browser.waitUntil(async () => { + const statuses = await browser.$$('.queue-item__status'); + const count = await statuses.length; + if (count === 0) { + return false + }; + + const textPromises = await statuses.map(el => el.getText()); + const texts = await Promise.all(textPromises); + return texts.every(text => text === 'Complete'); + }, { timeoutMsg: 'Not all uploads completed in time' }); + } + + /** + * Opens the Advanced Options section. + */ + public async openAdvancedOptions() { + await allureReporter.addStep('Open Advanced Options'); + + const header = await browser.$('#advancedHeader'); + await header.waitForClickable(); + await header.click(); + } + + /** + * Fills the Custom Public ID input. + */ + public async fillCustomPublicId(publicId: string) { + await allureReporter.addStep(`Fill Custom Public ID: ${publicId}`); + + const input = await browser.$('#publicIdInput'); + await input.waitForExist(); + await input.setValue(publicId); + } +} + +export const uploadToCloudinaryTab = new UploadToCloudinaryTab() From 83efc4fd9ac8da8e1740b28075bb123fe0cf5075 Mon Sep 17 00:00:00 2001 From: refael-m Date: Tue, 21 Apr 2026 11:48:22 +0300 Subject: [PATCH 2/8] test: e2e add test for upload asset --- test/e2e/specs/uploadFromSideBarView.spec.ts | 28 +++++++++++++++++--- test/e2e/src/utils/pathUtils.ts | 12 +++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/test/e2e/specs/uploadFromSideBarView.spec.ts b/test/e2e/specs/uploadFromSideBarView.spec.ts index 093a94b..ffa194e 100644 --- a/test/e2e/specs/uploadFromSideBarView.spec.ts +++ b/test/e2e/specs/uploadFromSideBarView.spec.ts @@ -4,13 +4,29 @@ import { pathUtils } from "../src/utils/pathUtils.js"; import { SideBarViewActions, sideBarViewUtils } from "../src/vscodeComponentsUtils/SideBarViewUtils.js"; import { activityBarUtils } from "../src/vscodeComponentsUtils/ActivityBarUtils.js"; import { uploadToCloudinaryTab } from "../src/webViewTabs/UploadToCloudinaryTab.js"; +import * as fs from 'node:fs'; +import { expect } from "@wdio/globals"; +import allureReporter from '@wdio/allure-reporter' describe('Upload asset from side bar Upload button', () => { const cloudinarySDK = new CloudinarySDK(); - const filePath = path.join(pathUtils.getTestAssetsPath(), 'sample_png.png'); + const assetPath = path.join(pathUtils.getTestAssetsPath(), 'sample_png.png'); + + const newFileName = `${crypto.randomUUID().substring(0, 8)}.png`; + const newFilePath = path.join(pathUtils.getTempFolderPath(), newFileName); + const firstAssetPublicID = `e2e-test-ae-${crypto.randomUUID().substring(0, 8)}`; + beforeEach(async () => { + try { + // Copy with a unique name so the sidebar shows the display name (not the public ID) in dynamic folder environments + fs.copyFileSync(assetPath, newFilePath); + } catch (error) { + throw new Error('Error copying asset:', error); + } + }); + afterEach(async () => { try { await cloudinarySDK.V2.api.delete_resources([firstAssetPublicID]); @@ -27,13 +43,19 @@ describe('Upload asset from side bar Upload button', () => { await uploadToCloudinaryTab.openAdvancedOptions(); await uploadToCloudinaryTab.fillCustomPublicId(firstAssetPublicID); - await uploadToCloudinaryTab.uploadLocalFile(filePath); + await uploadToCloudinaryTab.uploadLocalFile(newFilePath); await uploadToCloudinaryTab.close(); await activityBarUtils.openView('Cloudinary'); + await sideBarViewUtils.clickAction(SideBarViewActions.REFRESH); - await sideBarViewUtils.validateContentItemsExist([firstAssetPublicID]); + + await sideBarViewUtils.validateContentItemsExist([newFileName.replace('.png', '')]); + + await allureReporter.addStep('Validate that the asset was uploaded with the correct display name'); + const byPublicId = await cloudinarySDK.V2.api.resource(firstAssetPublicID); + expect(byPublicId.display_name).toBe(newFileName.replace('.png', '')); }); }); diff --git a/test/e2e/src/utils/pathUtils.ts b/test/e2e/src/utils/pathUtils.ts index b9d5bc7..f25d8a1 100644 --- a/test/e2e/src/utils/pathUtils.ts +++ b/test/e2e/src/utils/pathUtils.ts @@ -1,5 +1,6 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import * as fs from 'node:fs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -22,6 +23,17 @@ class PathUtils { public getTestAssetsPath() { return path.join(__dirname, '..', '..', 'assets'); } + + /** + * Gets the path to the temp folder. + */ + public getTempFolderPath() { + const tempFolderPath = path.join(__dirname, '..', '..', 'temp'); + if (!fs.existsSync(tempFolderPath)) { + fs.mkdirSync(tempFolderPath, { recursive: true }); + } + return tempFolderPath; + } } export const pathUtils = new PathUtils(); From bfdcdef507f9f2a5c578e8f89c522ebb7a1e5591 Mon Sep 17 00:00:00 2001 From: refael-m Date: Tue, 21 Apr 2026 11:59:43 +0300 Subject: [PATCH 3/8] test: e2e add test for upload asset --- test/e2e/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/e2e/README.md b/test/e2e/README.md index 66c06f4..9f293aa 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -56,6 +56,9 @@ test/e2e/ Tests use [Mocha](https://mochajs.org/) as the test framework and the `wdio-vscode-service` page objects to interact with VS Code. +VS Code page object references: +- [wdio-vscode-service API](https://webdriverio-community.github.io/wdio-vscode-service/modules.html) — full API reference for the WebdriverIO VS Code service. +- [vscode-extension-tester Wiki](https://github.com/redhat-developer/vscode-extension-tester/wiki) — documentation for page objects like ActivityBar, SideBarView, EditorView, WebView, etc. ```ts import { activityBarUtils } from '../src/utils/ActivityBarUtils.js' import { sideBarViewUtils } from '../src/utils/SideBarViewUtils.js' From ab3008d376681b0384ea0606d83e5717a62946a6 Mon Sep 17 00:00:00 2001 From: refael-m Date: Tue, 21 Apr 2026 12:13:13 +0300 Subject: [PATCH 4/8] test: e2e add test for upload asset --- test/e2e/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/e2e/README.md b/test/e2e/README.md index 9f293aa..d4b21b8 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -27,6 +27,12 @@ End-to-end runs require Cloudinary credentials in `process.env`. The `onPrepare` pnpm test:e2e ``` +To run a specific test file: + +```bash +pnpm test:e2e --spec [FILE_PATH] +``` + This will: 1. Download a VS Code binary (if not already cached in `.wdio-vscode-service/`) 2. Launch VS Code with the extension loaded From cc50cd6074cdb1dc8d5656f65770570108864284 Mon Sep 17 00:00:00 2001 From: refael-m Date: Thu, 23 Apr 2026 09:50:08 +0300 Subject: [PATCH 5/8] test: e2e add test for upload asset --- test/e2e/specs/uploadFromSideBarView.spec.ts | 4 +- .../src/webViewTabs/UploadToCloudinaryTab.ts | 28 ++------------ test/e2e/src/webViewTabs/WebViewTabBase.ts | 38 +++++++++++++++++++ 3 files changed, 44 insertions(+), 26 deletions(-) create mode 100644 test/e2e/src/webViewTabs/WebViewTabBase.ts diff --git a/test/e2e/specs/uploadFromSideBarView.spec.ts b/test/e2e/specs/uploadFromSideBarView.spec.ts index ffa194e..2ee7aa6 100644 --- a/test/e2e/specs/uploadFromSideBarView.spec.ts +++ b/test/e2e/specs/uploadFromSideBarView.spec.ts @@ -39,13 +39,13 @@ describe('Upload asset from side bar Upload button', () => { await activityBarUtils.openView('Cloudinary'); await sideBarViewUtils.clickAction(SideBarViewActions.UPLOAD); - await uploadToCloudinaryTab.open(); + await uploadToCloudinaryTab.switchTo(); await uploadToCloudinaryTab.openAdvancedOptions(); await uploadToCloudinaryTab.fillCustomPublicId(firstAssetPublicID); await uploadToCloudinaryTab.uploadLocalFile(newFilePath); - await uploadToCloudinaryTab.close(); + await uploadToCloudinaryTab.switchBack(); await activityBarUtils.openView('Cloudinary'); diff --git a/test/e2e/src/webViewTabs/UploadToCloudinaryTab.ts b/test/e2e/src/webViewTabs/UploadToCloudinaryTab.ts index ec9958b..5acd973 100644 --- a/test/e2e/src/webViewTabs/UploadToCloudinaryTab.ts +++ b/test/e2e/src/webViewTabs/UploadToCloudinaryTab.ts @@ -1,34 +1,14 @@ import { browser } from "@wdio/globals" import allureReporter from '@wdio/allure-reporter' -import { webViewUtils } from "../vscodeComponentsUtils/WebViewUtils.js"; -import { WebView } from "wdio-vscode-service"; +import { WebViewTabBase } from "./WebViewTabBase.js"; /** * Utility class for interacting with the Upload to Cloudinary webview. */ -class UploadToCloudinaryTab { +class UploadToCloudinaryTab extends WebViewTabBase { - private webview: WebView | null = null; - - /** - * Opens the Upload to Cloudinary webview. - * Must be called before any other interaction methods. - */ - public async open() { - this.webview = await webViewUtils.getWebView('Upload to Cloudinary'); - await this.webview.open(); - } - - /** - * Closes the Upload to Cloudinary webview. - * Uses the stored reference from open() to avoid re-fetching from inside the iframe. - */ - public async close() { - if (!this.webview) { - throw new Error('WebView not opened. Call open() first.'); - } - await this.webview.close(); - this.webview = null; + constructor() { + super('Upload to Cloudinary'); } /** diff --git a/test/e2e/src/webViewTabs/WebViewTabBase.ts b/test/e2e/src/webViewTabs/WebViewTabBase.ts new file mode 100644 index 0000000..62712d8 --- /dev/null +++ b/test/e2e/src/webViewTabs/WebViewTabBase.ts @@ -0,0 +1,38 @@ +import { WebView } from "wdio-vscode-service"; +import { webViewUtils } from "../vscodeComponentsUtils/WebViewUtils.js"; + + +/** + * Base class for all webview tabs. + */ +export abstract class WebViewTabBase { + protected title: string; + protected webview: WebView | null = null; + + /** + * Constructor. + * @param title - The title of the webview tab. + */ + constructor(title: string) { + this.title = title; + } + + /** + * Switches to the webview tab. + */ + public async switchTo() { + this.webview = await webViewUtils.getWebView(this.title); + await this.webview.open(); + } + + /** + * Switches back to the main view. + */ + public async switchBack() { + if (!this.webview) { + throw new Error('WebView not opened. Call switchTo() first.'); + } + await this.webview.close(); + this.webview = null; + } +} \ No newline at end of file From a5fbebbcec687f80e444b585b3bbe8b79bce1027 Mon Sep 17 00:00:00 2001 From: refael-m Date: Thu, 23 Apr 2026 14:39:33 +0300 Subject: [PATCH 6/8] test: e2e add test for upload asset --- test/e2e/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/README.md b/test/e2e/README.md index d4b21b8..263e3db 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -63,8 +63,8 @@ test/e2e/ Tests use [Mocha](https://mochajs.org/) as the test framework and the `wdio-vscode-service` page objects to interact with VS Code. VS Code page object references: -- [wdio-vscode-service API](https://webdriverio-community.github.io/wdio-vscode-service/modules.html) — full API reference for the WebdriverIO VS Code service. -- [vscode-extension-tester Wiki](https://github.com/redhat-developer/vscode-extension-tester/wiki) — documentation for page objects like ActivityBar, SideBarView, EditorView, WebView, etc. +- [wdio-vscode-service API](https://s3-proxy.cloudinary.com/cld-web-pages/vscode-page-objects/index.html) — full API reference for the WebdriverIO VS Code service. + ```ts import { activityBarUtils } from '../src/utils/ActivityBarUtils.js' import { sideBarViewUtils } from '../src/utils/SideBarViewUtils.js' From 63edef890695c356adbe5da5b5c06c4ad55962ed Mon Sep 17 00:00:00 2001 From: refael-m Date: Sun, 26 Apr 2026 09:05:04 +0300 Subject: [PATCH 7/8] test: e2e add test for upload asset --- test/e2e/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/README.md b/test/e2e/README.md index 263e3db..9541897 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -63,7 +63,7 @@ test/e2e/ Tests use [Mocha](https://mochajs.org/) as the test framework and the `wdio-vscode-service` page objects to interact with VS Code. VS Code page object references: -- [wdio-vscode-service API](https://s3-proxy.cloudinary.com/cld-web-pages/vscode-page-objects/index.html) — full API reference for the WebdriverIO VS Code service. +- [wdio-vscode-service API](https://jubilant-broccoli-www5lem.pages.github.io/vscode-po/index.html) — full API reference for the WebdriverIO VS Code service. ```ts import { activityBarUtils } from '../src/utils/ActivityBarUtils.js' From 6b78841fa1db07a93560fac29b2b6a6b84209e3e Mon Sep 17 00:00:00 2001 From: refael-m Date: Sun, 26 Apr 2026 12:15:54 +0300 Subject: [PATCH 8/8] test: add test for search --- test/e2e/specs/searchAssetFromSideBar.spec.ts | 44 +++++++++++++++++++ .../vscodeComponentsUtils/InputBoxUtils.ts | 32 ++++++++++++++ .../vscodeComponentsUtils/SideBarViewUtils.ts | 22 ++++++++++ 3 files changed, 98 insertions(+) create mode 100644 test/e2e/specs/searchAssetFromSideBar.spec.ts create mode 100644 test/e2e/src/vscodeComponentsUtils/InputBoxUtils.ts diff --git a/test/e2e/specs/searchAssetFromSideBar.spec.ts b/test/e2e/specs/searchAssetFromSideBar.spec.ts new file mode 100644 index 0000000..1c14386 --- /dev/null +++ b/test/e2e/specs/searchAssetFromSideBar.spec.ts @@ -0,0 +1,44 @@ +import path from 'node:path'; +import crypto from 'node:crypto'; +import { CloudinarySDK } from '../src/sdks/cloudinarySDK.js'; +import { activityBarUtils } from '../src/vscodeComponentsUtils/ActivityBarUtils.js'; +import { SideBarViewActions, sideBarViewUtils } from '../src/vscodeComponentsUtils/SideBarViewUtils.js'; +import { pathUtils } from '../src/utils/pathUtils.js'; +import { inputBoxUtils } from '../src/vscodeComponentsUtils/InputBoxUtils.js'; +import { browser } from '@wdio/globals'; + +describe('Search asset from side bar', () => { + + const cloudinarySDK = new CloudinarySDK(); + const assetPublicID = `${crypto.randomUUID().substring(0, 8)}`; + + beforeEach(async () => { + try { + await cloudinarySDK.V2.uploader.upload( + path.join(pathUtils.getTestAssetsPath(), 'sample_png.png'), + { public_id: assetPublicID } + ); + } catch (error) { + throw new Error('Error uploading asset:', error); + } + }); + + afterEach(async () => { + try { + await cloudinarySDK.V2.api.delete_resources([assetPublicID]); + } catch (error) { + throw new Error('Error deleting asset:', error); + } + }); + + it('should find the uploaded asset via sidebar search', async () => { + await activityBarUtils.openView('Cloudinary'); + + await sideBarViewUtils.clickAction(SideBarViewActions.SEARCH); + + await inputBoxUtils.fillAndConfirm(assetPublicID); + + await sideBarViewUtils.validateContentItemsExist(['Clear Search', assetPublicID]); + await sideBarViewUtils.validateContentItemsNumber(2); + }); +}); diff --git a/test/e2e/src/vscodeComponentsUtils/InputBoxUtils.ts b/test/e2e/src/vscodeComponentsUtils/InputBoxUtils.ts new file mode 100644 index 0000000..e84f9bf --- /dev/null +++ b/test/e2e/src/vscodeComponentsUtils/InputBoxUtils.ts @@ -0,0 +1,32 @@ +import { browser } from "@wdio/globals" +import { InputBox } from "wdio-vscode-service" +import allureReporter from '@wdio/allure-reporter' + +/** + * Utility class for interacting with the VS Code InputBox / Command Palette. + */ +class InputBoxUtils { + + /** + * Opens the VS Code command palette. + */ + public async getInputBox() { + await allureReporter.addStep('Get Input Box instance'); + const inputBox = await browser.$('.quick-input-widget input'); + await inputBox.waitForDisplayed(); + return inputBox; + } + + /** + * Fills an already-visible InputBox with text and confirms. + * Use when an action (e.g. Search) has already opened an InputBox. + */ + public async fillAndConfirm(text: string) { + await allureReporter.addStep(`Fill input box "${text}"`); + const inputBox = await this.getInputBox(); + await inputBox.setValue(text); + await browser.keys('Enter'); + } +} + +export const inputBoxUtils = new InputBoxUtils() diff --git a/test/e2e/src/vscodeComponentsUtils/SideBarViewUtils.ts b/test/e2e/src/vscodeComponentsUtils/SideBarViewUtils.ts index f9785ce..34dbaab 100644 --- a/test/e2e/src/vscodeComponentsUtils/SideBarViewUtils.ts +++ b/test/e2e/src/vscodeComponentsUtils/SideBarViewUtils.ts @@ -62,6 +62,7 @@ class SideBarViewUtils { const itemLabels = await Promise.all( visibleItems.map(item => item.getLabel()) ); + for (const expected of expectedItems) { expect(itemLabels).toContain(expected); } @@ -79,6 +80,27 @@ class SideBarViewUtils { await actionButton.click(); } + /** + * Validates that the Side Bar View contains exactly the expected items and no others. + */ + public async validateContentItemsNumber(expectedItemsNumber: number) { + await allureReporter.addStep(`Validate only ${expectedItemsNumber} items are visible`); + await this.waitContentToLoad(); + const content = await this.getSideBarViewContent(); + const sections = await content.getSections(); + const visibleItems = await sections[0].getVisibleItems() as TreeItem[]; + const itemLabels = await Promise.all( + visibleItems.map(item => item.getLabel()) + ); + + await browser.waitUntil(async () => { + return itemLabels.length === expectedItemsNumber; + }, { timeout: 15000, timeoutMsg: `Expected ${itemLabels.length} items, but got ${expectedItemsNumber} items` }) + } + + /** + * Waits for the content of the Side Bar View to load. + */ public async waitContentToLoad() { await allureReporter.addStep('Wait for content to load'); await browser.waitUntil(async () => {