feat: collection runner refinements (#4609)
Co-authored-by: nivedin <nivedinp@gmail.com> Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
parent
0ae21e2c2e
commit
d9d656269c
19 changed files with 244 additions and 49 deletions
|
|
@ -724,6 +724,7 @@
|
|||
"duplicate_name_error": "Same name response already exists",
|
||||
"filter_response_body": "Filter JSON response body (uses JSONPath syntax)",
|
||||
"headers": "Headers",
|
||||
"request_headers": "Request Headers",
|
||||
"html": "HTML",
|
||||
"image": "Image",
|
||||
"json": "JSON",
|
||||
|
|
@ -1224,10 +1225,12 @@
|
|||
"advanced_settings": "Advanced Settings",
|
||||
"stop_on_error": "Stop run if an error occurs",
|
||||
"persist_responses": "Persist responses",
|
||||
"keep_variable_values": "Keep variable values",
|
||||
"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",
|
||||
"response_body_lost_rerun": "Response body is lost. Run again the collection to get the response body.",
|
||||
"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.",
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ declare module 'vue' {
|
|||
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']
|
||||
|
|
|
|||
|
|
@ -136,7 +136,12 @@
|
|||
})
|
||||
}
|
||||
"
|
||||
@run-collection="emit('run-collection', $event)"
|
||||
@run-collection="
|
||||
emit('run-collection', {
|
||||
collectionID: node.data.data.data.id,
|
||||
path: node.id,
|
||||
})
|
||||
"
|
||||
@click="
|
||||
() => {
|
||||
handleCollectionClick({
|
||||
|
|
@ -233,7 +238,12 @@
|
|||
})
|
||||
}
|
||||
"
|
||||
@run-collection="emit('run-collection', $event)"
|
||||
@run-collection="
|
||||
emit('run-collection', {
|
||||
collectionID: node.data.data.data.id,
|
||||
path: node.id,
|
||||
})
|
||||
"
|
||||
@click="
|
||||
() => {
|
||||
handleCollectionClick({
|
||||
|
|
@ -662,7 +672,10 @@ const emit = defineEmits<{
|
|||
(event: "expand-team-collection", payload: string): void
|
||||
(event: "display-modal-add"): void
|
||||
(event: "display-modal-import-export"): void
|
||||
(event: "run-collection", collectionID: string): void
|
||||
(
|
||||
event: "run-collection",
|
||||
payload: { collectionID: string; path: string }
|
||||
): void
|
||||
}>()
|
||||
|
||||
const getPath = (path: string) => {
|
||||
|
|
|
|||
|
|
@ -109,7 +109,8 @@
|
|||
@run-collection="
|
||||
runCollectionHandler({
|
||||
type: 'team-collections',
|
||||
collectionID: $event,
|
||||
collectionID: $event.collectionID,
|
||||
path: $event.path,
|
||||
})
|
||||
"
|
||||
@share-request="shareRequest"
|
||||
|
|
@ -2866,8 +2867,28 @@ const setCollectionProperties = (newCollection: {
|
|||
displayModalEditProperties(false)
|
||||
}
|
||||
|
||||
const runCollectionHandler = (payload: CollectionRunnerData) => {
|
||||
collectionRunnerData.value = payload
|
||||
const runCollectionHandler = (
|
||||
payload: CollectionRunnerData & {
|
||||
path?: string
|
||||
}
|
||||
) => {
|
||||
if (payload.path && collectionsType.value.type === "team-collections") {
|
||||
const inheritedProperties =
|
||||
teamCollectionAdapter.cascadeParentCollectionForHeaderAuth(payload.path)
|
||||
|
||||
if (inheritedProperties) {
|
||||
collectionRunnerData.value = {
|
||||
type: "team-collections",
|
||||
collectionID: payload.collectionID,
|
||||
inheritedProperties: inheritedProperties,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
collectionRunnerData.value = {
|
||||
type: "my-collections",
|
||||
collectionID: payload.collectionID,
|
||||
}
|
||||
}
|
||||
showCollectionsRunnerModal.value = true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,19 +3,41 @@
|
|||
<HttpResponseMeta :response="doc.response" :is-embed="false" />
|
||||
<LensesResponseBodyRenderer
|
||||
v-if="hasResponse"
|
||||
v-model:document="doc"
|
||||
:document="{
|
||||
request: {
|
||||
...doc,
|
||||
response: null,
|
||||
testResults: null,
|
||||
},
|
||||
response: doc.response,
|
||||
testResults: doc.testResults,
|
||||
}"
|
||||
:is-editable="false"
|
||||
:is-test-runner="true"
|
||||
:show-response="showResponse"
|
||||
/>
|
||||
|
||||
<HoppSmartPlaceholder
|
||||
v-else
|
||||
:src="`/images/states/${colorMode.value}/add_files.svg`"
|
||||
:alt="`${t('collection_runner.response_body_lost_rerun')}`"
|
||||
:text="`${t('collection_runner.response_body_lost_rerun')}`"
|
||||
>
|
||||
</HoppSmartPlaceholder>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from "@vueuse/core"
|
||||
import { computed } from "vue"
|
||||
import { useI18n } from "~/composables/i18n"
|
||||
import { useColorMode } from "~/composables/theming"
|
||||
import { HoppRequestDocument } from "~/helpers/rest/document"
|
||||
import { TestRunnerRequest } from "~/services/test-runner/test-runner.service"
|
||||
|
||||
const t = useI18n()
|
||||
const colorMode = useColorMode()
|
||||
|
||||
const props = defineProps<{
|
||||
showResponse: boolean
|
||||
document: TestRunnerRequest
|
||||
|
|
@ -29,7 +51,8 @@ const doc = useVModel(props, "document", emit)
|
|||
|
||||
const hasResponse = computed(
|
||||
() =>
|
||||
doc.value.response?.type === "success" ||
|
||||
doc.value.response?.type === "fail"
|
||||
(doc.value.response?.type === "success" ||
|
||||
doc.value.response?.type === "fail") &&
|
||||
doc.value.response?.body instanceof ArrayBuffer
|
||||
)
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<template>
|
||||
<div class="flex items-stretch group ml-4 flex-col">
|
||||
<button
|
||||
class="w-full rounded px-4 py-3 transition cursor-pointer focus:outline-none hover:active hover:bg-primaryLight hover:text-secondaryDark"
|
||||
class="w-full rounded px-4 py-3 transition cursor-pointer focus:outline-none hover:bg-primaryLight hover:text-secondaryDark"
|
||||
:class="{ 'bg-primaryLight': isSelected }"
|
||||
@click="selectRequest()"
|
||||
>
|
||||
<div class="flex gap-4 mb-1 items-center">
|
||||
|
|
|
|||
|
|
@ -54,8 +54,10 @@
|
|||
:tab="tab"
|
||||
:collection-adapter="collectionAdapter"
|
||||
:is-running="tab.document.status === 'running'"
|
||||
:selected-request-path="selectedRequestPath"
|
||||
@on-change-tab="showTestsType = $event as 'all' | 'passed' | 'failed'"
|
||||
@on-select-request="onSelectRequest"
|
||||
@request-path="onChangeRequestPath"
|
||||
/>
|
||||
</template>
|
||||
<template #secondary>
|
||||
|
|
@ -91,9 +93,9 @@
|
|||
|
||||
<HoppSmartPlaceholder
|
||||
v-else-if="!selectedRequest"
|
||||
:src="`/images/states/${colorMode.value}/add_files.svg`"
|
||||
:alt="`${t('collection_runner.select_request')}`"
|
||||
:text="`${t('collection_runner.select_request')}`"
|
||||
:src="`/images/states/${colorMode.value}/pack.svg`"
|
||||
:alt="`${t('collection_runner.response_body_lost_rerun')}`"
|
||||
:text="`${t('collection_runner.response_body_lost_rerun')}`"
|
||||
>
|
||||
</HoppSmartPlaceholder>
|
||||
</template>
|
||||
|
|
@ -120,7 +122,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "@composables/i18n"
|
||||
import { HoppCollection } from "@hoppscotch/data"
|
||||
import { HoppCollection, HoppRESTHeader } from "@hoppscotch/data"
|
||||
import { SmartTreeAdapter } from "@hoppscotch/ui"
|
||||
import { useVModel } from "@vueuse/core"
|
||||
import { useService } from "dioc/vue"
|
||||
|
|
@ -174,6 +176,9 @@ const emit = defineEmits<{
|
|||
const tabs = useService(RESTTabService)
|
||||
const tab = useVModel(props, "modelValue", emit)
|
||||
|
||||
const selectedRequestPath = computed(
|
||||
() => tab.value.document.selectedRequestPath
|
||||
)
|
||||
const duration = computed(() => tab.value.document.testRunnerMeta.totalTime)
|
||||
const avgResponseTime = computed(() =>
|
||||
calculateAverageTime(
|
||||
|
|
@ -203,6 +208,10 @@ const onSelectRequest = async (request: TestRunnerRequest) => {
|
|||
tab.value.document.request = request
|
||||
}
|
||||
|
||||
const onChangeRequestPath = (path: string) => {
|
||||
tab.value.document.selectedRequestPath = path
|
||||
}
|
||||
|
||||
const collectionName = computed(() =>
|
||||
props.modelValue.document.type === "test-runner"
|
||||
? props.modelValue.document.collection.name
|
||||
|
|
@ -243,16 +252,41 @@ const runTests = async () => {
|
|||
collectionType
|
||||
)
|
||||
|
||||
const { auth, headers } = collectionInheritedProps ?? {
|
||||
auth: { authActive: true, authType: "none" },
|
||||
headers: [],
|
||||
}
|
||||
let resolvedCollection: HoppCollection = collection.value
|
||||
|
||||
// Accommodate collection properties for personal workspace
|
||||
// TODO: Resolve the collection properties computation for team workspaces
|
||||
const resolvedCollection = isPersonalWorkspace
|
||||
? { ...collection.value, auth, headers }
|
||||
: collection.value
|
||||
if (!isPersonalWorkspace) {
|
||||
const requestAuth = tab.value.document.inheritedProperties?.auth
|
||||
.inheritedAuth ?? {
|
||||
authActive: true,
|
||||
authType: "none",
|
||||
}
|
||||
|
||||
const requestHeaders = tab.value.document.inheritedProperties?.headers.map(
|
||||
(header) => {
|
||||
if (header.inheritedHeader) {
|
||||
return header.inheritedHeader
|
||||
}
|
||||
return []
|
||||
}
|
||||
)
|
||||
|
||||
resolvedCollection = {
|
||||
...collection.value,
|
||||
auth: requestAuth,
|
||||
headers: requestHeaders as HoppRESTHeader[],
|
||||
}
|
||||
} else {
|
||||
const { auth, headers } = collectionInheritedProps ?? {
|
||||
auth: { authActive: true, authType: "none" },
|
||||
headers: [],
|
||||
}
|
||||
|
||||
resolvedCollection = {
|
||||
...collection.value,
|
||||
auth,
|
||||
headers,
|
||||
}
|
||||
}
|
||||
|
||||
testRunnerStopRef.value = false // when testRunnerStopRef is false, the test runner will start running
|
||||
testRunnerService.runTests(tab, resolvedCollection, {
|
||||
|
|
|
|||
|
|
@ -65,23 +65,25 @@
|
|||
</span>
|
||||
</HoppSmartCheckbox>
|
||||
|
||||
<!-- <HoppSmartCheckbox
|
||||
<HoppSmartCheckbox
|
||||
class="pr-2"
|
||||
:on="config.keepVariableValues"
|
||||
@change="
|
||||
config.keepVariableValues = !config.keepVariableValues
|
||||
"
|
||||
>
|
||||
<span>Keep variable values</span>
|
||||
<span>
|
||||
{{ t("collection_runner.keep_variable_values") }}
|
||||
</span>
|
||||
<HoppButtonSecondary
|
||||
class="!py-0 pl-2"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
class="!py-0 pl-2"
|
||||
to="https://docs.hoppscotch.io/documentation/features/inspections"
|
||||
blank
|
||||
:title="t('app.wiki')"
|
||||
:icon="IconHelpCircle"
|
||||
/>
|
||||
</HoppSmartCheckbox> -->
|
||||
</HoppSmartCheckbox>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
@ -193,6 +195,7 @@ import { GQLError } from "~/helpers/backend/GQLClient"
|
|||
import { cloneDeep } from "lodash-es"
|
||||
import { getErrorMessage } from "~/helpers/runner/collection-tree"
|
||||
import { getRESTCollectionByRefId } from "~/newstore/collections"
|
||||
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
|
||||
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
|
|
@ -210,6 +213,7 @@ export type CollectionRunnerData =
|
|||
| {
|
||||
type: "team-collections"
|
||||
collectionID: string
|
||||
inheritedProperties?: HoppInheritedProperty
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
@ -246,7 +250,7 @@ const config = ref<TestRunnerConfig>({
|
|||
delay: 500,
|
||||
stopOnError: false,
|
||||
persistResponses: true,
|
||||
keepVariableValues: false,
|
||||
keepVariableValues: true,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
|
|
@ -273,7 +277,6 @@ const runTests = async () => {
|
|||
|
||||
let tabIdToClose = null
|
||||
if (props.sameTab) tabIdToClose = cloneDeep(tabs.currentTabID.value)
|
||||
|
||||
tabs.createNewTab({
|
||||
type: "test-runner",
|
||||
collectionType: props.collectionRunnerData.type,
|
||||
|
|
@ -291,6 +294,10 @@ const runTests = async () => {
|
|||
passedTests: 0,
|
||||
totalTests: 0,
|
||||
},
|
||||
inheritedProperties:
|
||||
"inheritedProperties" in props.collectionRunnerData
|
||||
? props.collectionRunnerData.inheritedProperties
|
||||
: undefined,
|
||||
})
|
||||
|
||||
if (tabIdToClose) tabs.closeTab(tabIdToClose)
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@
|
|||
:request="node.data.data.data"
|
||||
:request-i-d="node.id"
|
||||
:parent-i-d="node.data.data.parentIndex"
|
||||
:is-selected="node.data.isSelected"
|
||||
:is-selected="node.id === selectedRequestPath"
|
||||
:show-selection="showCheckbox"
|
||||
:is-last-item="node.data.isLastItem"
|
||||
@select-request="selectRequest(node.data.data.data)"
|
||||
@select-request="selectRequest(node.data.data.data, node.id)"
|
||||
/>
|
||||
</template>
|
||||
</HoppSmartTree>
|
||||
|
|
@ -102,19 +102,22 @@ defineProps<{
|
|||
tab: HoppTab<HoppTestRunnerDocument>
|
||||
collectionAdapter: SmartTreeAdapter<any>
|
||||
isRunning: boolean
|
||||
selectedRequestPath: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "onSelectRequest", request: TestRunnerRequest): void
|
||||
(e: "onChangeTab", event: string): void
|
||||
(e: "requestPath", path: string): void
|
||||
}>()
|
||||
|
||||
const selectedTestTab = ref<"all" | "passed" | "failed">("all")
|
||||
|
||||
const showCheckbox = ref(false)
|
||||
|
||||
const selectRequest = (request: TestRunnerRequest) => {
|
||||
const selectRequest = (request: TestRunnerRequest, indexPath: string) => {
|
||||
emit("onSelectRequest", request)
|
||||
emit("requestPath", indexPath)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,18 @@
|
|||
>
|
||||
<HttpTestResult v-model="doc.testResults" />
|
||||
</HoppSmartTab>
|
||||
<HoppSmartTab
|
||||
v-if="requestHeaders"
|
||||
id="req-headers"
|
||||
:label="t('response.request_headers')"
|
||||
:info="`${requestHeaders?.length}`"
|
||||
class="flex flex-1 flex-col"
|
||||
>
|
||||
<LensesHeadersRenderer
|
||||
:model-value="requestHeaders"
|
||||
:is-editable="false"
|
||||
/>
|
||||
</HoppSmartTab>
|
||||
</HoppSmartTabs>
|
||||
</template>
|
||||
|
||||
|
|
@ -50,11 +62,11 @@ import {
|
|||
import { useI18n } from "@composables/i18n"
|
||||
import { useVModel } from "@vueuse/core"
|
||||
import { HoppRequestDocument } from "~/helpers/rest/document"
|
||||
import { TestRunnerRequest } from "~/services/test-runner/test-runner.service"
|
||||
|
||||
const props = defineProps<{
|
||||
document: HoppRequestDocument | TestRunnerRequest
|
||||
document: HoppRequestDocument
|
||||
isEditable: boolean
|
||||
isTestRunner?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
|
@ -65,7 +77,6 @@ const emit = defineEmits<{
|
|||
const doc = useVModel(props, "document", emit)
|
||||
|
||||
const isSavable = computed(() => {
|
||||
if (doc.value.type === "test-response") return false
|
||||
return doc.value.response?.type === "success" && doc.value.saveContext
|
||||
})
|
||||
|
||||
|
|
@ -104,6 +115,11 @@ const maybeHeaders = computed(() => {
|
|||
return doc.value.response.headers
|
||||
})
|
||||
|
||||
const requestHeaders = computed(() => {
|
||||
if (!props.isTestRunner || !doc.value) return null
|
||||
return doc.value.request.headers
|
||||
})
|
||||
|
||||
const validLenses = computed(() => {
|
||||
if (!doc.value.response) return []
|
||||
return getSuitableLenses(doc.value.response)
|
||||
|
|
@ -120,8 +136,6 @@ watch(
|
|||
"results",
|
||||
]
|
||||
|
||||
if (doc.value.type === "test-response") return
|
||||
|
||||
const { responseTabPreference } = doc.value
|
||||
|
||||
if (
|
||||
|
|
@ -137,7 +151,7 @@ watch(
|
|||
)
|
||||
|
||||
watch(selectedLensTab, (newLensID) => {
|
||||
if (doc.value.type === "test-response") return
|
||||
if (props.isTestRunner) return
|
||||
doc.value.responseTabPreference = newLensID
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ import { HoppRESTResponse } from "./types/HoppRESTResponse"
|
|||
import { HoppTestData, HoppTestResult } from "./types/HoppTestResult"
|
||||
import { getEffectiveRESTRequest } from "./utils/EffectiveURL"
|
||||
import { isJSONContentType } from "./utils/contenttypes"
|
||||
import {
|
||||
getTemporaryVariables,
|
||||
setTemporaryVariables,
|
||||
} from "./runner/temp_envs"
|
||||
|
||||
const secretEnvironmentService = getService(SecretEnvironmentService)
|
||||
|
||||
|
|
@ -74,10 +78,12 @@ export const combineEnvVariables = (variables: {
|
|||
environments: {
|
||||
selected: Environment["variables"]
|
||||
global: Environment["variables"]
|
||||
temp?: Environment["variables"]
|
||||
}
|
||||
requestVariables: Environment["variables"]
|
||||
}) => [
|
||||
...variables.requestVariables,
|
||||
...(variables.environments.temp ?? []),
|
||||
...variables.environments.selected,
|
||||
...variables.environments.global,
|
||||
]
|
||||
|
|
@ -377,7 +383,17 @@ function updateEnvsAfterTestScript(runResult: E.Right<SandboxTestResult>) {
|
|||
return updatedRunResult
|
||||
}
|
||||
|
||||
export function runTestRunnerRequest(request: HoppRESTRequest): Promise<
|
||||
/**
|
||||
* Run the test runner request
|
||||
* @param request The request to run
|
||||
* @param persistEnv Whether to persist the environment variables after running the test script
|
||||
* @returns The response and the test result
|
||||
*/
|
||||
|
||||
export function runTestRunnerRequest(
|
||||
request: HoppRESTRequest,
|
||||
persistEnv = true
|
||||
): Promise<
|
||||
| E.Left<"script_fail">
|
||||
| E.Right<{
|
||||
response: HoppRESTResponse
|
||||
|
|
@ -399,7 +415,10 @@ export function runTestRunnerRequest(request: HoppRESTRequest): Promise<
|
|||
v: 1,
|
||||
name: "Env",
|
||||
variables: combineEnvVariables({
|
||||
environments: envs.right,
|
||||
environments: {
|
||||
...envs.right,
|
||||
temp: !persistEnv ? getTemporaryVariables() : [],
|
||||
},
|
||||
requestVariables: [],
|
||||
}),
|
||||
})
|
||||
|
|
@ -431,7 +450,18 @@ export function runTestRunnerRequest(request: HoppRESTRequest): Promise<
|
|||
runResult.right
|
||||
)
|
||||
|
||||
updateEnvsAfterTestScript(runResult)
|
||||
// Update the environment variables after running the test script when persistEnv is true. else store the updated environment variables in the store as a temporary variable.
|
||||
if (persistEnv) {
|
||||
updateEnvsAfterTestScript(runResult)
|
||||
} else {
|
||||
// Combine global and selected environment changes
|
||||
const allChanges = [
|
||||
...runResult.right.envs.global,
|
||||
...runResult.right.envs.selected,
|
||||
]
|
||||
|
||||
setTemporaryVariables(allChanges)
|
||||
}
|
||||
|
||||
return E.right({
|
||||
response: res,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ export const replaceTemplateStringsInObjectValues = <
|
|||
const restTabsService = getService(RESTTabService)
|
||||
|
||||
const requestVariables =
|
||||
source === "REST"
|
||||
source === "REST" &&
|
||||
restTabsService.currentActiveTab.value.document.type === "request"
|
||||
? restTabsService.currentActiveTab.value.document.request.requestVariables.map(
|
||||
({ key, value }) => ({
|
||||
key,
|
||||
|
|
|
|||
|
|
@ -216,12 +216,15 @@ export const teamCollToHoppRESTColl = (
|
|||
headers: [],
|
||||
}
|
||||
|
||||
const { auth, headers } = parseCollectionData(data)
|
||||
|
||||
return makeCollection({
|
||||
id: coll.id,
|
||||
name: coll.title,
|
||||
folders: coll.children?.map(teamCollToHoppRESTColl) ?? [],
|
||||
requests: coll.requests?.map((x) => x.request) ?? [],
|
||||
auth: data.auth ?? { authType: "inherit", authActive: true },
|
||||
headers: data.headers ?? [],
|
||||
auth: auth ?? { authType: "inherit", authActive: true },
|
||||
headers: headers ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ const unsecretEnvironments = (
|
|||
}
|
||||
}
|
||||
|
||||
export const getCombinedEnvVariables = () => {
|
||||
export const getCombinedEnvVariables = (temp?: Environment["variables"]) => {
|
||||
const reformedVars = unsecretEnvironments(
|
||||
getCurrentEnvironment(),
|
||||
getGlobalVariables()
|
||||
|
|
@ -71,6 +71,7 @@ export const getCombinedEnvVariables = () => {
|
|||
return {
|
||||
global: cloneDeep(reformedVars.global),
|
||||
selected: cloneDeep(reformedVars.selected),
|
||||
temp: temp ? cloneDeep(temp) : [],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +80,7 @@ export const getFinalEnvsFromPreRequest = (
|
|||
envs: {
|
||||
global: Environment["variables"]
|
||||
selected: Environment["variables"]
|
||||
temp: Environment["variables"]
|
||||
}
|
||||
): Promise<E.Either<string, TestResult["envs"]>> =>
|
||||
runPreRequestScript(script, envs)
|
||||
|
|
|
|||
|
|
@ -127,6 +127,12 @@ export type HoppTestRunnerDocument = {
|
|||
*/
|
||||
collectionID: string
|
||||
|
||||
/**
|
||||
* Selected request id
|
||||
* (if any)
|
||||
*/
|
||||
selectedRequestPath?: string
|
||||
|
||||
/**
|
||||
* The request as it is in the document
|
||||
*/
|
||||
|
|
@ -166,6 +172,12 @@ export type HoppTestRunnerDocument = {
|
|||
* (atleast as far as we can say)
|
||||
*/
|
||||
isDirty: boolean
|
||||
|
||||
/**
|
||||
* The inherited properties from the parent collection also the collection itself
|
||||
* (if any) - Used for team collections
|
||||
*/
|
||||
inheritedProperties?: HoppInheritedProperty
|
||||
}
|
||||
|
||||
export type HoppRequestDocument = {
|
||||
|
|
|
|||
20
packages/hoppscotch-common/src/helpers/runner/temp_envs.ts
Normal file
20
packages/hoppscotch-common/src/helpers/runner/temp_envs.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { ref } from "vue"
|
||||
import { GlobalEnvironmentVariable } from "@hoppscotch/data"
|
||||
|
||||
export const temporaryVariables = ref<GlobalEnvironmentVariable[]>([])
|
||||
|
||||
export function getTemporaryVariables() {
|
||||
return temporaryVariables.value
|
||||
}
|
||||
|
||||
export function setTemporaryVariables(variables: GlobalEnvironmentVariable[]) {
|
||||
temporaryVariables.value = variables
|
||||
}
|
||||
|
||||
export function clearTemporaryVariables() {
|
||||
temporaryVariables.value = []
|
||||
}
|
||||
|
||||
export function addTemporaryVariable(variable: GlobalEnvironmentVariable) {
|
||||
temporaryVariables.value.push(variable)
|
||||
}
|
||||
|
|
@ -537,6 +537,7 @@ export const REST_TAB_STATE_SCHEMA = z
|
|||
response: z.nullable(HoppRESTResponseSchema),
|
||||
testResults: z.optional(z.nullable(HoppTestResultSchema)),
|
||||
isDirty: z.boolean(),
|
||||
inheritedProperties: z.optional(HoppInheritedPropertySchema),
|
||||
}),
|
||||
z.object({
|
||||
// !Versioned entity
|
||||
|
|
|
|||
|
|
@ -268,7 +268,10 @@ export class TestRunnerService extends Service {
|
|||
error: undefined,
|
||||
})
|
||||
|
||||
const results = await runTestRunnerRequest(request)
|
||||
const results = await runTestRunnerRequest(
|
||||
request,
|
||||
options.keepVariableValues
|
||||
)
|
||||
|
||||
if (options.stopRef?.value) {
|
||||
throw new Error("Test execution stopped")
|
||||
|
|
|
|||
|
|
@ -598,14 +598,15 @@ function setupUserCollectionDuplicatedSubscription() {
|
|||
)
|
||||
|
||||
// Incoming data transformed to the respective internal representations
|
||||
const { auth, headers, _ref_id } =
|
||||
const { auth, headers } =
|
||||
data && data != "null"
|
||||
? JSON.parse(data)
|
||||
: {
|
||||
auth: { authType: "inherit", authActive: false },
|
||||
headers: [],
|
||||
_ref_id: generateUniqueRefId("coll"),
|
||||
}
|
||||
// Duplicated collection will have a unique ref id
|
||||
const _ref_id = generateUniqueRefId("coll")
|
||||
|
||||
const folders = transformDuplicatedCollections(childCollectionsJSONStr)
|
||||
|
||||
|
|
@ -1041,6 +1042,8 @@ function transformDuplicatedCollections(
|
|||
? JSON.parse(data)
|
||||
: { auth: { authType: "inherit", authActive: false }, headers: [] }
|
||||
|
||||
const _ref_id = generateUniqueRefId("coll")
|
||||
|
||||
const folders = transformDuplicatedCollections(childCollectionsJSONStr)
|
||||
|
||||
const requests = transformDuplicatedCollectionRequests(userRequests)
|
||||
|
|
@ -1050,6 +1053,7 @@ function transformDuplicatedCollections(
|
|||
name,
|
||||
folders,
|
||||
requests,
|
||||
_ref_id,
|
||||
v: 5,
|
||||
auth,
|
||||
headers: addDescriptionField(headers),
|
||||
|
|
|
|||
Loading…
Reference in a new issue