diff --git a/package.json b/package.json index 4512af597..c56d8ff13 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postcat", - "version": "0.5.0", + "version": "0.5.1", "main": "out/app/electron-main/main.js", "description": "A lightweight, extensible API tool", "homepage": "https://github.com/Postcatlab/postcat.git", diff --git a/src/browser/locale/messages.xlf b/src/browser/locale/messages.xlf index a3636420d..d5bf905ab 100644 --- a/src/browser/locale/messages.xlf +++ b/src/browser/locale/messages.xlf @@ -77,49 +77,49 @@ Close Other Tags (excluding current tabs) src/app/components/eo-ui/tab/tab.component.html - 74,75 + 17,18 Close All Tabs src/app/components/eo-ui/tab/tab.component.html - 76 + 19 Close Tabs To the Left src/app/components/eo-ui/tab/tab.component.html - 78,79 + 21,22 Close Tabs to the Right src/app/components/eo-ui/tab/tab.component.html - 86,87 + 24,25 Do you want to save the changes? src/app/components/eo-ui/tab/tab.component.ts - 93 + 98 Your changes will be lost if you don't save them. src/app/components/eo-ui/tab/tab.component.ts - 94 + 99 Cancel src/app/components/eo-ui/tab/tab.component.ts - 99 + 104 src/app/pages/components/user-modal/user-modal.component.ts @@ -158,14 +158,18 @@ Don't Save src/app/components/eo-ui/tab/tab.component.ts - 106 + 111 Save src/app/components/eo-ui/tab/tab.component.ts - 114 + 119 + + + src/app/pages/components/extension/detail/components/extensions-settings.component.ts + 12 src/app/pages/workspace/project/api/env/env-edit/env-edit.component.html @@ -239,19 +243,19 @@ src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 330 + 334 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 357 + 361 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 373 + 377 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 397 + 401 @@ -278,15 +282,15 @@ src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 346 + 350 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 365 + 369 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 381 + 385 src/app/pages/workspace/project/api/components/history/eo-history.component.html @@ -1103,8 +1107,8 @@ 225 - - Save Success + + Saved successfully src/app/pages/components/extension/detail/components/extensions-settings.component.ts 34 @@ -1692,7 +1696,7 @@ src/app/pages/workspace/project/api/http/mock/api-mock.service.ts - 135 + 141 src/app/pages/workspace/project/api/store/api-effect.service.ts @@ -1707,7 +1711,7 @@ src/app/pages/workspace/project/api/http/mock/api-mock.service.ts - 138 + 144 src/app/pages/workspace/project/api/store/api-effect.service.ts @@ -1717,6 +1721,10 @@ src/app/pages/workspace/project/api/store/api-effect.service.ts 257 + + src/app/pages/workspace/project/api/store/api-effect.service.ts + 284 + Edit workspace failed @@ -1926,7 +1934,7 @@ src/app/pages/workspace/project/api/group-edit/group.component.ts - 96 + 98 @@ -1973,7 +1981,7 @@ src/app/pages/workspace/project/api/http/mock/api-mock.service.ts - 111 + 113 @@ -2101,7 +2109,7 @@ src/app/pages/workspace/project/api/http/mock/api-mock.service.ts - 122 + 124 @@ -2120,7 +2128,7 @@ src/app/pages/workspace/project/api/http/mock/api-mock.service.ts - 125 + 127 @@ -2373,7 +2381,7 @@ src/app/pages/workspace/project/api/group-edit/group.component.html - 31 + 34 src/app/pages/workspace/project/api/http/test/api-test-ui.component.html @@ -2398,25 +2406,25 @@ Import API src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 59 + 60 Import From File src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 60 + 61 Sync API from URL src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 65 + 66 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 66 + 67 src/app/pages/workspace/project/setting/project-setting.component.ts @@ -2431,47 +2439,47 @@ Case src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 252 + 256 does not support sorting at the moment, please report to Github Issue src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 255 + 259 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 259 + 263 Add Mock src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 334 + 338 Add Case src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 338 + 342 Copy src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 342 + 346 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 361 + 365 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 377 + 381 @Copy @@ -2479,21 +2487,21 @@ Add API src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 389 + 393 Add Subgroup src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 393 + 397 Delete src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 401 + 405 @@ -2502,6 +2510,10 @@ src/app/pages/workspace/project/api/components/group/api-group.service.ts 38 + + src/app/pages/workspace/project/api/http/mock/api-mock.service.ts + 136 + src/app/pages/workspace/project/api/http/test/api-case.service.ts 41 @@ -2786,14 +2798,14 @@ Authorzation will take effect for each API under the group during testing, and you can also override the authorization in the API. src/app/pages/workspace/project/api/group-edit/group.component.html - 33,34 + 36,37 Save src/app/pages/workspace/project/api/group-edit/group.component.html - 45,46 + 48,49 src/app/pages/workspace/project/api/http/mock/mock.component.html @@ -2804,21 +2816,21 @@ Edited Group Info successfully src/app/pages/workspace/project/api/group-edit/group.component.ts - 79 + 81 Edited Group Name successfully src/app/pages/workspace/project/api/group-edit/group.component.ts - 157 + 159 Created Group successfully src/app/pages/workspace/project/api/group-edit/group.component.ts - 163 + 165 @@ -3082,6 +3094,13 @@ 84 + + + + src/app/pages/workspace/project/api/http/mock/api-mock.service.ts + 137 + + Please input mock name @@ -3210,11 +3229,11 @@ 218 - - Please input case name + + Curl text error: - src/app/pages/workspace/project/api/http/test/api-test.component.html - 5 + src/app/pages/workspace/project/api/http/test/api-test-ui.component.ts + 486 @@ -3457,7 +3476,7 @@ The test service connection failed, Request Body Too Large src/app/pages/workspace/project/api/service/test-server/remote-node/test-connect.service.ts - 46 + 45 @@ -3465,7 +3484,7 @@ If the current test URL is a local API, please download the desktop and re-initiate the test. src/app/pages/workspace/project/api/service/test-server/test-server.service.ts - 150 + 154 diff --git a/src/browser/locale/messages.zh.xlf b/src/browser/locale/messages.zh.xlf index 8178a6755..e9a1e7d07 100644 --- a/src/browser/locale/messages.zh.xlf +++ b/src/browser/locale/messages.zh.xlf @@ -86,7 +86,7 @@ Close Other Tags (excluding current tabs) src/app/components/eo-ui/tab/tab.component.html - 74,75 + 17,18 关闭其它标签(不包括当前标签) @@ -94,7 +94,7 @@ Close All Tabs src/app/components/eo-ui/tab/tab.component.html - 76 + 19 关闭所有标签 @@ -102,7 +102,7 @@ Close Tabs To the Left src/app/components/eo-ui/tab/tab.component.html - 78,79 + 21,22 关闭左侧标签页 @@ -110,7 +110,7 @@ Close Tabs to the Right src/app/components/eo-ui/tab/tab.component.html - 86,87 + 24,25 关闭右边的标签 @@ -118,7 +118,7 @@ Do you want to save the changes? src/app/components/eo-ui/tab/tab.component.ts - 93 + 98 您要保存这些更改吗? @@ -126,7 +126,7 @@ Your changes will be lost if you don't save them. src/app/components/eo-ui/tab/tab.component.ts - 94 + 99 如未保存,所有更改将会被丢弃。 @@ -134,7 +134,7 @@ Cancel src/app/components/eo-ui/tab/tab.component.ts - 99 + 104 src/app/pages/components/user-modal/user-modal.component.ts @@ -174,7 +174,7 @@ Don't Save src/app/components/eo-ui/tab/tab.component.ts - 106 + 111 放弃保存 @@ -182,7 +182,11 @@ Save src/app/components/eo-ui/tab/tab.component.ts - 114 + 119 + + + src/app/pages/components/extension/detail/components/extensions-settings.component.ts + 12 src/app/pages/workspace/project/api/env/env-edit/env-edit.component.html @@ -262,19 +266,19 @@ src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 330 + 334 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 357 + 361 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 373 + 377 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 397 + 401 编辑 @@ -302,15 +306,15 @@ src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 346 + 350 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 365 + 369 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 381 + 385 src/app/pages/workspace/project/api/components/history/eo-history.component.html @@ -1230,8 +1234,8 @@ 游客 - - Save Success + + Saved successfully src/app/pages/components/extension/detail/components/extensions-settings.component.ts 34 @@ -1884,7 +1888,7 @@ src/app/pages/workspace/project/api/http/mock/api-mock.service.ts - 135 + 141 src/app/pages/workspace/project/api/store/api-effect.service.ts @@ -1900,7 +1904,7 @@ src/app/pages/workspace/project/api/http/mock/api-mock.service.ts - 138 + 144 src/app/pages/workspace/project/api/store/api-effect.service.ts @@ -1910,6 +1914,10 @@ src/app/pages/workspace/project/api/store/api-effect.service.ts 257 + + src/app/pages/workspace/project/api/store/api-effect.service.ts + 284 + 删除成功 @@ -2140,7 +2148,7 @@ src/app/pages/workspace/project/api/group-edit/group.component.ts - 96 + 98 新建分组 @@ -2192,7 +2200,7 @@ src/app/pages/workspace/project/api/http/mock/api-mock.service.ts - 111 + 113 新建 Mock @@ -2332,7 +2340,7 @@ src/app/pages/workspace/project/api/http/mock/api-mock.service.ts - 122 + 124 添加失败 @@ -2352,7 +2360,7 @@ src/app/pages/workspace/project/api/http/mock/api-mock.service.ts - 125 + 127 添加成功 @@ -2636,7 +2644,7 @@ src/app/pages/workspace/project/api/group-edit/group.component.html - 31 + 34 src/app/pages/workspace/project/api/http/test/api-test-ui.component.html @@ -2664,7 +2672,7 @@ Import API src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 59 + 60 导入 API @@ -2672,7 +2680,7 @@ Import From File src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 60 + 61 从文件导入 API @@ -2680,11 +2688,11 @@ Sync API from URL src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 65 + 66 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 66 + 67 src/app/pages/workspace/project/setting/project-setting.component.ts @@ -2700,7 +2708,7 @@ Case src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 252 + 256 用例 @@ -2708,11 +2716,11 @@ does not support sorting at the moment, please report to Github Issue src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 255 + 259 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 259 + 263 目前不支持排序,请反馈到 Github Issue @@ -2720,7 +2728,7 @@ Add Mock src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 334 + 338 添加 Mock @@ -2728,7 +2736,7 @@ Add Case src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 338 + 342 添加用例 @@ -2736,15 +2744,15 @@ Copy src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 342 + 346 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 361 + 365 src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 377 + 381 @Copy 复制 @@ -2753,7 +2761,7 @@ Add API src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 389 + 393 添加 API @@ -2761,7 +2769,7 @@ Add Subgroup src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 393 + 397 添加子分组 @@ -2769,7 +2777,7 @@ Delete src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts - 401 + 405 删除 @@ -2779,6 +2787,10 @@ src/app/pages/workspace/project/api/components/group/api-group.service.ts 38 + + src/app/pages/workspace/project/api/http/mock/api-mock.service.ts + 136 + src/app/pages/workspace/project/api/http/test/api-case.service.ts 41 @@ -3097,7 +3109,7 @@ Authorzation will take effect for each API under the group during testing, and you can also override the authorization in the API. src/app/pages/workspace/project/api/group-edit/group.component.html - 33,34 + 36,37 在测试过程中,鉴权将当前分组下的所有 API 生效,您也可以在 API 测试中覆盖鉴权。 @@ -3105,7 +3117,7 @@ Save src/app/pages/workspace/project/api/group-edit/group.component.html - 45,46 + 48,49 src/app/pages/workspace/project/api/http/mock/mock.component.html @@ -3117,7 +3129,7 @@ Edited Group Info successfully src/app/pages/workspace/project/api/group-edit/group.component.ts - 79 + 81 修改分组信息成功 @@ -3125,7 +3137,7 @@ Edited Group Name successfully src/app/pages/workspace/project/api/group-edit/group.component.ts - 157 + 159 修改分组名称成功 @@ -3133,7 +3145,7 @@ Created Group successfully src/app/pages/workspace/project/api/group-edit/group.component.ts - 163 + 165 新建分组成功 @@ -3425,6 +3437,13 @@ 必填 + + + + src/app/pages/workspace/project/api/http/mock/api-mock.service.ts + 137 + + Please input mock name @@ -3569,13 +3588,13 @@ 保存为文件 - - Please input case name + + Curl text error: - src/app/pages/workspace/project/api/http/test/api-test.component.html - 5 + src/app/pages/workspace/project/api/http/test/api-test-ui.component.ts + 486 - 请输入用例名称 + Curl 文本错误: Save as API @@ -3845,7 +3864,7 @@ The test service connection failed, Request Body Too Large src/app/pages/workspace/project/api/service/test-server/remote-node/test-connect.service.ts - 46 + 45 测试服务连接失败,请求体太大 @@ -3854,7 +3873,7 @@ If the current test URL is a local API, please download the desktop and re-initiate the test. src/app/pages/workspace/project/api/service/test-server/test-server.service.ts - 150 + 154 服务连接失败。 当前正在使用服务器测试,如果当前测试 URL 为 本地 API,请下载桌面端后重新发起测试。 diff --git a/src/browser/src/app/components/eo-ui/tab/tab-operate.service.ts b/src/browser/src/app/components/eo-ui/tab/tab-operate.service.ts index 646a3e6ee..e68310d50 100644 --- a/src/browser/src/app/components/eo-ui/tab/tab-operate.service.ts +++ b/src/browser/src/app/components/eo-ui/tab/tab-operate.service.ts @@ -339,8 +339,7 @@ export class TabOperateService { getCurrentTab() { return this.getTabByIndex(this.selectedIndex); } - closeTabByOperate(action: string | TabOperate) { - const currentTabID = this.tabStorage.tabOrder[this.selectedIndex]; + closeTabByOperate(action: string | TabOperate, currentTabID = this.tabStorage.tabOrder[this.selectedIndex]) { let tabsObj = { //Close tab has hasChanged tab needTips: false, diff --git a/src/browser/src/app/components/eo-ui/tab/tab.component.html b/src/browser/src/app/components/eo-ui/tab/tab.component.html index 2ba4ade1d..f9a7061c8 100644 --- a/src/browser/src/app/components/eo-ui/tab/tab.component.html +++ b/src/browser/src/app/components/eo-ui/tab/tab.component.html @@ -6,7 +6,25 @@ (nzSelectChange)="selectChange($event)" [nzTabBarExtraContent]="extraTemplate" > - + + +
    +
  • + Close Other Tags (excluding current tabs) +
  • +
  • Close All Tabs
  • +
  • + Close Tabs To the Left +
  • +
  • + Close Tabs to the Right +
  • +
+
val === uuid) === 0; + } + checkIsLastTab(uuid) { + return this.tabStorage.tabOrder.length - 1 === this.tabStorage.tabOrder.findIndex(val => val === uuid); + } /** * Tab Close Operate * * @param action */ - closeTabByOperate(action: TabOperate | string) { - this.tabOperate.closeTabByOperate(action); + closeTabByOperate(action: TabOperate | string, uuid?) { + this.tabOperate.closeTabByOperate(action, uuid); } private watchRouterChange() { this.routerSubscribe = this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe((res: NavigationEnd) => { diff --git a/src/browser/src/app/core/services/theme/theme.service.ts b/src/browser/src/app/core/services/theme/theme.service.ts index fa09aea5f..87ecbcbff 100644 --- a/src/browser/src/app/core/services/theme/theme.service.ts +++ b/src/browser/src/app/core/services/theme/theme.service.ts @@ -68,7 +68,16 @@ export class ThemeService { queryExtensionThemes() { const extensions = this.themeExtension.getExtensionThemes(this.coreThemes); - this.themes.push(...extensions); + extensions.forEach(val => { + const index = this.themes.findIndex(val1 => val1.id === val.id); + //Not exsit + if (index === -1) { + this.themes.push(val); + return; + } + //Has exist + this.themes.splice(index, 1, val); + }); } changeEditorTheme(currentTheme = StorageUtil.get('pc_theme')) { const editorTheme = this.getEditorTheme(currentTheme); diff --git a/src/browser/src/app/pages/components/extension/detail/components/extensions-settings.component.ts b/src/browser/src/app/pages/components/extension/detail/components/extensions-settings.component.ts index 9d9c9695a..74dfeac8e 100644 --- a/src/browser/src/app/pages/components/extension/detail/components/extensions-settings.component.ts +++ b/src/browser/src/app/pages/components/extension/detail/components/extensions-settings.component.ts @@ -9,7 +9,7 @@ import { SettingService } from 'pc/browser/src/app/components/system-setting/set class="sticky top-0 py-[12px] border-solid border-0 border-b-[1px] z-10 mb-[3px]" style="border-color: var(--border-color); background-color: var(--background-color); border-bottom: 1px solid var(--system-border-color);" > - +
@@ -31,6 +31,6 @@ export class ExtensionSettingComponent implements OnInit { } handleSave = () => { this.settingService.saveSetting(this.localSettings); - this.feedback.success($localize`Save Success`); + this.feedback.success($localize`Saved successfully`); }; } diff --git a/src/browser/src/app/pages/workspace/project/api/components/group/api-group-tree.component.html b/src/browser/src/app/pages/workspace/project/api/components/group/api-group-tree.component.html index 04103ac19..4a050872e 100644 --- a/src/browser/src/app/pages/workspace/project/api/components/group/api-group-tree.component.html +++ b/src/browser/src/app/pages/workspace/project/api/components/group/api-group-tree.component.html @@ -64,7 +64,7 @@
-
+
{{ origin.title }}
+ +
    + +
  • + {{ item.title }} +
  • +
    +
+
- -
    - -
  • - {{ item.title }} -
  • -
    -
-
diff --git a/src/browser/src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts b/src/browser/src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts index 5cb968533..7551a2382 100644 --- a/src/browser/src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/components/group/api-group-tree.component.ts @@ -2,6 +2,7 @@ import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { EoNgFeedbackMessageService } from 'eo-ng-feedback'; import { action, autorun, reaction, toJS } from 'mobx'; +import { NzContextMenuService, NzDropdownMenuComponent } from 'ng-zorro-antd/dropdown'; import { NzTreeComponent, NzFormatEmitEvent, NzTreeNodeOptions, NzTreeNode, NzFormatBeforeDropEvent } from 'ng-zorro-antd/tree'; import { PageUniqueName } from 'pc/browser/src/app/pages/workspace/project/api/api-tab.service'; import { ApiGroupService } from 'pc/browser/src/app/pages/workspace/project/api/components/group/api-group.service'; @@ -93,11 +94,14 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy { private feedback: EoNgFeedbackMessageService, private router: Router, private route: ActivatedRoute, + private nzContextMenuService: NzContextMenuService, @Inject(BASIC_TABS_INFO) public tabsConfig: TabsConfig ) { this.operateByModule = this.getGroupOperate(); } - + contextMenu($event: MouseEvent, menu: NzDropdownMenuComponent): void { + this.nzContextMenuService.create($event, menu); + } searchFunc = (node: NzTreeNodeOptions) => { const { title } = node; const uri = node.relationInfo?.uri; diff --git a/src/browser/src/app/pages/workspace/project/api/group-edit/group.component.html b/src/browser/src/app/pages/workspace/project/api/group-edit/group.component.html index 133f52e81..61c054252 100644 --- a/src/browser/src/app/pages/workspace/project/api/group-edit/group.component.html +++ b/src/browser/src/app/pages/workspace/project/api/group-edit/group.component.html @@ -20,6 +20,9 @@

{{ validateForm.value?.name }}

+
diff --git a/src/browser/src/app/pages/workspace/project/api/group-edit/group.component.ts b/src/browser/src/app/pages/workspace/project/api/group-edit/group.component.ts index cc437a924..0abf02c50 100644 --- a/src/browser/src/app/pages/workspace/project/api/group-edit/group.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/group-edit/group.component.ts @@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { EoNgFeedbackMessageService } from 'eo-ng-feedback'; import { EditTabViewComponent } from 'pc/browser/src/app/components/eo-ui/tab/tab.model'; import { AuthorizationExtensionFormComponent } from 'pc/browser/src/app/pages/workspace/project/api/components/authorization-extension-form/authorization-extension-form.component'; +import { ApiGroupService } from 'pc/browser/src/app/pages/workspace/project/api/components/group/api-group.service'; import { AuthTypeValue } from 'pc/browser/src/app/pages/workspace/project/api/constants/auth.model'; import { ApiService } from 'pc/browser/src/app/services/storage/api.service'; import { Group } from 'pc/browser/src/app/services/storage/db/models'; @@ -39,6 +40,7 @@ export class GroupComponent implements OnDestroy, EditTabViewComponent { private effect: ApiEffectService, public globalStore: StoreService, private fb: FormBuilder, + public group: ApiGroupService, private feedback: EoNgFeedbackMessageService, private route: ActivatedRoute, private router: Router, diff --git a/src/browser/src/app/pages/workspace/project/api/http/mock/api-mock.service.ts b/src/browser/src/app/pages/workspace/project/api/http/mock/api-mock.service.ts index 4a4ea55a4..574a595f6 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/mock/api-mock.service.ts +++ b/src/browser/src/app/pages/workspace/project/api/http/mock/api-mock.service.ts @@ -7,6 +7,7 @@ import { ApiTestUtilService } from 'pc/browser/src/app/pages/workspace/project/a import { ProjectApiService } from 'pc/browser/src/app/pages/workspace/project/api/service/project-api.service'; import { ApiEffectService } from 'pc/browser/src/app/pages/workspace/project/api/store/api-effect.service'; import { syncUrlAndQuery } from 'pc/browser/src/app/pages/workspace/project/api/utils/api.utils'; +import { ModalService } from 'pc/browser/src/app/services/modal.service'; import { ApiService } from 'pc/browser/src/app/services/storage/api.service'; import { MockCreateWay } from 'pc/browser/src/app/services/storage/db/models'; import { ApiData } from 'pc/browser/src/app/services/storage/db/models/apiData'; @@ -27,6 +28,7 @@ export class ApiMockService { private message: EoNgFeedbackMessageService, private apiEffect: ApiEffectService, private projectApi: ProjectApiService, + private modalService: ModalService, @Inject(BASIC_TABS_INFO) public tabsConfig: TabsConfig ) { this.mockOperateUrl = this.tabsConfig.pathByName[PageUniqueName.HttpMock]; @@ -130,13 +132,19 @@ export class ApiMockService { }); } async toDelete(id: number) { - const data = await this.deleteMock(id); - if (!data) { - this.message.error($localize`Failed to delete`); - return; - } - this.message.success($localize`Successfully deleted`); - this.apiEffect.deleteMockDetail(); + const modelRef = this.modalService.confirm({ + nzTitle: $localize`Deletion Confirmation?`, + nzContent: $localize``, + nzOnOk: async () => { + const data = await this.deleteMock(id); + if (!data) { + this.message.error($localize`Failed to delete`); + return; + } + this.message.success($localize`Successfully deleted`); + this.apiEffect.deleteMockDetail(); + } + }); } async copy(mock_id: string) { const [res] = await this.api.api_mockDetail({ id: mock_id }); diff --git a/src/browser/src/app/pages/workspace/project/api/http/test/api-test-ui.component.ts b/src/browser/src/app/pages/workspace/project/api/http/test/api-test-ui.component.ts index 6f447c218..21ffc2293 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/test/api-test-ui.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/http/test/api-test-ui.component.ts @@ -1,6 +1,5 @@ import { Component, - OnInit, OnDestroy, ChangeDetectorRef, Input, @@ -9,13 +8,13 @@ import { ViewChild, ElementRef, AfterViewInit, - HostListener, OnChanges, Inject, TemplateRef } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; +import { EoNgFeedbackMessageService } from 'eo-ng-feedback'; import { isEmpty, isEqual } from 'lodash-es'; import { autorun, reaction } from 'mobx'; import { NzResizeEvent } from 'ng-zorro-antd/resizable'; @@ -51,14 +50,7 @@ import { interval, Subscription, Subject } from 'rxjs'; import { takeUntil, distinctUntilChanged, takeWhile } from 'rxjs/operators'; import { enumsToArr, JSONParse } from '../../../../../../shared/utils/index.utils'; -import { - ApiBodyType, - ApiParamsType, - BASIC_TABS_INFO, - BodyContentType as ContentTypeEnum, - RequestMethod, - TabsConfig -} from '../../constants/api.model'; +import { ApiBodyType, ApiParamsType, BASIC_TABS_INFO, RequestMethod, TabsConfig } from '../../constants/api.model'; import { ApiParamsNumPipe } from '../../pipe/api-param-num.pipe'; import { ApiTestUtilService } from '../../service/api-test-util.service'; import { TestServerService } from '../../service/test-server/test-server.service'; @@ -144,6 +136,7 @@ export class ApiTestUiComponent implements AfterViewInit, OnDestroy, OnChanges { private project: ProjectApiService, private elementRef: ElementRef, private apiEdit: ApiEditUtilService, + private feedback: EoNgFeedbackMessageService, private trace: TraceService, @Inject(BASIC_TABS_INFO) public tabsConfig: TabsConfig ) { @@ -275,12 +268,24 @@ export class ApiTestUiComponent implements AfterViewInit, OnDestroy, OnChanges { private fixedHeaderAndContentType() { const bodyType = this.model.request?.apiAttrInfo?.contentType; if (bodyType !== ApiBodyType.Binary) { + //* User customer headers first + const userCustomerHeader = this.model.request.requestParams.headerParams.find( + //@ts-ignore + val => val.name.toLowerCase() === 'content-type' && !val.disableEdit + ); + if (userCustomerHeader) { + const contentType = this.getContentTypeByBodyType(); + this.model.userSelectedContentType = contentType as ContentType; + return; + } + + //* app set header default const contentType = this.getContentTypeByBodyType(); this.model.request.requestParams.headerParams = this.apiTestUtil.addOrReplaceContentType( contentType, this.model.request.requestParams.headerParams ); - this.model.userSelectedContentType ??= contentType as ContentType; + this.model.userSelectedContentType = contentType as ContentType; return; } @@ -294,6 +299,7 @@ export class ApiTestUiComponent implements AfterViewInit, OnDestroy, OnChanges { } changeBodyType($event) { StorageUtil.set('api_test_body_type', $event); + this.fixedHeaderAndContentType(); } handleBottomTabSelect(tab) { if (tab.index === 2) { @@ -474,9 +480,14 @@ export class ApiTestUiComponent implements AfterViewInit, OnDestroy, OnChanges { this.validateForm = this.fb.group(controls); this.validateForm.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(x => { - //Watch uri changes + //Import curl when uri match if (x?.uri?.trim().startsWith('curl')) { - this.model = this.apiTestUtil.getTestDataFromCurl(x.uri, this.model); + const [result, err] = this.apiTestUtil.getTestDataFromCurl(x.uri, this.model); + if (err) { + this.feedback.error($localize`Curl text error: ${err}`); + return; + } + this.model = result; this.validateForm.patchValue({ uri: this.model.request.uri, method: this.model.request.apiAttrInfo?.requestMethod diff --git a/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.html b/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.html index 6265307d9..e8497379d 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.html +++ b/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.html @@ -1,8 +1,8 @@
-
+ - + ; response: ApiTestResData }): ApiData { inData = eoDeepCopy(inData); - pcConsole.log('formatUIApiDataToStorage', inData); + // pcConsole.log('formatUIApiDataToStorage', inData); const result = { ...inData.request, protocol: Protocol.HTTP, @@ -192,52 +194,73 @@ export class ApiTestUtilService { /** * Parse curl to test formdata */ - getTestDataFromCurl(text, originModel: testViewModel): testViewModel { + getTestDataFromCurl(text, originModel: testViewModel): [testViewModel, string?] { const result: testViewModel = eoDeepCopy(originModel); + let requestObj; try { - const requestObj = parseCurl(text || ''); - console.log(requestObj); + requestObj = parseCurl(text || ''); + console.log('getTestDataFromCurl', requestObj); + } catch (e) { + pcConsole.error(`parseCurl error: ${e}`); + return [result, e.message]; + } + result.request.uri = requestObj.url; + //@ts-ignore + result.request.apiAttrInfo.requestMethod = RequestMethod[requestObj.method]; + //Set Query + result.request.requestParams.queryParams = Object.keys(requestObj.query) + .map(name => ({ + name, + value: requestObj.query[name] + })) + .map(val => { + return { + name: val.name, + isRequired: 1, + paramAttr: { + example: val.value + } + }; + }); - result.request.uri = requestObj.url; - result.request.apiAttrInfo.requestMethod = RequestMethod[requestObj.method]; - //Set Query - result.request.requestParams.queryParams = Object.keys(requestObj.query) - .map(name => ({ - name, - value: requestObj.query[name] - })) - .map(val => { - return { - name: val.name, - isRequired: 1, - paramAttr: { - example: val.value - } - }; - }); + //Set Header + result.request.requestParams.headerParams = Object.keys(requestObj.header) + .map(name => ({ + name, + value: requestObj.header[name] + })) + //Ignore some headers + .filter(val => !IGNORE_HEADERS.includes(val.name.toLowerCase())) + .map(val => { + return { + name: val.name, + isRequired: 1, + paramAttr: { + example: val.value + } + }; + }); - //Set Header - result.request.requestParams.headerParams = Object.keys(requestObj.header) - .map(name => ({ - name, - value: requestObj.header[name] - })) - //Ignore some headers - .filter(val => !IGNORE_HEADERS.includes(val.name.toLowerCase())) - .map(val => { - return { - name: val.name, + //Set body and contentType + const formDataContentType = FORMDATA_CONTENT_TYPE_BY_ABRIDGE.find(val => requestObj.contentType?.includes(val.value)); + if (formDataContentType) { + result.request.apiAttrInfo.contentType = ApiBodyType.FormData; + if (formDataContentType.value === 'application/x-www-form-urlencoded') { + //Urlencode formdata + const formArr: BodyParam[] = []; + new URLSearchParams(requestObj.body).forEach((val, name) => { + formArr.push({ + name, + dataType: ApiParamsType.string, isRequired: 1, paramAttr: { - example: val.value + example: val } - }; + }); }); - - //Set body and contentType - if (FORMDATA_CONTENT_TYPE_BY_ABRIDGE.find(val => requestObj.contentType.includes(val.value))) { - result.request.apiAttrInfo.contentType = ApiBodyType.FormData; - //Get formdata + result.request.requestParams.bodyParams = formArr; + } else { + //Multipart-formdata const formArr: BodyParam[] = []; const nameReg = /name=\"(.+?)\"/; requestObj.body @@ -247,6 +270,7 @@ export class ApiTestUtilService { const fIndex = Math.floor(index / 2); //FormName if (!formArr[fIndex]) { + debugger; const name = val.match(nameReg)[1]; formArr[fIndex] = { name: name, @@ -260,19 +284,17 @@ export class ApiTestUtilService { formArr[fIndex].paramAttr.example = val; }); result.request.requestParams.bodyParams = formArr; - } else { - result.request.apiAttrInfo.contentType = ApiBodyType.Raw; - result.request.requestParams.bodyParams = [ - { - binaryRawData: requestObj.body - } - ]; } - } catch (e) { - pcConsole.error(`parseCurl error: ${e}`); + } else { + result.request.apiAttrInfo.contentType = ApiBodyType.Raw; + result.request.requestParams.bodyParams = [ + { + binaryRawData: requestObj.body + } + ]; } console.log('getTestDataFromCurl', result); - return result; + return [result]; } getContentType(headers = []) { const existHeader = headers.find(val => val.name.toLowerCase() === 'content-type'); @@ -287,7 +309,7 @@ export class ApiTestUtilService { */ addOrReplaceContentType(contentType: ContentType | string, headers: HeaderParam[] | any = []) { const existHeader = headers.find(val => val.name.toLowerCase() === 'content-type'); - if (existHeader?.paramAttr) { + if (existHeader?.paramAttr && existHeader.disableEdit) { existHeader.paramAttr.example = contentType; return headers; } @@ -297,7 +319,8 @@ export class ApiTestUtilService { name: 'content-type', paramAttr: { example: contentType - } + }, + disableEdit: true }, ...headers ]; diff --git a/src/browser/src/app/pages/workspace/project/api/service/test-server/remote-node/test-connect.service.ts b/src/browser/src/app/pages/workspace/project/api/service/test-server/remote-node/test-connect.service.ts index f23daf0cb..504183dfa 100644 --- a/src/browser/src/app/pages/workspace/project/api/service/test-server/remote-node/test-connect.service.ts +++ b/src/browser/src/app/pages/workspace/project/api/service/test-server/remote-node/test-connect.service.ts @@ -32,7 +32,6 @@ export class TestServerRemoteService extends TestServerService { xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); xhr.onreadystatechange = e => { if (xhr.readyState === XMLHttpRequest.DONE) { - console.log(xhr.status); switch (xhr.status) { case 200: { this.receiveMessage(this.formatResponseData(JSON.parse(xhr.responseText).data)); diff --git a/src/browser/src/app/pages/workspace/project/api/service/test-server/test-server.service.ts b/src/browser/src/app/pages/workspace/project/api/service/test-server/test-server.service.ts index 5faf5656e..79ecc31c1 100644 --- a/src/browser/src/app/pages/workspace/project/api/service/test-server/test-server.service.ts +++ b/src/browser/src/app/pages/workspace/project/api/service/test-server/test-server.service.ts @@ -145,8 +145,12 @@ export abstract class TestServerService implements TestServer { contentType: ['formData', 'raw', 'json', 'xml', 'binary'][history.requestInfo.requestType] || 'raw' } }; - - if (response.statusCode === 0 && !this.electron.isElectron) { + console.log(response.body); + if ( + response.statusCode === 0 && + ['getaddrinfo enotfound'].some(val => response.body.toLowerCase().includes(val)) && + !this.electron.isElectron + ) { response.body = $localize`Service connection failed. The server test is currently being used.\nIf the current test URL is a local API, please download the desktop and re-initiate the test.`; } result = { diff --git a/src/browser/src/app/pages/workspace/project/api/store/api-effect.service.ts b/src/browser/src/app/pages/workspace/project/api/store/api-effect.service.ts index 55c791890..d5d1952d4 100644 --- a/src/browser/src/app/pages/workspace/project/api/store/api-effect.service.ts +++ b/src/browser/src/app/pages/workspace/project/api/store/api-effect.service.ts @@ -281,6 +281,7 @@ export class ApiEffectService { if (err) { return; } + this.feedback.success($localize`Successfully deleted`); const envList = this.store.getEnvList.filter(it => it.id !== id); this.store.setEnvList(envList); } diff --git a/src/browser/src/app/pages/workspace/project/api/utils/parse-curl.utils.ts b/src/browser/src/app/pages/workspace/project/api/utils/parse-curl.utils.ts index 9e47c921e..c5ace7c58 100644 --- a/src/browser/src/app/pages/workspace/project/api/utils/parse-curl.utils.ts +++ b/src/browser/src/app/pages/workspace/project/api/utils/parse-curl.utils.ts @@ -63,7 +63,7 @@ export const parseCurl = function (s) { args.forEach(function (arg) { switch (true) { case isURL(arg): - out.url = arg.split('?')[0]; + out.url = arg; out.query = getQueryFromURL(arg); break; @@ -115,7 +115,10 @@ export const parseCurl = function (s) { break; case 'data': if (out.method === 'GET' || out.method === 'HEAD') out.method = 'POST'; - out.header['Content-Type'] ??= out.header['Content-Type'] || out.header['content-type'] || 'application/x-www-form-urlencoded'; + if (!out.header['content-Type'] && !out.header['Content-Type']) { + out.header['content-type'] ??= + out.header['Content-Type'] || out.header['content-type'] || 'application/x-www-form-urlencoded'; + } out.body = out.body ? `${out.body}&${arg}` : arg; state = ''; break; @@ -135,6 +138,6 @@ export const parseCurl = function (s) { break; } }); - out.contentType = out.header['Content-Type']; + out.contentType = out.header['Content-Type'] || out.header['content-type']; return out; }; diff --git a/src/browser/src/app/shared/utils/index.utils.ts b/src/browser/src/app/shared/utils/index.utils.ts index aef787db6..afd3a6968 100644 --- a/src/browser/src/app/shared/utils/index.utils.ts +++ b/src/browser/src/app/shared/utils/index.utils.ts @@ -79,10 +79,12 @@ export const whatTextType = (tmpText): 'xml' | 'json' | 'html' | 'text' => { * @param enum */ export const enumsToObject = tEnum => - Object.entries(tEnum).reduce((acc, [key, value]) => { - acc[value] = key; - return acc; - }, {}); + Object.values(tEnum) + .filter(val => !isNumber(val)) + .reduce((acc, val) => { + acc[tEnum[val]] = val; + return acc; + }, {}); /** * Reverse Typescript enums key and value * diff --git a/src/node/test-server/request/libs/apiUtil.js b/src/node/test-server/request/libs/apiUtil.js index 2721f53d3..5b8252c6c 100644 --- a/src/node/test-server/request/libs/apiUtil.js +++ b/src/node/test-server/request/libs/apiUtil.js @@ -439,6 +439,7 @@ privateFun.parseBeforeCode = async function (scritEngines = 'pm', inputData, inp //Get runtime instance const ctx = await pmRuntime.createContextAsync({ timeout: 10000, disableLegacyAPIs: true }); + let tmpEnvGlobals = Object.assign({}, global.eoTestGlobals || {}, tmpBasicEnv.envParam || {}); const context = { enviroment: [], request: { @@ -463,7 +464,7 @@ privateFun.parseBeforeCode = async function (scritEngines = 'pm', inputData, inp }, header: Object.keys(inputData.headers).map(keyName => ({ key: keyName, value: inputData.headers[keyName] })) }, - globals: Object.keys(global.eoTestGlobals).map(keyName => ({ key: keyName, type: 'any', value: global.eoTestGlobals[keyName] })) + globals: Object.keys(tmpEnvGlobals).map(keyName => ({ key: keyName, type: 'any', value: tmpEnvGlobals[keyName] })) }; switch (inputData.requestType) { case '0': { @@ -751,6 +752,7 @@ privateFun.parseBeforeCode = async function (scritEngines = 'pm', inputData, inp } } let tmpEnvGlobals = Object.assign({}, global.eoTestGlobals || {}, tmpEnviroments || {}); + tmpOutput.url = tmpTargetTypeData.apiUrl.split('?')[0]; for (let key in tmpEnvGlobals) { let val = tmpEnvGlobals[key]; let templateParamObject = {}; @@ -760,7 +762,7 @@ privateFun.parseBeforeCode = async function (scritEngines = 'pm', inputData, inp delete tmp_query_param_obj[tmp_query_param_key]; tmp_query_param_obj[_LibsCommon.replaceAll('{{' + key + '}}', val || '', tmp_query_param_key)] = tmp_query_param_val; } - tmpOutput.url = _LibsCommon.replaceAll('{{' + key + '}}', val || '', tmpTargetTypeData.apiUrl.split('?')[0]); + tmpOutput.url = _LibsCommon.replaceAll('{{' + key + '}}', val || '', tmpOutput.url); for (let childKey in tmpHeaders) { tmpHeaders[childKey] = _LibsCommon.replaceAll('{{' + key + '}}', val, tmpHeaders[childKey]); if (childKey.indexOf('{{' + key + '}}') > -1) { diff --git a/src/node/test-server/request/libs/http.package.js b/src/node/test-server/request/libs/http.package.js index 372b399b0..3bfc26691 100644 --- a/src/node/test-server/request/libs/http.package.js +++ b/src/node/test-server/request/libs/http.package.js @@ -208,14 +208,14 @@ errObj: /getaddrinfo enotfound/i.test(tmpInputErr.message) ? { name: 'API请求地址有误', - message: '请检查是否正确填写URL以及URL是否允许访问' + message: `请检查是否正确填写URL以及URL是否允许访问,${tmpInputErr.message}` } : /socket hang up/i.test(tmpInputErr.message) ? { name: '请求错误', message: SOCKET_HANG_UP_TIP_TEXT_OBJ['SAAS_SERVER'] || - '无法访问目标地址,请检查接口是否能被正常访问,是否存在网络隔离或防火墙。' + `无法访问目标地址,请检查接口是否能被正常访问,是否存在网络隔离或防火墙,${tmpInputErr.message}` } : tmpInputErr }, diff --git a/test/e2e/tests/api-edit.spec.ts b/test/e2e/tests/api-edit.spec.ts index 1feda04be..da1df3920 100644 --- a/test/e2e/tests/api-edit.spec.ts +++ b/test/e2e/tests/api-edit.spec.ts @@ -62,9 +62,9 @@ test('Basic Operate', async ({ page }) => { test('Check All Param Operate', async ({ page }) => { await page.getByRole('banner').getByRole('button').click(); - // //Url + //Url await page.locator('input[name="uri"]').fill('/?json'); - // //Name + //Name await page.locator('input[name="name"]').click(); await page.locator('input[name="name"]').fill('JSON'); //?example descript isRequire addChild delete @@ -155,9 +155,6 @@ test('Save Form Data API', async ({ page }) => { //Save API await page.locator('input[name="uri"]').press('Meta+s'); await ifTipsExist(page, 'Added successfully'); - - //XML - //Raw }); test('Save XML API', async ({ page }) => { @@ -182,9 +179,6 @@ test('Save XML API', async ({ page }) => { //Save API await page.locator('input[name="uri"]').press('Meta+s'); await ifTipsExist(page, 'Added successfully'); - - //XML - //Raw }); test('Save Raw API', async ({ page }) => { @@ -204,12 +198,7 @@ test('Save Raw API', async ({ page }) => { Example: 'value' } }); - //Import - //Save API await page.locator('input[name="uri"]').press('Meta+s'); await ifTipsExist(page, 'Added successfully'); - - //XML - //Raw }); diff --git a/test/e2e/tests/api-group.spec.ts b/test/e2e/tests/api-group.spec.ts index 80dbbcb6b..e88a256b5 100644 --- a/test/e2e/tests/api-group.spec.ts +++ b/test/e2e/tests/api-group.spec.ts @@ -1,6 +1,6 @@ import { test, expect } from '@playwright/test'; -import { ifTipsExist, operateGroup, seletGroup } from '../utils/commom.util'; +import { clickButtonByIconName, closeTab, ifTipsExist, operateGroup, seletGroup } from '../utils/commom.util'; test.beforeEach(async ({ page }) => { await page.goto('/'); await page.getByRole('button', { name: 'Got it' }).click(); @@ -18,21 +18,26 @@ test.beforeEach(async ({ page }) => { await page.getByPlaceholder('Group Name').press('Enter'); //Close group tab - await page.getByRole('tab').getByText(subGroupName).hover(); - await page.getByRole('button', { name: 'Close tab' }).click(); + await closeTab(page, subGroupName); }); test('Basic Operate', async ({ page }) => { //Edit group await operateGroup(page, 'Sub Group', 'Edit'); - await page.locator('nz-form-control').filter({ hasText: 'Sub Group' }).getByRole('button').click(); + await clickButtonByIconName(page, 'edit'); await page.getByPlaceholder('Group Name').fill('Sub Group after'); await page.getByPlaceholder('Group Name').press('Enter'); - //Delete group + //Delete group from tree await operateGroup(page, 'Sub Group', 'Delete'); await page.getByRole('button', { name: 'Ok' }).click(); await ifTipsExist(page, 'Successfully deleted'); + + //Delete group from edit page + await page.locator('nz-tree-node-title div').first().click(); + await clickButtonByIconName(page, 'delete'); + await page.getByRole('button', { name: 'OK' }).click(); + await ifTipsExist(page, 'Successfully deleted'); }); test('Search', async ({ page }) => { //Search Group diff --git a/test/e2e/tests/api-unit-test.spec.ts b/test/e2e/tests/api-unit-test.spec.ts index fb8f05b14..3534a803f 100644 --- a/test/e2e/tests/api-unit-test.spec.ts +++ b/test/e2e/tests/api-unit-test.spec.ts @@ -1,6 +1,6 @@ import { test, expect, chromium } from '@playwright/test'; -import { adaTabledRow, addTextToEditor, ECHO_API_URL, ifTipsExist } from '../utils/commom.util'; +import { adaTabledRow, addEnv, addTextToEditor, ECHO_API_URL, ifTipsExist } from '../utils/commom.util'; const testAndWaitForResponse = async page => { const responsePromise = page.waitForResponse('**/api/unit'); await page.getByRole('button', { name: 'Send' }).click(); @@ -75,7 +75,7 @@ test('Unit Test', async ({ page }) => { */ test('Import Data Unit Test', async ({ page }) => { await page.getByPlaceholder('Enter URL').click(); - await page.getByPlaceholder('Enter URL').fill('http://demo.gokuapi.com:8280/Web/Test/all/print'); + await page.getByPlaceholder('Enter URL').fill(ECHO_API_URL); //Header await page.getByText('Headers').click(); await adaTabledRow(page, { @@ -167,3 +167,101 @@ test('Raw Test', async ({ page }) => { const res1 = await testAndWaitForResponse(page); expect(res1.body).toEqual(`{"test":1,"test1":2}`); }); + +/** + * Global Varibale Test + */ +test('Global Variable Test', async ({ page }) => { + //Env Globals + await addEnv(page); + + await page.getByPlaceholder('Enter URL').click(); + await page.getByPlaceholder('Enter URL').fill('/Web/Test/all/{{pathVariable}}'); + //JSON Body + await addTextToEditor(page, `{"{{globalName}}":"{{globalName}}"}`); + //Header + await page.getByText('Headers').click(); + //First row is content-type + await adaTabledRow(page, { + index: 1, + valueByKey: { + Name: '{{globalName}}', + Value: '{{globalName}}' + } + }); + + //Query + await page.getByText('Query').click(); + await adaTabledRow(page, { + index: 0, + valueByKey: { + Name: '{{globalName}}', + Value: '{{globalName}}' + } + }); + const res = await testAndWaitForResponse(page); + expect(res.path).toEqual('/Web/Test/all/print'); + expect(res.body).toEqual(`{"globalVariable":"globalVariable"}`); + expect(res.query.globalVariable[0]).toEqual('globalVariable'); + expect(res.header.Globalvariable[0]).toEqual('globalVariable'); + + //Script Globals + await page.getByText('Script Action').click(); + await addTextToEditor(page, `pc.globals.set("scriptVariable","scriptVariable");`); + await page.getByText('Body').first().click(); + await addTextToEditor(page, `{"{{globalName}}":"{{globalName}}","{{scriptVariable}}":"{{scriptVariable}}"}`); + const res1 = await testAndWaitForResponse(page); + expect(res1.body).toEqual(`{"globalVariable":"globalVariable","scriptVariable":"scriptVariable"}`); + + //Form-data + await page.getByText('Form-Data').click(); + await adaTabledRow(page, { + index: 0, + valueByKey: { + Name: '{{globalName}}', + Value: '{{globalName}}' + } + }); + const res2 = await testAndWaitForResponse(page); + expect(res2.body).toEqual(`globalVariable=globalVariable`); +}); + +test('Import Curl Test', async ({ page }) => { + await page.getByPlaceholder('Enter URL').click(); + //Form-Data application/x-www-form-urlencoded + await page.getByPlaceholder('Enter URL').fill(`curl 'http://demo.gokuapi.com:8280/Web/Test/all/print?query=query' \ + -H 'Accept: */*' \ + -H 'Accept-Language: en,zh-CN;q=0.9,zh;q=0.8' \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -H 'Cookie: uid=1' \ + -H 'Eo-Token: 85f3cfbb-e185-48d9-8dc0-f5da86177329' \ + -H 'Origin: chrome-extension://plecpgbpgkbmgigendedfaahcajeaimi' \ + -H 'Proxy-Connection: keep-alive' \ + -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36' \ + -H 'header: header' \ + --data-raw 'form=value&form2=value' \ + --compressed \ + --insecure`); + const res = await testAndWaitForResponse(page); + expect(res.body).toEqual(`form=value&form2=value`); + expect(res.query.query[0]).toEqual('query'); + expect(res.header.Header[0]).toEqual('header'); + + //Form-Dara + // await page.getByPlaceholder('Enter URL').click(); + // await page.getByPlaceholder('Enter URL').fill(`curl 'http://demo.gokuapi.com:8280/Web/Test/all/print?query=query' \ + // -H 'Accept: */*' \ + // -H 'Accept-Language: en,zh-CN;q=0.9,zh;q=0.8' \ + // -H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryVBz1dMCpoC5JE3xx' \ + // -H 'Cookie: uid=1' \ + // -H 'Eo-Token: 5815ecc4-20ab-40ba-a943-f5e7c3624274' \ + // -H 'Origin: chrome-extension://plecpgbpgkbmgigendedfaahcajeaimi' \ + // -H 'Proxy-Connection: keep-alive' \ + // -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36' \ + // -H 'header: header' \ + // --data-raw $'------WebKitFormBoundaryVBz1dMCpoC5JE3xx\r\nContent-Disposition: form-data; name="form"\r\n\r\nvalue\r\n------WebKitFormBoundaryVBz1dMCpoC5JE3xx\r\nContent-Disposition: form-data; name="file"; filename="latest.yml"\r\nContent-Type: application/x-yaml\r\n\r\nversion: 0.5.0\nfiles:\n - url: Postcat-Setup-0.5.0.exe\n sha512: Hu07AiOfO9xGuddFfvP9BItyWjylYi5Kc2BLbGTj22P4damBk8oR+2yacaW4t/M0fMdHnmyXMXGRYLN/u6PhAQ==\n size: 90365912\npath: Postcat-Setup-0.5.0.exe\nsha512: Hu07AiOfO9xGuddFfvP9BItyWjylYi5Kc2BLbGTj22P4damBk8oR+2yacaW4t/M0fMdHnmyXMXGRYLN/u6PhAQ==\nreleaseDate: \'2023-04-04T12:03:57.000Z\'\n\r\n------WebKitFormBoundaryVBz1dMCpoC5JE3xx--\r\n' \ + // --compressed \ + // --insecure`); + // const res1 = await testAndWaitForResponse(page); + // expect(res1.body).toEqual(`form=value&f1orm2=value`); +}); diff --git a/test/e2e/tests/env.spec.ts b/test/e2e/tests/env.spec.ts index 4d429abfe..2281b0b1c 100644 --- a/test/e2e/tests/env.spec.ts +++ b/test/e2e/tests/env.spec.ts @@ -1,43 +1,28 @@ import { test, expect } from '@playwright/test'; -import { ECHO_API_URL, ifTipsExist } from '../utils/commom.util'; -const url = new URL(ECHO_API_URL); +import { adaTabledRow, addEnv, ECHO_API_URL, ifTipsExist } from '../utils/commom.util'; test.beforeEach(async ({ page }) => { await page.goto('/'); - //Add env - await page.locator('a').filter({ hasText: 'Environment' }).click(); - await page.getByRole('banner').getByRole('button').click(); - await page.getByLabel('Name').fill('DEV'); - await page.getByLabel('Host').click({ timeout: 1000 }); - await page.getByLabel('Host').fill(url.host); - await page.getByPlaceholder('Name').click({ timeout: 1000 }); - await page.getByPlaceholder('Name').fill('globalName'); - await page.getByPlaceholder('Value').first().dblclick(); - await page.getByPlaceholder('Value').first().fill('globalVariable'); - await page.getByPlaceholder('Description').first().click(); - await page.getByPlaceholder('Description').first().fill('globalDescription'); - await page.getByRole('button', { name: 'Save' }).click(); - await ifTipsExist(page, 'Added successfully'); + await page.getByRole('button', { name: 'Got it' }).click(); + await addEnv(page, { + name: 'DEV' + }); }); test('Env Basic', async ({ page }) => { //Add first env will choose it + await page.locator('nz-tree-node-title div').first().click(); //Edit env - await page.getByRole('tablist').locator('div').filter({ hasText: 'DEV' }).nth(2).hover(); - await page.getByRole('button', { name: 'Close tab' }).click(); - await page.locator('div').filter({ hasText: 'DEV' }).click(); await page.getByLabel('Name').press('Meta+s'); await ifTipsExist(page, 'Edited successfully'); //Delete env -}); - -test('Env Delete', async ({ page }) => { await page.locator('nz-tree-node-title div').first().hover(); await page.locator('nz-tree-node-title').getByRole('button').click(); await page.getByRole('button', { name: 'Delete' }).click(); + await ifTipsExist(page, 'Successfully deleted'); }); -// test('Use Env', async ({ page }) => { +// test('Preview Env', async ({ page }) => { // //Host uri // //Global variable // //Change env will change host uri diff --git a/test/e2e/tests/extension.spec.ts b/test/e2e/tests/extension.spec.ts index 41a5a593b..61baef239 100644 --- a/test/e2e/tests/extension.spec.ts +++ b/test/e2e/tests/extension.spec.ts @@ -2,24 +2,25 @@ import { test, expect } from '@playwright/test'; const installExtension = async () => {}; test.beforeEach(async ({ page }) => { await page.goto('/'); + await page.getByRole('button', { name: 'Got it' }).click(); }); -test('Basic Operate', async ({ page }) => { - //Install Extension - //Close Extension - //Open Extension - //Uninstall Extension - //Switch Extension Type - //Search Extension -}); -test('Sync URL From TEST', async ({ page }) => {}); -test('Import Swagger', async ({ page }) => { - await page.getByRole('button', { name: 'Tap or drag files directly to this area Only supports importing a single file' }).click(); - await page - .getByRole('button', { name: 'Tap or drag files directly to this area Only supports importing a single file' }) - .setInputFiles('Postcat-Export-0403.json'); -}); -test('APISpace Extension', async ({ page }) => {}); +// test('Basic Operate', async ({ page }) => { +// //Install Extension +// //Close Extension +// //Open Extension +// //Uninstall Extension +// //Switch Extension Type +// //Search Extension +// }); +// test('Sync URL From TEST', async ({ page }) => {}); +// test('Import Swagger', async ({ page }) => { +// await page.getByRole('button', { name: 'Tap or drag files directly to this area Only supports importing a single file' }).click(); +// await page +// .getByRole('button', { name: 'Tap or drag files directly to this area Only supports importing a single file' }) +// .setInputFiles('Postcat-Export-0403.json'); +// }); +// test('APISpace Extension', async ({ page }) => {}); test('Export API', async ({ page }) => { await page.goto('/'); await page.locator('a:has-text("Setting")').click(); diff --git a/test/e2e/tests/member.spec.ts b/test/e2e/tests/member.spec.ts index a8a0f0d91..27d681db9 100644 --- a/test/e2e/tests/member.spec.ts +++ b/test/e2e/tests/member.spec.ts @@ -1,21 +1,21 @@ import { test, expect } from '@playwright/test'; import { login } from '../utils/commom.util'; -test.beforeEach(async ({ page }) => { - await page.goto('/'); -}); -test('Workspace Member', async ({ page }) => { - //Login - await login(page); - //Switch to cloud workspace - //Add member to workspace - //Change role,default - //Remove member - //Add Member - //Login with new member - //Quit workspace -}); -test('Project Member', async ({ page }) => { - //Add member to workspace - //Add member to project -}); +// test.beforeEach(async ({ page }) => { +// await page.goto('/'); +// }); +// test('Workspace Member', async ({ page }) => { +// //Login +// await login(page); +// //Switch to cloud workspace +// //Add member to workspace +// //Change role,default +// //Remove member +// //Add Member +// //Login with new member +// //Quit workspace +// }); +// test('Project Member', async ({ page }) => { +// //Add member to workspace +// //Add member to project +// }); diff --git a/test/e2e/tests/project.spec.ts b/test/e2e/tests/project.spec.ts index ec722f529..a0dc2f9b4 100644 --- a/test/e2e/tests/project.spec.ts +++ b/test/e2e/tests/project.spec.ts @@ -1,12 +1,12 @@ import { test, expect } from '@playwright/test'; -test.beforeEach(async ({ page }) => { - await page.goto('/'); - await page.getByRole('button', { name: 'Got it' }).click(); -}); -test('Basic Operate', async ({ page }) => { - //Back to Project List - //Add project - //Edit project - //Delete project -}); -//Share Project +// test.beforeEach(async ({ page }) => { +// await page.goto('/'); +// await page.getByRole('button', { name: 'Got it' }).click(); +// }); +// test('Basic Operate', async ({ page }) => { +// //Back to Project List +// //Add project +// //Edit project +// //Delete project +// }); +// //Share Project diff --git a/test/e2e/tests/websocket-test.spec.ts b/test/e2e/tests/websocket-test.spec.ts index e69de29bb..67907938f 100644 --- a/test/e2e/tests/websocket-test.spec.ts +++ b/test/e2e/tests/websocket-test.spec.ts @@ -0,0 +1,31 @@ +import { test, expect, chromium } from '@playwright/test'; + +import { adaTabledRow, addEnv, addTextToEditor, ECHO_API_URL, ifTipsExist } from '../utils/commom.util'; +test.beforeEach(async ({ page }) => { + await page.goto('/'); + await page.getByRole('button', { name: 'Got it' }).click(); +}); +/** + * Basic Test + */ +test('Websocekt Test', async ({ page }) => { + await page.locator('eo-tab').getByRole('button').first().hover(); + await page.getByText('Websocket').click(); + + //Connect + await page.getByPlaceholder('Enter URL').click(); + await page + .getByPlaceholder('Enter URL') + .fill('wss://demo.piesocket.com/v3/channel_1?api_key=VCXCEuvhGcBDP7XhiJJUDvR1e1D3eiVjgZ9VRiaV¬ify_self='); + await page.getByRole('button', { name: 'Connect' }).click(); + await ifTipsExist(page, 'Connected to'); + + //Send Body + await addTextToEditor(page, 'i am a body'); + await page.getByRole('button', { name: 'Send' }).click(); + await page.getByRole('list').getByText('i am a body').isVisible(); + + //Disconnect + await page.getByRole('button', { name: 'Disconnect' }).click(); + await ifTipsExist(page, 'Disconnect from'); +}); diff --git a/test/e2e/utils/commom.util.ts b/test/e2e/utils/commom.util.ts index ce99030fc..759531627 100644 --- a/test/e2e/utils/commom.util.ts +++ b/test/e2e/utils/commom.util.ts @@ -1,10 +1,13 @@ import { expect } from '@playwright/test'; export const ECHO_API_URL = 'http://demo.gokuapi.com:8280/Web/Test/all/print'; export const addTextToEditor = async (page, text, monacoEditor = page.locator('.ant-modal-body .monaco-editor').first()) => { - const isExist = await monacoEditor.count(); + const isExist = await monacoEditor.isVisible(); monacoEditor = isExist ? monacoEditor : await page.locator('.monaco-editor').first(); + //? some times monaco editor not ready + await page.waitForTimeout(500); await monacoEditor.click(); await page.keyboard.press('Meta+KeyA'); + await page.keyboard.press('Delete'); await page.keyboard.type(text); }; export const clickButtonByIconName = async (page, name) => { @@ -27,9 +30,43 @@ export const operateGroup = async (page, groupName, operateName) => { await page.getByText(operateName, { exact: true }).click(); } }; +export const closeTab = async (page, tabName) => { + await page.getByRole('tab').getByText(tabName).hover(); + await page.getByRole('button', { name: 'Close tab' }).click(); +}; export const ifTipsExist = async (page, tips) => { await expect(page.locator(`text=${tips}`)).toBeVisible(); }; +export const addEnv = async (page, env?) => { + env = { + name: 'DEV', + host: 'http://demo.gokuapi.com:8280', + globalVariable: [ + { + Name: 'globalName', + Value: 'globalVariable', + Description: 'globalDescription' + }, + { + Name: 'pathVariable', + Value: 'print' + } + ], + ...env + }; + //Add env + await page.locator('a').filter({ hasText: 'Environment' }).click(); + await page.getByRole('banner').getByRole('button').click(); + await page.getByLabel('Name').fill(env.name); + await page.getByLabel('Host').click({ timeout: 1000 }); + await page.getByLabel('Host').fill(env.host); + await adaTabledRow(page, { + enums: env.globalVariable + }); + await page.getByRole('button', { name: 'Save' }).click(); + await ifTipsExist(page, 'Added successfully'); + await closeTab(page, env.name); +}; export const login = async page => { await page.getByRole('button', { name: 'Sign in/Up' }).click();