Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collection Runner Feature #3600

Draft
wants to merge 13 commits into
base: release/2023.12.0
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -19,6 +19,7 @@ pids
*.pid
*.seed
*.pid.lock
*.env

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
Expand Down
Expand Up @@ -6,6 +6,7 @@
--line-height-body: 1rem;
--upper-primary-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;
Expand Down
19 changes: 17 additions & 2 deletions packages/hoppscotch-common/locales/en.json
Expand Up @@ -160,6 +160,8 @@
}
},
"collection": {
"title": "Collection",
"run": "Run Collection",
"created": "Collection created",
"different_parent": "Cannot reorder collection with different parent",
"edit": "Edit Collection",
Expand Down Expand Up @@ -241,6 +243,7 @@
"tests": "There are no tests for this request"
},
"environment": {
"heading": "Environment",
"add_to_global": "Add to Global",
"added": "Environment addition",
"create_new": "Create new environment",
Expand Down Expand Up @@ -326,6 +329,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": {
Expand Down Expand Up @@ -882,7 +886,10 @@
"tests": "Tests",
"types": "Types",
"variables": "Variables",
"websocket": "WebSocket"
"websocket": "WebSocket",
"all_tests": "All Tests",
"passed": "Passed",
"failed": "Failed"
},
"team": {
"already_member": "You are already a member of this team. Contact your team owner.",
Expand Down Expand Up @@ -946,14 +953,22 @@
"not_found": "Environment not found."
},
"test": {
"requests": "Requests",
"selection": "Selection",
"failed": "test failed",
"javascript_code": "JavaScript Code",
"learn": "Read documentation",
"passed": "test passed",
"report": "Test Report",
"results": "Test Results",
"script": "Script",
"snippets": "Snippets"
"snippets": "Snippets",
"run": "Run",
"stop": "Stop",
"new_run": "New Run",
"iterations": "Iterations",
"duration": "Duration",
"avg_resp": "Avg. Response Time"
},
"websocket": {
"communication": "Communication",
Expand Down
15 changes: 15 additions & 0 deletions packages/hoppscotch-common/src/components.d.ts
Expand Up @@ -57,6 +57,7 @@ declare module 'vue' {
CollectionsImportExport: typeof import('./components/collections/ImportExport.vue')['default']
CollectionsMyCollections: typeof import('./components/collections/MyCollections.vue')['default']
CollectionsRequest: typeof import('./components/collections/Request.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']
Expand Down Expand Up @@ -124,6 +125,7 @@ declare module 'vue' {
HttpBody: typeof import('./components/http/Body.vue')['default']
HttpBodyParameters: typeof import('./components/http/BodyParameters.vue')['default']
HttpCodegenModal: typeof import('./components/http/CodegenModal.vue')['default']
HttpCollectionRunner: typeof import('./components/http/CollectionRunner.vue')['default']
HttpHeaders: typeof import('./components/http/Headers.vue')['default']
HttpImportCurl: typeof import('./components/http/ImportCurl.vue')['default']
HttpOAuth2Authorization: typeof import('./components/http/OAuth2Authorization.vue')['default']
Expand All @@ -136,13 +138,24 @@ declare module 'vue' {
HttpRequestTab: typeof import('./components/http/RequestTab.vue')['default']
HttpResponse: typeof import('./components/http/Response.vue')['default']
HttpResponseMeta: typeof import('./components/http/ResponseMeta.vue')['default']
HttpRunner: typeof import('./components/http/Runner.vue')['default']
HttpSidebar: typeof import('./components/http/Sidebar.vue')['default']
HttpTabHead: typeof import('./components/http/TabHead.vue')['default']
HttpTestFolder: typeof import('./components/http/test/Folder.vue')['default']
HttpTestRequest: typeof import('./components/http/test/Request.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']
HttpTestRunnerResult: typeof import('./components/http/test/RunnerResult.vue')['default']
HttpTests: typeof import('./components/http/Tests.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']
Expand All @@ -159,6 +172,8 @@ 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']
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']
Expand Down
Expand Up @@ -65,7 +65,7 @@ const tabs = useService(RESTTabService)
watch(
() => props.show,
(show) => {
if (show) {
if (show && tabs.currentActiveTab.value.document.type === "request") {
editingName.value = tabs.currentActiveTab.value.document.request.name
}
}
Expand Down
Expand Up @@ -33,7 +33,7 @@
dropItemID = ''
}
"
@contextmenu.prevent="options?.tippy.show()"
@contextmenu.prevent="options?.tippy?.show()"
>
<div
class="flex min-w-0 flex-1 items-center justify-center"
Expand Down Expand Up @@ -122,6 +122,18 @@
}
"
/>
<HoppSmartItem
ref="folderAction"
:icon="IconPlaySquare"
:label="t('collection.run')"
:shortcut="['N']"
@click="
() => {
emit('run-collection')
hide()
}
"
/>
<HoppSmartItem
ref="edit"
:icon="IconEdit"
Expand Down Expand Up @@ -184,6 +196,7 @@
</template>

<script setup lang="ts">
import IconPlaySquare from "~icons/lucide/play-square"
import IconCheckCircle from "~icons/lucide/check-circle"
import IconFolderPlus from "~icons/lucide/folder-plus"
import IconFilePlus from "~icons/lucide/file-plus"
Expand Down Expand Up @@ -244,6 +257,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: "export-data"): void
(event: "remove-collection"): void
Expand Down Expand Up @@ -303,7 +317,7 @@ watch(
() => props.exportLoading,
(val) => {
if (!val) {
options.value!.tippy.hide()
options.value!.tippy?.hide()
}
}
)
Expand Down
Expand Up @@ -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', {
Expand Down Expand Up @@ -117,6 +123,12 @@
folderPath: node.id,
})
"
@run-collection="
emit('run-collection', {
collectionIndex: node.id,
collection: node.data.data.data,
})
"
folder-type="folder"
@add-request="
node.data.type === 'folders' &&
Expand Down Expand Up @@ -418,6 +430,13 @@ const emit = defineEmits<{
folder: HoppCollection<HoppRESTRequest>
}
): void
(
event: "run-collection",
payload: {
collectionIndex: string
collection: HoppCollection<HoppRESTRequest>
}
): void
(
event: "edit-collection",
payload: {
Expand Down
43 changes: 40 additions & 3 deletions packages/hoppscotch-common/src/components/collections/index.vue
Expand Up @@ -34,6 +34,7 @@
:filter-text="filterTexts"
:save-request="saveRequest"
:picked="picked"
@run-collection="runCollection"
@add-folder="addFolder"
@add-request="addRequest"
@edit-collection="editCollection"
Expand Down Expand Up @@ -184,6 +185,7 @@ import {
} from "~/newstore/collections"
import TeamCollectionAdapter from "~/helpers/teams/TeamCollectionAdapter"
import {
getDefaultRESTRequest,
HoppCollection,
HoppRESTRequest,
makeCollection,
Expand Down Expand Up @@ -635,7 +637,9 @@ const addRequest = (payload: {

const onAddRequest = (requestName: string) => {
const newRequest = {
...cloneDeep(tabs.currentActiveTab.value.document.request),
...(tabs.currentActiveTab.value.document.type === "request"
? cloneDeep(tabs.currentActiveTab.value.document.request)
: getDefaultRESTRequest()),
name: requestName,
}

Expand All @@ -645,6 +649,7 @@ const onAddRequest = (requestName: string) => {
const insertionIndex = saveRESTRequestAs(path, newRequest)

tabs.createNewTab({
type: "request",
request: newRequest,
isDirty: false,
saveContext: {
Expand Down Expand Up @@ -694,6 +699,7 @@ const onAddRequest = (requestName: string) => {
const { createRequestInCollection } = result

tabs.createNewTab({
type: "request",
request: newRequest,
isDirty: false,
saveContext: {
Expand All @@ -712,6 +718,32 @@ const onAddRequest = (requestName: string) => {
}
}

const runCollection = (payload: {
collectionIndex: string
collection: HoppCollection<HoppRESTRequest>
}) => {
const possibleTab = tabs.getTabRefWithSaveContext(
{
originLocation: "user-collection",
folderPath: payload.collectionIndex!!,
},
"collection"
)
if (possibleTab) {
tabs.setActiveTab(possibleTab.value.id)
} else {
tabs.createNewTab({
type: "test-runner",
collection: payload.collection,
isDirty: false,
saveContext: {
originLocation: "user-collection",
folderPath: payload.collectionIndex!,
},
})
}
}

const addFolder = (payload: {
path: string
folder: HoppCollection<HoppRESTRequest> | TeamCollection
Expand Down Expand Up @@ -924,7 +956,10 @@ const updateEditingRequest = (newName: string) => {

editRESTRequest(folderPath, requestIndex, requestUpdated)

if (possibleActiveTab) {
if (
possibleActiveTab &&
possibleActiveTab.value.document.type === "request"
) {
possibleActiveTab.value.document.request.name = requestUpdated.name
nextTick(() => {
possibleActiveTab.value.document.isDirty = false
Expand Down Expand Up @@ -965,7 +1000,7 @@ const updateEditingRequest = (newName: string) => {
requestID,
})

if (possibleTab) {
if (possibleTab && possibleTab.value.document.type === "request") {
possibleTab.value.document.request.name = requestName
nextTick(() => {
possibleTab.value.document.isDirty = false
Expand Down Expand Up @@ -1297,6 +1332,7 @@ const selectRequest = (selectedRequest: {
tabs.setActiveTab(possibleTab.value.id)
} else {
tabs.createNewTab({
type: "request",
request: cloneDeep(request),
isDirty: false,
saveContext: {
Expand All @@ -1316,6 +1352,7 @@ const selectRequest = (selectedRequest: {
} else {
// If not, open the request in a new tab
tabs.createNewTab({
type: "request",
request: cloneDeep(request),
isDirty: false,
saveContext: {
Expand Down
4 changes: 2 additions & 2 deletions packages/hoppscotch-common/src/components/embeds/index.vue
Expand Up @@ -86,13 +86,13 @@ import { useStreamSubscriber } from "~/composables/stream"
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
import { runRESTRequest$ } from "~/helpers/RequestRunner"
import { HoppTab } from "~/services/tab"
import { HoppRESTDocument } from "~/helpers/rest/document"
import { HoppRequestDocument } from "~/helpers/rest/document"
import IconSave from "~icons/lucide/save"
const t = useI18n()
const toast = useToast()

const props = defineProps<{
modelTab: HoppTab<HoppRESTDocument>
modelTab: HoppTab<HoppRequestDocument>
properties: string[]
sharedRequestID: string
}>()
Expand Down
Expand Up @@ -297,6 +297,7 @@ const clearHistory = () => {
const tabs = useService(RESTTabService)
const useHistory = (entry: RESTHistoryEntry) => {
tabs.createNewTab({
type: "request",
request: entry.request,
isDirty: false,
})
Expand Down
4 changes: 2 additions & 2 deletions packages/hoppscotch-common/src/components/http/Request.vue
Expand Up @@ -260,7 +260,7 @@ import { useService } from "dioc/vue"
import { InspectionService } from "~/services/inspection"
import { InterceptorService } from "~/services/interceptor.service"
import { HoppTab } from "~/services/tab"
import { HoppRESTDocument } from "~/helpers/rest/document"
import { HoppRequestDocument } from "~/helpers/rest/document"
import { RESTTabService } from "~/services/tab/rest"
import { getMethodLabelColor } from "~/helpers/rest/labelColoring"

Expand All @@ -284,7 +284,7 @@ const toast = useToast()

const { subscribeToStream } = useStreamSubscriber()

const props = defineProps<{ modelValue: HoppTab<HoppRESTDocument> }>()
const props = defineProps<{ modelValue: HoppTab<HoppRequestDocument> }>()
const emit = defineEmits(["update:modelValue"])

const tab = useVModel(props, "modelValue", emit)
Expand Down