From e8ed938b4c841a20901ab16908691088c32e1e51 Mon Sep 17 00:00:00 2001 From: Anwarul Islam Date: Tue, 26 Nov 2024 16:26:09 +0600 Subject: [PATCH] feat: collection runner (#3600) Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com> Co-authored-by: nivedin --- .gitignore | 1 + package.json | 2 +- .../unit/fixtures/workspace-access.mock.ts | 3 +- .../__tests__/unit/workspace-access.spec.ts | 14 +- .../hoppscotch-common/assets/scss/styles.scss | 18 + .../assets/themes/base-themes.scss | 2 + packages/hoppscotch-common/locales/en.json | 32 +- packages/hoppscotch-common/package.json | 2 +- .../hoppscotch-common/src/components.d.ts | 68 +++- .../src/components/collections/AddRequest.vue | 6 +- .../src/components/collections/Collection.vue | 27 +- .../components/collections/EditRequest.vue | 2 +- .../components/collections/MyCollections.vue | 19 + .../src/components/collections/Runner.vue | 149 ------- .../src/components/collections/index.vue | 78 ++-- .../src/components/history/index.vue | 2 +- .../src/components/http/TestResult.vue | 18 +- .../src/components/http/TestResultEntry.vue | 92 +++-- .../src/components/http/TestResultEnv.vue | 8 +- .../src/components/http/test/Env.vue | 104 +++++ .../src/components/http/test/Folder.vue | 90 +++++ .../src/components/http/test/Request.vue | 86 ++++ .../src/components/http/test/Response.vue | 35 ++ .../src/components/http/test/ResultFolder.vue | 91 +++++ .../components/http/test/ResultRequest.vue | 113 ++++++ .../src/components/http/test/Runner.vue | 335 ++++++++++++++++ .../src/components/http/test/RunnerMeta.vue | 21 + .../src/components/http/test/RunnerModal.vue | 370 ++++++++++++++++++ .../src/components/http/test/RunnerResult.vue | 104 +++++ .../src/components/http/test/TestResult.vue | 303 ++++++++++++++ .../lenses/ResponseBodyRenderer.vue | 7 +- .../src/components/smart/EnvInput.vue | 4 +- .../src/helpers/RequestRunner.ts | 231 +++++++---- .../editor/extensions/HoppEnvironment.ts | 18 +- .../src/helpers/rest/document.ts | 131 ++++++- .../src/helpers/runner/adapter.ts | 179 +++++++++ .../src/helpers/runner/collection-tree.ts | 42 ++ .../src/newstore/collections.ts | 47 ++- .../hoppscotch-common/src/pages/graphql.vue | 1 - .../hoppscotch-common/src/pages/index.vue | 49 ++- .../hoppscotch-common/src/pages/r/_id.vue | 1 + .../std/inspections/extension.inspector.ts | 4 +- .../hoppscotch-common/src/platform/tab.ts | 11 + .../context-menu/menu/parameter.menu.ts | 3 + .../services/context-menu/menu/url.menu.ts | 2 +- .../src/services/inspection/index.ts | 24 +- .../inspectors/authorization.inspector.ts | 6 +- .../inspectors/environment.inspector.ts | 14 +- .../inspection/inspectors/header.inspector.ts | 3 + .../persistence/__tests__/__mocks__/index.ts | 4 +- .../src/services/persistence/index.ts | 10 +- .../persistence/validation-schemas/index.ts | 55 +-- .../searchers/collections.searcher.ts | 2 +- .../src/services/tab/rest.ts | 4 +- .../test-runner/test-runner.service.ts | 355 +++++++++++++++++ packages/hoppscotch-data/package.json | 2 + .../hoppscotch-data/src/collection/index.ts | 10 +- .../hoppscotch-data/src/collection/v/4.ts | 4 +- .../hoppscotch-data/src/collection/v/5.ts | 36 ++ packages/hoppscotch-data/src/index.ts | 1 + .../hoppscotch-data/src/utils/collection.ts | 13 + .../collections/collections.platform.ts | 27 +- .../platform/collections/collections.sync.ts | 12 +- .../platform/tabState/tabState.platform.ts | 38 ++ .../tailwind.config.ts | 1 + pnpm-lock.yaml | 145 ++++--- 66 files changed, 3201 insertions(+), 490 deletions(-) delete mode 100644 packages/hoppscotch-common/src/components/collections/Runner.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/Env.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/Folder.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/Request.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/Response.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/ResultFolder.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/ResultRequest.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/Runner.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/RunnerMeta.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/RunnerModal.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/RunnerResult.vue create mode 100644 packages/hoppscotch-common/src/components/http/test/TestResult.vue create mode 100644 packages/hoppscotch-common/src/helpers/runner/adapter.ts create mode 100644 packages/hoppscotch-common/src/helpers/runner/collection-tree.ts create mode 100644 packages/hoppscotch-common/src/platform/tab.ts create mode 100644 packages/hoppscotch-common/src/services/test-runner/test-runner.service.ts create mode 100644 packages/hoppscotch-data/src/collection/v/5.ts create mode 100644 packages/hoppscotch-data/src/utils/collection.ts create mode 100644 packages/hoppscotch-selfhost-web/src/platform/tabState/tabState.platform.ts diff --git a/.gitignore b/.gitignore index 71152c462..bdfa619cd 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ pids *.pid *.seed *.pid.lock +*.env # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/package.json b/package.json index b3568633d..a4656c8b4 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "devDependencies": { "@commitlint/cli": "19.5.0", "@commitlint/config-conventional": "19.5.0", - "@hoppscotch/ui": "0.2.1", + "@hoppscotch/ui": "0.2.2", "@types/node": "22.7.6", "cross-env": "7.0.3", "http-server": "14.1.1", diff --git a/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts b/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts index daee18f64..6c79deae3 100644 --- a/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts +++ b/packages/hoppscotch-cli/src/__tests__/unit/fixtures/workspace-access.mock.ts @@ -79,7 +79,7 @@ export const WORKSPACE_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Workspa collectionID: "clx1ldkzs005t10f8rp5u60q7", teamID: "clws3hg58000011o8h07glsb1", title: "RequestA", - request: `{"v":"${RESTReqSchemaVersion}","id":"clpttpdq00003qp16kut6doqv","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"RequestA","method":"GET","params":[],"headers":[],"endpoint":"https://echo.hoppscotch.io","testScript":"pw.test(\\"Correctly inherits auth and headers from the root collection\\", ()=> {\\n pw.expect(pw.response.body.headers[\\"x-test-header\\"]).toBe(\\"Set at root collection\\");\\n pw.expect(pw.response.body.headers[\\"authorization\\"]).toBe(\\"Bearer BearerToken\\");\\n});","preRequestScript":"","requestVariables":[]}`, + request: `{"v":"${RESTReqSchemaVersion}","id":"clpttpdq00003qp16kut6doqv","auth":{"authType":"inherit","authActive":true},"body":{"body":null,"contentType":null},"name":"RequestA","method":"GET","params":[],"headers":[],"endpoint":"https://echo.hoppscotch.io","testScript":"pw.test(\\"Correctly inherits auth and headers from the root collection\\", ()=> {\\n pw.expect(pw.response.body.headers[\\"x-test-header\\"]).toBe(\\"Set at root collection\\");\\n pw.expect(pw.response.body.headers[\\"authorization\\"]).toBe(\\"Bearer BearerToken\\");\\n});","preRequestScript":"","requestVariables":[],"responses":{}}`, }, ], }, @@ -233,6 +233,7 @@ export const TRANSFORMED_DEEPLY_NESTED_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: HoppC 'pw.test("Correctly inherits auth and headers from the root collection", ()=> {\n pw.expect(pw.response.body.headers["x-test-header"]).toBe("Set at root collection");\n pw.expect(pw.response.body.headers["authorization"]).toBe("Bearer BearerToken");\n});', preRequestScript: "", requestVariables: [], + responses: {}, }, ], auth: { diff --git a/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts b/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts index da38e914b..c119a1c55 100644 --- a/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts +++ b/packages/hoppscotch-cli/src/__tests__/unit/workspace-access.spec.ts @@ -31,12 +31,14 @@ const migrateCollections = (collections: unknown[]): HoppCollection[] => { ); } - return collectionSchemaParsedResult.data.map((collection) => { - return { - ...collection, - folders: migrateCollections(collection.folders), - }; - }); + return collectionSchemaParsedResult.data.map( + ({ _ref_id, folders, ...rest }) => { + return { + ...rest, + folders: migrateCollections(folders), + }; + } + ); }; describe("workspace-access", () => { diff --git a/packages/hoppscotch-common/assets/scss/styles.scss b/packages/hoppscotch-common/assets/scss/styles.scss index d62c88be7..e2b37d80a 100644 --- a/packages/hoppscotch-common/assets/scss/styles.scss +++ b/packages/hoppscotch-common/assets/scss/styles.scss @@ -344,26 +344,44 @@ pre.ace_editor { .info-response { color: var(--status-info-color); + &.outlined { + border: 1px solid var(--status-info-color); + } } .success-response { color: var(--status-success-color); + &.outlined { + border: 1px solid var(--status-success-color); + } } .redirect-response { color: var(--status-redirect-color); + &.outlined { + border: 1px solid var(--status-redirect-color); + } } .critical-error-response { color: var(--status-critical-error-color); + &.outlined { + border: 1px solid var(--status-critical-error-color); + } } .server-error-response { color: var(--status-server-error-color); + &.outlined { + border: 1px solid var(--status-server-error-color); + } } .missing-data-response { color: var(--status-missing-data-color); + &.outlined { + border: 1px solid var(--status-missing-data-color); + } } .toasted-container { diff --git a/packages/hoppscotch-common/assets/themes/base-themes.scss b/packages/hoppscotch-common/assets/themes/base-themes.scss index 9bc0a4f30..b5efbcec3 100644 --- a/packages/hoppscotch-common/assets/themes/base-themes.scss +++ b/packages/hoppscotch-common/assets/themes/base-themes.scss @@ -5,7 +5,9 @@ --font-size-tiny: 0.625rem; --line-height-body: 1rem; --upper-primary-sticky-fold: 4.125rem; + --upper-runner-sticky-fold: 4.125rem; --upper-secondary-sticky-fold: 6.188rem; + --upper-runner-sticky-fold: 4.5rem; --upper-tertiary-sticky-fold: 8.25rem; --upper-fourth-sticky-fold: 10.2rem; --upper-mobile-primary-sticky-fold: 6.75rem; diff --git a/packages/hoppscotch-common/locales/en.json b/packages/hoppscotch-common/locales/en.json index 772b7a21e..cce7e9746 100644 --- a/packages/hoppscotch-common/locales/en.json +++ b/packages/hoppscotch-common/locales/en.json @@ -231,6 +231,8 @@ } }, "collection": { + "title": "Collection", + "run": "Run Collection", "created": "Collection created", "different_parent": "Cannot reorder collection with different parent", "edit": "Edit Collection", @@ -341,6 +343,7 @@ "response": "No response received" }, "environment": { + "heading": "Environment", "add_to_global": "Add to Global", "added": "Environment addition", "create_new": "Create new environment", @@ -448,6 +451,7 @@ "invalid_name": "Please provide a name for the folder", "name_length_insufficient": "Folder name should be at least 3 characters long", "new": "New Folder", + "run": "Run Folder", "renamed": "Folder renamed" }, "graphql": { @@ -1069,7 +1073,10 @@ "tests": "Tests", "types": "Types", "variables": "Variables", - "websocket": "WebSocket" + "websocket": "WebSocket", + "all_tests": "All Tests", + "passed": "Passed", + "failed": "Failed" }, "team": { "already_member": "This email is associated with an existing user.", @@ -1137,6 +1144,8 @@ "not_found": "Environment not found." }, "test": { + "requests": "Requests", + "selection": "Selection", "failed": "test failed", "javascript_code": "JavaScript Code", "learn": "Read documentation", @@ -1144,7 +1153,14 @@ "report": "Test Report", "results": "Test Results", "script": "Script", - "snippets": "Snippets" + "snippets": "Snippets", + "run": "Run", + "run_again": "Run again", + "stop": "Stop", + "new_run": "New Run", + "iterations": "Iterations", + "duration": "Duration", + "avg_resp": "Avg. Response Time" }, "websocket": { "communication": "Communication", @@ -1194,7 +1210,17 @@ "cli_environment_id_description": "This environment ID will be used by the CLI collection runner for Hoppscotch.", "include_active_environment": "Include active environment:", "cli": "CLI", - "ui": "Runner (coming soon)", + "delay": "Delay", + "ui": "Runner", + "running_collection": "Running collection", + "run_config": "Run Configuration", + "advanced_settings": "Advanced Settings", + "stop_on_error": "Stop run if an error occurs", + "persist_responses": "Persist responses", + "collection_not_found": "Collection not found. May be deleted or moved.", + "empty_collection": "Collection is empty. Add requests to run.", + "no_response_persist": "The collection runner is presently configured not to persist responses. This setting prevents showing the response data. To modify this behavior, initiate a new run configuration.", + "select_request": "Select a request to see response and test results", "cli_command_generation_description_cloud": "Copy the below command and run it from the CLI. Please specify a personal access token.", "cli_command_generation_description_sh": "Copy the below command and run it from the CLI. Please specify a personal access token and verify the generated SH instance server URL.", "cli_command_generation_description_sh_with_server_url_placeholder": "Copy the below command and run it from the CLI. Please specify a personal access token and the SH instance server URL.", diff --git a/packages/hoppscotch-common/package.json b/packages/hoppscotch-common/package.json index 51e3be7c5..b1454e649 100644 --- a/packages/hoppscotch-common/package.json +++ b/packages/hoppscotch-common/package.json @@ -38,7 +38,7 @@ "@hoppscotch/data": "workspace:^", "@hoppscotch/httpsnippet": "3.0.6", "@hoppscotch/js-sandbox": "workspace:^", - "@hoppscotch/ui": "0.2.1", + "@hoppscotch/ui": "0.2.2", "@hoppscotch/vue-toasted": "0.1.0", "@lezer/highlight": "1.2.0", "@noble/curves": "1.6.0", diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index a77795471..8f59d99ce 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -7,6 +7,9 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + '(chore': fix broken runner for user collection) + '(feat': collection runner config in modal) + '(fix': run again function) AccessTokens: typeof import('./components/accessTokens/index.vue')['default'] AccessTokensGenerateModal: typeof import('./components/accessTokens/GenerateModal.vue')['default'] AccessTokensList: typeof import('./components/accessTokens/List.vue')['default'] @@ -14,6 +17,7 @@ declare module 'vue' { AiexperimentsMergeView: typeof import('./components/aiexperiments/MergeView.vue')['default'] AiexperimentsModifyBodyModal: typeof import('./components/aiexperiments/ModifyBodyModal.vue')['default'] AppActionHandler: typeof import('./components/app/ActionHandler.vue')['default'] + AppAnnouncement: (typeof import("./components/app/Announcement.vue"))["default"] AppBanner: typeof import('./components/app/Banner.vue')['default'] AppContextMenu: typeof import('./components/app/ContextMenu.vue')['default'] AppDeveloperOptions: typeof import('./components/app/DeveloperOptions.vue')['default'] @@ -41,6 +45,8 @@ declare module 'vue' { AppSpotlightSearch: typeof import('./components/app/SpotlightSearch.vue')['default'] AppSupport: typeof import('./components/app/Support.vue')['default'] AppWhatsNewDialog: typeof import('./components/app/WhatsNewDialog.vue')['default'] + ButtonPrimary: (typeof import("./../../hoppscotch-ui/src/components/button/Primary.vue"))["default"] + ButtonSecondary: (typeof import("./../../hoppscotch-ui/src/components/button/Secondary.vue"))["default"] Collections: typeof import('./components/collections/index.vue')['default'] CollectionsAdd: typeof import('./components/collections/Add.vue')['default'] CollectionsAddFolder: typeof import('./components/collections/AddFolder.vue')['default'] @@ -66,7 +72,7 @@ declare module 'vue' { CollectionsMyCollections: typeof import('./components/collections/MyCollections.vue')['default'] CollectionsProperties: typeof import('./components/collections/Properties.vue')['default'] CollectionsRequest: typeof import('./components/collections/Request.vue')['default'] - CollectionsRunner: typeof import('./components/collections/Runner.vue')['default'] + CollectionsRunner: (typeof import("./components/collections/Runner.vue"))["default"] CollectionsSaveRequest: typeof import('./components/collections/SaveRequest.vue')['default'] CollectionsTeamCollections: typeof import('./components/collections/TeamCollections.vue')['default'] CookiesAllModal: typeof import('./components/cookies/AllModal.vue')['default'] @@ -107,8 +113,10 @@ declare module 'vue' { HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary'] HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary'] HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor'] + HoppSmartAutoComplete: (typeof import("@hoppscotch/ui"))["HoppSmartAutoComplete"] HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox'] HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'] + HoppSmartExpand: (typeof import("@hoppscotch/ui"))["HoppSmartExpand"] HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip'] HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput'] HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection'] @@ -129,6 +137,8 @@ declare module 'vue' { HoppSmartTree: typeof import('@hoppscotch/ui')['HoppSmartTree'] HoppSmartWindow: typeof import('@hoppscotch/ui')['HoppSmartWindow'] HoppSmartWindows: typeof import('@hoppscotch/ui')['HoppSmartWindows'] + HoppTestEnv: (typeof import("@hoppscotch/ui"))["HoppTestEnv"] + HoppTestRunnerModal: (typeof import("@hoppscotch/ui"))["HoppTestRunnerModal"] HttpAuthorization: typeof import('./components/http/Authorization.vue')['default'] HttpAuthorizationAkamaiEG: typeof import('./components/http/authorization/AkamaiEG.vue')['default'] HttpAuthorizationApiKey: typeof import('./components/http/authorization/ApiKey.vue')['default'] @@ -143,6 +153,7 @@ declare module 'vue' { HttpBodyParameters: typeof import('./components/http/BodyParameters.vue')['default'] HttpCodegen: typeof import('./components/http/Codegen.vue')['default'] HttpCodegenModal: typeof import('./components/http/CodegenModal.vue')['default'] + HttpCollectionRunner: (typeof import("./components/http/CollectionRunner.vue"))["default"] HttpExampleLenseBodyRenderer: typeof import('./components/http/example/LenseBodyRenderer.vue')['default'] HttpExampleResponse: typeof import('./components/http/example/Response.vue')['default'] HttpExampleResponseMeta: typeof import('./components/http/example/ResponseMeta.vue')['default'] @@ -151,6 +162,7 @@ declare module 'vue' { HttpHeaders: typeof import('./components/http/Headers.vue')['default'] HttpImportCurl: typeof import('./components/http/ImportCurl.vue')['default'] HttpKeyValue: typeof import('./components/http/KeyValue.vue')['default'] + HttpOAuth2Authorization: (typeof import("./components/http/OAuth2Authorization.vue"))["default"] HttpParameters: typeof import('./components/http/Parameters.vue')['default'] HttpPreRequestScript: typeof import('./components/http/PreRequestScript.vue')['default'] HttpRawBody: typeof import('./components/http/RawBody.vue')['default'] @@ -162,19 +174,36 @@ declare module 'vue' { HttpResponse: typeof import('./components/http/Response.vue')['default'] HttpResponseInterface: typeof import('./components/http/ResponseInterface.vue')['default'] HttpResponseMeta: typeof import('./components/http/ResponseMeta.vue')['default'] + HttpRunner: (typeof import("./components/http/Runner.vue"))["default"] HttpSaveResponseName: typeof import('./components/http/SaveResponseName.vue')['default'] HttpSidebar: typeof import('./components/http/Sidebar.vue')['default'] HttpTabHead: typeof import('./components/http/TabHead.vue')['default'] + HttpTestEnv: typeof import('./components/http/test/Env.vue')['default'] + HttpTestFolder: typeof import('./components/http/test/Folder.vue')['default'] + HttpTestRequest: typeof import('./components/http/test/Request.vue')['default'] + HttpTestResponse: typeof import('./components/http/test/Response.vue')['default'] HttpTestResult: typeof import('./components/http/TestResult.vue')['default'] HttpTestResultEntry: typeof import('./components/http/TestResultEntry.vue')['default'] HttpTestResultEnv: typeof import('./components/http/TestResultEnv.vue')['default'] + HttpTestResultFolder: typeof import('./components/http/test/ResultFolder.vue')['default'] HttpTestResultReport: typeof import('./components/http/TestResultReport.vue')['default'] + HttpTestResultRequest: typeof import('./components/http/test/ResultRequest.vue')['default'] + HttpTestRunner: typeof import('./components/http/test/Runner.vue')['default'] + HttpTestRunnerConfig: typeof import('./components/http/test/RunnerConfig.vue')['default'] + HttpTestRunnerMeta: typeof import('./components/http/test/RunnerMeta.vue')['default'] + HttpTestRunnerModal: typeof import('./components/http/test/RunnerModal.vue')['default'] + HttpTestRunnerResult: typeof import('./components/http/test/RunnerResult.vue')['default'] HttpTests: typeof import('./components/http/Tests.vue')['default'] + HttpTestSelector: (typeof import("./components/http/test/Selector.vue"))["default"] + HttpTestSelectRequest: (typeof import("./components/http/test/SelectRequest.vue"))["default"] + HttpTestTestResult: typeof import('./components/http/test/TestResult.vue')['default'] HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default'] IconLucideActivity: typeof import('~icons/lucide/activity')['default'] + IconLucideAlertCircle: (typeof import("~icons/lucide/alert-circle"))["default"] IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default'] IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default'] + IconLucideBrush: (typeof import("~icons/lucide/brush"))["default"] IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] IconLucideGlobe: typeof import('~icons/lucide/globe')['default'] @@ -184,10 +213,12 @@ declare module 'vue' { IconLucideLayers: typeof import('~icons/lucide/layers')['default'] IconLucideListEnd: typeof import('~icons/lucide/list-end')['default'] IconLucideMinus: typeof import('~icons/lucide/minus')['default'] - IconLucideRss: typeof import('~icons/lucide/rss')['default'] + IconLucidePlay: (typeof import("~icons/lucide/play"))["default"] + IconLucidePlaySquare: (typeof import("~icons/lucide/play-square"))["default"] + IconLucideRss: (typeof import("~icons/lucide/rss"))["default"] IconLucideSearch: typeof import('~icons/lucide/search')['default'] IconLucideUsers: typeof import('~icons/lucide/users')['default'] - IconLucideVerified: typeof import('~icons/lucide/verified')['default'] + IconLucideVerified: (typeof import("~icons/lucide/verified"))["default"] IconLucideX: typeof import('~icons/lucide/x')['default'] ImportExportBase: typeof import('./components/importExport/Base.vue')['default'] ImportExportImportExportList: typeof import('./components/importExport/ImportExportList.vue')['default'] @@ -214,6 +245,8 @@ declare module 'vue' { LensesRenderersVideoLensRenderer: typeof import('./components/lenses/renderers/VideoLensRenderer.vue')['default'] LensesRenderersXMLLensRenderer: typeof import('./components/lenses/renderers/XMLLensRenderer.vue')['default'] LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default'] + ProfileShortcode: (typeof import("./components/profile/Shortcode.vue"))["default"] + ProfileShortcodes: (typeof import("./components/profile/Shortcodes.vue"))["default"] ProfileUserDelete: typeof import('./components/profile/UserDelete.vue')['default'] RealtimeCommunication: typeof import('./components/realtime/Communication.vue')['default'] RealtimeConnectionConfig: typeof import('./components/realtime/ConnectionConfig.vue')['default'] @@ -228,14 +261,43 @@ declare module 'vue' { ShareCustomizeModal: typeof import('./components/share/CustomizeModal.vue')['default'] ShareModal: typeof import('./components/share/Modal.vue')['default'] ShareRequest: typeof import('./components/share/Request.vue')['default'] + ShareRequestModal: (typeof import("./components/share/RequestModal.vue"))["default"] + ShareShareRequestModal: (typeof import("./components/share/ShareRequestModal.vue"))["default"] ShareTemplatesButton: typeof import('./components/share/templates/Button.vue')['default'] ShareTemplatesEmbeds: typeof import('./components/share/templates/Embeds.vue')['default'] ShareTemplatesLink: typeof import('./components/share/templates/Link.vue')['default'] SmartAccentModePicker: typeof import('./components/smart/AccentModePicker.vue')['default'] + SmartAnchor: (typeof import("./../../hoppscotch-ui/src/components/smart/Anchor.vue"))["default"] + SmartAutoComplete: (typeof import("./../../hoppscotch-ui/src/components/smart/AutoComplete.vue"))["default"] SmartChangeLanguage: typeof import('./components/smart/ChangeLanguage.vue')['default'] + SmartCheckbox: (typeof import("./../../hoppscotch-ui/src/components/smart/Checkbox.vue"))["default"] SmartColorModePicker: typeof import('./components/smart/ColorModePicker.vue')['default'] + SmartConfirmModal: (typeof import("./../../hoppscotch-ui/src/components/smart/ConfirmModal.vue"))["default"] SmartEncodingPicker: typeof import('./components/smart/EncodingPicker.vue')['default'] SmartEnvInput: typeof import('./components/smart/EnvInput.vue')['default'] + SmartExpand: (typeof import("./../../hoppscotch-ui/src/components/smart/Expand.vue"))["default"] + SmartFileChip: (typeof import("./../../hoppscotch-ui/src/components/smart/FileChip.vue"))["default"] + SmartInput: (typeof import("./../../hoppscotch-ui/src/components/smart/Input.vue"))["default"] + SmartIntersection: (typeof import("./../../hoppscotch-ui/src/components/smart/Intersection.vue"))["default"] + SmartItem: (typeof import("./../../hoppscotch-ui/src/components/smart/Item.vue"))["default"] + SmartLink: (typeof import("./../../hoppscotch-ui/src/components/smart/Link.vue"))["default"] + SmartModal: (typeof import("./../../hoppscotch-ui/src/components/smart/Modal.vue"))["default"] + SmartPicture: (typeof import("./../../hoppscotch-ui/src/components/smart/Picture.vue"))["default"] + SmartPlaceholder: (typeof import("./../../hoppscotch-ui/src/components/smart/Placeholder.vue"))["default"] + SmartProgressRing: (typeof import("./../../hoppscotch-ui/src/components/smart/ProgressRing.vue"))["default"] + SmartRadio: (typeof import("./../../hoppscotch-ui/src/components/smart/Radio.vue"))["default"] + SmartRadioGroup: (typeof import("./../../hoppscotch-ui/src/components/smart/RadioGroup.vue"))["default"] + SmartSelectWrapper: (typeof import("./../../hoppscotch-ui/src/components/smart/SelectWrapper.vue"))["default"] + SmartSlideOver: (typeof import("./../../hoppscotch-ui/src/components/smart/SlideOver.vue"))["default"] + SmartSpinner: (typeof import("./../../hoppscotch-ui/src/components/smart/Spinner.vue"))["default"] + SmartTab: (typeof import("./../../hoppscotch-ui/src/components/smart/Tab.vue"))["default"] + SmartTable: (typeof import("./../../hoppscotch-ui/src/components/smart/Table.vue"))["default"] + SmartTabs: (typeof import("./../../hoppscotch-ui/src/components/smart/Tabs.vue"))["default"] + SmartToggle: (typeof import("./../../hoppscotch-ui/src/components/smart/Toggle.vue"))["default"] + SmartTree: (typeof import("./../../hoppscotch-ui/src/components/smart/Tree.vue"))["default"] + SmartTreeBranch: (typeof import("./../../hoppscotch-ui/src/components/smart/TreeBranch.vue"))["default"] + SmartWindow: (typeof import("./../../hoppscotch-ui/src/components/smart/Window.vue"))["default"] + SmartWindows: (typeof import("./../../hoppscotch-ui/src/components/smart/Windows.vue"))["default"] TabPrimary: typeof import('./components/tab/Primary.vue')['default'] TabSecondary: typeof import('./components/tab/Secondary.vue')['default'] Teams: typeof import('./components/teams/index.vue')['default'] diff --git a/packages/hoppscotch-common/src/components/collections/AddRequest.vue b/packages/hoppscotch-common/src/components/collections/AddRequest.vue index 26eb32a01..1b4f19875 100644 --- a/packages/hoppscotch-common/src/components/collections/AddRequest.vue +++ b/packages/hoppscotch-common/src/components/collections/AddRequest.vue @@ -157,10 +157,8 @@ watch( () => props.show, (show) => { if (show) { - if (tabs.currentActiveTab.value.document.type === "example-response") - return - - editingName.value = tabs.currentActiveTab.value.document.request.name + if (tabs.currentActiveTab.value.document.type === "request") + editingName.value = tabs.currentActiveTab.value.document.request.name } } ) diff --git a/packages/hoppscotch-common/src/components/collections/Collection.vue b/packages/hoppscotch-common/src/components/collections/Collection.vue index 4fb467ab0..3de4eb428 100644 --- a/packages/hoppscotch-common/src/components/collections/Collection.vue +++ b/packages/hoppscotch-common/src/components/collections/Collection.vue @@ -74,7 +74,6 @@ @click="emit('add-folder')" /> + - @@ -298,6 +296,7 @@ const emit = defineEmits<{ (event: "toggle-children"): void (event: "add-request"): void (event: "add-folder"): void + (event: "run-collection"): void (event: "edit-collection"): void (event: "edit-properties"): void (event: "duplicate-collection"): void diff --git a/packages/hoppscotch-common/src/components/collections/EditRequest.vue b/packages/hoppscotch-common/src/components/collections/EditRequest.vue index 7ab69bf2e..ed45e7add 100644 --- a/packages/hoppscotch-common/src/components/collections/EditRequest.vue +++ b/packages/hoppscotch-common/src/components/collections/EditRequest.vue @@ -114,7 +114,7 @@ const t = useI18n() const props = withDefaults( defineProps<{ show: boolean - loadingState: boolean + loadingState?: boolean modelValue?: string requestContext: HoppRESTRequest | null }>(), diff --git a/packages/hoppscotch-common/src/components/collections/MyCollections.vue b/packages/hoppscotch-common/src/components/collections/MyCollections.vue index fcf1706d5..d90729542 100644 --- a/packages/hoppscotch-common/src/components/collections/MyCollections.vue +++ b/packages/hoppscotch-common/src/components/collections/MyCollections.vue @@ -64,6 +64,12 @@ folder: node.data.data.data, }) " + @run-collection=" + emit('run-collection', { + collectionIndex: node.id, + collection: node.data.data.data, + }) + " @edit-collection=" node.data.type === 'collections' && emit('edit-collection', { @@ -133,6 +139,12 @@ }) " folder-type="folder" + @run-collection=" + emit('run-collection', { + collectionIndex: node.id, + collection: node.data.data.data, + }) + " @add-request=" node.data.type === 'folders' && emit('add-request', { @@ -493,6 +505,13 @@ const emit = defineEmits<{ folder: HoppCollection } ): void + ( + event: "run-collection", + payload: { + collectionIndex: string + collection: HoppCollection + } + ): void ( event: "edit-collection", payload: { diff --git a/packages/hoppscotch-common/src/components/collections/Runner.vue b/packages/hoppscotch-common/src/components/collections/Runner.vue deleted file mode 100644 index 5ffcdffc0..000000000 --- a/packages/hoppscotch-common/src/components/collections/Runner.vue +++ /dev/null @@ -1,149 +0,0 @@ - - - diff --git a/packages/hoppscotch-common/src/components/collections/index.vue b/packages/hoppscotch-common/src/components/collections/index.vue index 2363feef5..65ad9976e 100644 --- a/packages/hoppscotch-common/src/components/collections/index.vue +++ b/packages/hoppscotch-common/src/components/collections/index.vue @@ -33,6 +33,13 @@ :filter-text="filterTexts" :save-request="saveRequest" :picked="picked" + @run-collection=" + runCollectionHandler({ + type: 'my-collections', + collectionID: $event.collection._ref_id, + collectionIndex: $event.collectionIndex, + }) + " @add-folder="addFolder" @add-request="addRequest" @edit-request="editRequest" @@ -99,7 +106,12 @@ @remove-folder="removeFolder" @remove-request="removeRequest" @remove-response="removeResponse" - @run-collection="runCollectionHandler" + @run-collection=" + runCollectionHandler({ + type: 'team-collections', + collectionID: $event, + }) + " @share-request="shareRequest" @select-request="selectRequest" @select-response="selectResponse" @@ -193,11 +205,9 @@ /> - @@ -207,6 +217,7 @@ import { useI18n } from "@composables/i18n" import { useToast } from "@composables/toast" import { + getDefaultRESTRequest, HoppCollection, HoppRESTAuth, HoppRESTHeaders, @@ -218,7 +229,7 @@ import * as TE from "fp-ts/TaskEither" import { pipe } from "fp-ts/function" import { cloneDeep, debounce, isEqual } from "lodash-es" import { PropType, computed, nextTick, onMounted, ref, watch } from "vue" -import { useReadonlyStream, useStream } from "~/composables/stream" +import { useReadonlyStream } from "~/composables/stream" import { defineActionHandler, invokeAction } from "~/helpers/actions" import { GQLError } from "~/helpers/backend/GQLClient" import { @@ -278,10 +289,7 @@ import { updateRESTCollectionOrder, updateRESTRequestOrder, } from "~/newstore/collections" -import { - selectedEnvironmentIndex$, - setSelectedEnvironmentIndex, -} from "~/newstore/environments" + import { useLocalState } from "~/newstore/localstate" import { currentReorderingStatus$ } from "~/newstore/reordering" import { platform } from "~/platform" @@ -292,7 +300,7 @@ import { TeamWorkspace, WorkspaceService } from "~/services/workspace.service" import { RESTOptionTabs } from "../http/RequestOptions.vue" import { Collection as NodeCollection } from "./MyCollections.vue" import { EditingProperties } from "./Properties.vue" -import { getDefaultRESTRequest } from "~/helpers/rest/default" +import { CollectionRunnerData } from "../http/test/RunnerModal.vue" const t = useI18n() const toast = useToast() @@ -383,15 +391,6 @@ const teamLoadingCollections = useReadonlyStream( [] ) const teamEnvironmentAdapter = new TeamEnvironmentAdapter(undefined) -const teamEnvironmentList = useReadonlyStream( - teamEnvironmentAdapter.teamEnvironmentList$, - [] -) -const selectedEnvironmentIndex = useStream( - selectedEnvironmentIndex$, - { type: "NO_ENV_SELECTED" }, - setSelectedEnvironmentIndex -) const { cascadeParentCollectionForHeaderAuthForSearchResults, @@ -692,8 +691,7 @@ const showConfirmModal = ref(false) const showTeamModalAdd = ref(false) const showCollectionsRunnerModal = ref(false) -const selectedCollectionID = ref(null) -const activeEnvironmentID = ref(null) +const collectionRunnerData = ref(null) const displayModalAdd = (show: boolean) => { showModalAdd.value = show @@ -837,7 +835,9 @@ const onAddRequest = (requestName: string) => { if (!request) return const newRequest = { - ...cloneDeep(request), + ...(tabs.currentActiveTab.value.document.type === "request" + ? cloneDeep(tabs.currentActiveTab.value.document.request) + : getDefaultRESTRequest()), name: requestName, } @@ -849,9 +849,9 @@ const onAddRequest = (requestName: string) => { const { auth, headers } = cascadeParentCollectionForHeaderAuth(path, "rest") tabs.createNewTab({ + type: "request", request: newRequest, isDirty: false, - type: "request", saveContext: { originLocation: "user-collection", folderPath: path, @@ -904,9 +904,9 @@ const onAddRequest = (requestName: string) => { const { auth, headers } = teamCollectionAdapter.cascadeParentCollectionForHeaderAuth(path) tabs.createNewTab({ + type: "request", request: newRequest, isDirty: false, - type: "request", saveContext: { originLocation: "team-collection", requestID: createRequestInCollection.id, @@ -1973,13 +1973,13 @@ const selectRequest = (selectedRequest: { requestID: requestIndex, }) - if (possibleTab) { + if (possibleTab && possibleTab.value.document.type === "request") { tabs.setActiveTab(possibleTab.value.id) } else { tabs.createNewTab({ + type: "request", request: cloneDeep(request), isDirty: false, - type: "request", saveContext: { originLocation: "team-collection", requestID: requestIndex, @@ -2004,9 +2004,9 @@ const selectRequest = (selectedRequest: { } else { // If not, open the request in a new tab tabs.createNewTab({ + type: "request", request: cloneDeep(request), isDirty: false, - type: "request", saveContext: { originLocation: "user-collection", folderPath: folderPath!, @@ -2866,25 +2866,9 @@ const setCollectionProperties = (newCollection: { displayModalEditProperties(false) } -const runCollectionHandler = (collectionID: string) => { - selectedCollectionID.value = collectionID +const runCollectionHandler = (payload: CollectionRunnerData) => { + collectionRunnerData.value = payload showCollectionsRunnerModal.value = true - - const activeWorkspace = workspace.value - const currentEnv = selectedEnvironmentIndex.value - - if (["NO_ENV_SELECTED", "MY_ENV"].includes(currentEnv.type)) { - activeEnvironmentID.value = null - return - } - - if (activeWorkspace.type === "team" && currentEnv.type === "TEAM_ENV") { - activeEnvironmentID.value = teamEnvironmentList.value.find( - (env) => - env.teamID === activeWorkspace.teamID && - env.environment.id === currentEnv.environment.id - )?.environment.id - } } const resolveConfirmModal = (title: string | null) => { diff --git a/packages/hoppscotch-common/src/components/history/index.vue b/packages/hoppscotch-common/src/components/history/index.vue index 04f82a0a8..750994a46 100644 --- a/packages/hoppscotch-common/src/components/history/index.vue +++ b/packages/hoppscotch-common/src/components/history/index.vue @@ -298,9 +298,9 @@ const clearHistory = () => { const tabs = useService(RESTTabService) const useHistory = (entry: RESTHistoryEntry) => { tabs.createNewTab({ + type: "request", request: entry.request, isDirty: false, - type: "request", }) } diff --git a/packages/hoppscotch-common/src/components/http/TestResult.vue b/packages/hoppscotch-common/src/components/http/TestResult.vue index ca15e5ba7..84da5ba04 100644 --- a/packages/hoppscotch-common/src/components/http/TestResult.vue +++ b/packages/hoppscotch-common/src/components/http/TestResult.vue @@ -135,9 +135,7 @@ :key="`result-${index}`" class="flex items-center px-4 py-2" > -
+
() +const props = withDefaults( + defineProps<{ + modelValue: HoppTestResult | null | undefined + showEmptyMessage?: boolean + }>(), + { + showEmptyMessage: true, + } +) const emit = defineEmits<{ (e: "update:modelValue", val: HoppTestResult | null | undefined): void diff --git a/packages/hoppscotch-common/src/components/http/TestResultEntry.vue b/packages/hoppscotch-common/src/components/http/TestResultEntry.vue index 4464b9ad4..1367e6084 100644 --- a/packages/hoppscotch-common/src/components/http/TestResultEntry.vue +++ b/packages/hoppscotch-common/src/components/http/TestResultEntry.vue @@ -8,57 +8,87 @@
-
- -
- - {{ result.message }} - - - - {{ - result.status === "pass" ? t("test.passed") : t("test.failed") - }} - +
+ +
+ + {{ result.message }} + + + + {{ + result.status === "pass" ? t("test.passed") : t("test.failed") + }} + +
-
+
diff --git a/packages/hoppscotch-common/src/components/http/TestResultEnv.vue b/packages/hoppscotch-common/src/components/http/TestResultEnv.vue index fc942d0f2..e34de8249 100644 --- a/packages/hoppscotch-common/src/components/http/TestResultEnv.vue +++ b/packages/hoppscotch-common/src/components/http/TestResultEnv.vue @@ -1,6 +1,6 @@