Merge branch 'master' into patch-zyb
This commit is contained in:
@@ -286,6 +286,11 @@ hr {
|
||||
.info:not(.toasted) {
|
||||
margin-left: 4px;
|
||||
color: var(--fg-light-color);
|
||||
|
||||
.material-icons {
|
||||
vertical-align: middle;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-color {
|
||||
@@ -535,11 +540,6 @@ ol {
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
|
||||
&.response-headers {
|
||||
display: inline-flex;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin: 0;
|
||||
|
||||
@@ -62,7 +62,7 @@ TODO:
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="collections.length === 0" class="info">
|
||||
Create new collection
|
||||
<i class="material-icons">help_outline</i> Create new collection
|
||||
</p>
|
||||
<div class="virtual-list">
|
||||
<ul>
|
||||
@@ -76,9 +76,6 @@ TODO:
|
||||
@edit-request="editRequest($event)"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="collections.length === 0">
|
||||
<label>Collections are empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<nuxt-link :to="localePath('doc')" :aria-label="$t('documentation')">
|
||||
|
||||
@@ -35,14 +35,6 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea
|
||||
id="variableList"
|
||||
readonly
|
||||
v-textarea-auto-height="variableString"
|
||||
v-model="variableString"
|
||||
:placeholder="$t('add_one_variable')"
|
||||
rows="1"
|
||||
></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(variable, index) in this.editingEnvCopy.variables" :key="index">
|
||||
@@ -114,13 +106,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import textareaAutoHeight from "~/directives/textareaAutoHeight"
|
||||
import { fb } from "~/helpers/fb"
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
textareaAutoHeight,
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingEnvironment: Object,
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="environments.length === 0" class="info">
|
||||
Create new environment
|
||||
<i class="material-icons">help_outline</i> Create new environment
|
||||
</p>
|
||||
<div class="virtual-list">
|
||||
<ul>
|
||||
@@ -37,9 +37,6 @@
|
||||
@select-environment="$emit('use-environment', environment)"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="environments.length === 0">
|
||||
<label>Environments are empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</pw-section>
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
<p v-if="history.length === 0" class="info">
|
||||
{{ $t("history_empty") }}
|
||||
<i class="material-icons">schedule</i> {{ $t("history_empty") }}
|
||||
</p>
|
||||
<div v-if="history.length !== 0">
|
||||
<div class="flex-wrap" v-if="!isClearingHistory">
|
||||
@@ -221,7 +221,7 @@
|
||||
</div>
|
||||
<div class="flex-wrap" v-else>
|
||||
<label for="clear-history-button" class="info">
|
||||
{{ $t("are_you_sure") }}
|
||||
<i class="material-icons">help_outline</i> {{ $t("are_you_sure") }}
|
||||
</label>
|
||||
<div>
|
||||
<button
|
||||
|
||||
44
components/lenses/ResponseBodyRenderer.vue
Normal file
44
components/lenses/ResponseBodyRenderer.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<tabs>
|
||||
<tab
|
||||
v-for="(lens, index) in validLenses"
|
||||
:key="lens.lensName"
|
||||
:id="lens.lensName"
|
||||
:label="lens.lensName"
|
||||
:selected="index === 0"
|
||||
>
|
||||
<component :is="lens.renderer" :response="response" />
|
||||
</tab>
|
||||
<tab
|
||||
v-if="Object.keys(response.headers).length !== 0"
|
||||
id="headers"
|
||||
:label="`Headers \xA0 • \xA0 ${Object.keys(response.headers).length}`"
|
||||
>
|
||||
<headers :headers="response.headers" />
|
||||
</tab>
|
||||
</tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSuitableLenses, getLensRenderers } from "~/helpers/lenses/lenses"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
tabs: () => import("../ui/tabs"),
|
||||
tab: () => import("../ui/tab"),
|
||||
headers: () => import("./headers"),
|
||||
// Lens Renderers
|
||||
...getLensRenderers(),
|
||||
},
|
||||
props: {
|
||||
response: {},
|
||||
},
|
||||
computed: {
|
||||
validLenses() {
|
||||
return getSuitableLenses(this.response)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
17
components/lenses/headers.vue
Normal file
17
components/lenses/headers.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul v-for="(value, key) in headers" :key="key">
|
||||
<li>
|
||||
<input :value="`${key} → ${value}`" :name="key" class="bg-color" readonly />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
headers: {},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
153
components/lenses/renderers/HTMLLensRenderer.vue
Normal file
153
components/lenses/renderers/HTMLLensRenderer.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="body">{{ $t("response") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="ToggleExpandResponse"
|
||||
ref="ToggleExpandResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="{
|
||||
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ !expandResponse ? "unfold_more" : "unfold_less" }}
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
v-if="response.body"
|
||||
class="icon"
|
||||
@click.prevent="togglePreview"
|
||||
v-tooltip="{
|
||||
content: previewEnabled ? $t('hide_preview') : $t('preview_html'),
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ !previewEnabled ? "visibility" : "visibility_off" }}
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="downloadResponse"
|
||||
ref="downloadResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="$t('download_file')"
|
||||
>
|
||||
<i class="material-icons">save_alt</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="copyResponse"
|
||||
ref="copyResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="$t('copy_response')"
|
||||
>
|
||||
<i class="material-icons">content_copy</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="response-details-wrapper">
|
||||
<Editor
|
||||
:value="responseBodyText"
|
||||
:lang="'html'"
|
||||
:options="{
|
||||
maxLines: responseBodyMaxLines,
|
||||
minLines: '16',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
/>
|
||||
<iframe
|
||||
:class="{ hidden: !previewEnabled }"
|
||||
class="covers-response"
|
||||
ref="previewFrame"
|
||||
src="about:blank"
|
||||
></iframe>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<script>
|
||||
import AceEditor from "../../ui/ace-editor"
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Editor: AceEditor,
|
||||
},
|
||||
mixins: [TextContentRendererMixin],
|
||||
props: {
|
||||
response: {},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expandResponse: false,
|
||||
responseBodyMaxLines: 16,
|
||||
doneButton: '<i class="material-icons">done</i>',
|
||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
||||
copyButton: '<i class="material-icons">content_copy</i>',
|
||||
previewEnabled: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
ToggleExpandResponse() {
|
||||
this.expandResponse = !this.expandResponse
|
||||
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
|
||||
},
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.responseBodyText
|
||||
const file = new Blob([dataToWrite], { type: "text/html" })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `response on ${Date()}`.replace(/\./g, "[dot]")
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.$refs.downloadResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$refs.downloadResponse.innerHTML = this.downloadButton
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
this.$refs.copyResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done",
|
||||
})
|
||||
const aux = document.createElement("textarea")
|
||||
const copy = this.responseBodyText
|
||||
aux.innerText = copy
|
||||
document.body.appendChild(aux)
|
||||
aux.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(aux)
|
||||
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000)
|
||||
},
|
||||
togglePreview() {
|
||||
this.previewEnabled = !this.previewEnabled
|
||||
if (this.previewEnabled) {
|
||||
if (this.$refs.previewFrame.getAttribute("data-previewing-url") === this.url) return
|
||||
// Use DOMParser to parse document HTML.
|
||||
const previewDocument = new DOMParser().parseFromString(this.responseBodyText, "text/html")
|
||||
// Inject <base href="..."> tag to head, to fix relative CSS/HTML paths.
|
||||
previewDocument.head.innerHTML =
|
||||
`<base href="${this.url}">` + previewDocument.head.innerHTML
|
||||
// Finally, set the iframe source to the resulting HTML.
|
||||
this.$refs.previewFrame.srcdoc = previewDocument.documentElement.outerHTML
|
||||
this.$refs.previewFrame.setAttribute("data-previewing-url", this.url)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
102
components/lenses/renderers/ImageLensRenderer.vue
Normal file
102
components/lenses/renderers/ImageLensRenderer.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="body">{{ $t("response") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="downloadResponse"
|
||||
ref="downloadResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="$t('download_file')"
|
||||
>
|
||||
<i class="material-icons">save_alt</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="response-details-wrapper">
|
||||
<img class="response-image" :src="imageSource" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.response-image {
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
response: {},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imageSource: "",
|
||||
doneButton: '<i class="material-icons">done</i>',
|
||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
responseType() {
|
||||
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase()
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
response: {
|
||||
immediate: true,
|
||||
handler(newValue) {
|
||||
this.imageSource = ""
|
||||
|
||||
const buf = this.response.body
|
||||
const bytes = new Uint8Array(buf)
|
||||
const blob = new Blob([bytes.buffer])
|
||||
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
this.imageSource = e.target.result
|
||||
}
|
||||
reader.readAsDataURL(blob)
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.imageSource = ""
|
||||
|
||||
const buf = this.response.body
|
||||
const bytes = new Uint8Array(buf)
|
||||
const blob = new Blob([bytes.buffer])
|
||||
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
this.imageSource = e.target.result
|
||||
}
|
||||
reader.readAsDataURL(blob)
|
||||
},
|
||||
methods: {
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.response.body
|
||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `response on ${Date()}`.replace(/\./g, "[dot]")
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.$refs.downloadResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$refs.downloadResponse.innerHTML = this.downloadButton
|
||||
}, 1000)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
149
components/lenses/renderers/JSONLensRenderer.vue
Normal file
149
components/lenses/renderers/JSONLensRenderer.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="body">{{ $t("response") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="ToggleExpandResponse"
|
||||
ref="ToggleExpandResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="{
|
||||
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ !expandResponse ? "unfold_more" : "unfold_less" }}
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="downloadResponse"
|
||||
ref="downloadResponse"
|
||||
v-if="response.body && canDownloadResponse"
|
||||
v-tooltip="$t('download_file')"
|
||||
>
|
||||
<i class="material-icons">save_alt</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="copyResponse"
|
||||
ref="copyResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="$t('copy_response')"
|
||||
>
|
||||
<i class="material-icons">content_copy</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="valid-warning" v-if="jsonInvalid">
|
||||
<p class="info"><i class="material-icons">error_outline</i> Invalid JSON</p>
|
||||
</div>
|
||||
<div id="response-details-wrapper">
|
||||
<Editor
|
||||
:value="jsonBodyText"
|
||||
:lang="'json'"
|
||||
:options="{
|
||||
maxLines: responseBodyMaxLines,
|
||||
minLines: '16',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AceEditor from "../../ui/ace-editor"
|
||||
import { isJSONContentType } from "~/helpers/utils/contenttypes"
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Editor: AceEditor,
|
||||
},
|
||||
mixins: [TextContentRendererMixin],
|
||||
props: {
|
||||
response: {},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expandResponse: false,
|
||||
jsonInvalid: false,
|
||||
responseBodyMaxLines: 16,
|
||||
doneButton: '<i class="material-icons">done</i>',
|
||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
||||
copyButton: '<i class="material-icons">content_copy</i>',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
jsonBodyText() {
|
||||
try {
|
||||
this.jsonInvalid = false
|
||||
return JSON.stringify(JSON.parse(this.responseBodyText), null, 2)
|
||||
} catch (e) {
|
||||
// Most probs invalid JSON was returned, so drop prettification (should we warn ?)
|
||||
this.jsonInvalid = true
|
||||
return this.responseBodyText
|
||||
}
|
||||
},
|
||||
responseType() {
|
||||
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase()
|
||||
},
|
||||
canDownloadResponse() {
|
||||
return (
|
||||
this.response &&
|
||||
this.response.headers &&
|
||||
this.response.headers["content-type"] &&
|
||||
isJSONContentType(this.response.headers["content-type"])
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
ToggleExpandResponse() {
|
||||
this.expandResponse = !this.expandResponse
|
||||
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
|
||||
},
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.responseBodyText
|
||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `response on ${Date()}`.replace(/\./g, "[dot]")
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.$refs.downloadResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$refs.downloadResponse.innerHTML = this.downloadButton
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
this.$refs.copyResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done",
|
||||
})
|
||||
const aux = document.createElement("textarea")
|
||||
const copy = this.responseBodyText
|
||||
aux.innerText = copy
|
||||
document.body.appendChild(aux)
|
||||
aux.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(aux)
|
||||
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
134
components/lenses/renderers/RawLensRenderer.vue
Normal file
134
components/lenses/renderers/RawLensRenderer.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="body">{{ $t("response") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="ToggleExpandResponse"
|
||||
ref="ToggleExpandResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="{
|
||||
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ !expandResponse ? "unfold_more" : "unfold_less" }}
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="downloadResponse"
|
||||
ref="downloadResponse"
|
||||
v-if="response.body && canDownloadResponse"
|
||||
v-tooltip="$t('download_file')"
|
||||
>
|
||||
<i class="material-icons">save_alt</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="copyResponse"
|
||||
ref="copyResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="$t('copy_response')"
|
||||
>
|
||||
<i class="material-icons">content_copy</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="response-details-wrapper">
|
||||
<Editor
|
||||
:value="responseBodyText"
|
||||
:lang="'plain_text'"
|
||||
:options="{
|
||||
maxLines: responseBodyMaxLines,
|
||||
minLines: '16',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<script>
|
||||
import AceEditor from "../../ui/ace-editor"
|
||||
import { isJSONContentType } from "~/helpers/utils/contenttypes"
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Editor: AceEditor,
|
||||
},
|
||||
mixins: [TextContentRendererMixin],
|
||||
props: {
|
||||
response: {},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expandResponse: false,
|
||||
responseBodyMaxLines: 16,
|
||||
doneButton: '<i class="material-icons">done</i>',
|
||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
||||
copyButton: '<i class="material-icons">content_copy</i>',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
responseType() {
|
||||
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase()
|
||||
},
|
||||
canDownloadResponse() {
|
||||
return (
|
||||
this.response &&
|
||||
this.response.headers &&
|
||||
this.response.headers["content-type"] &&
|
||||
isJSONContentType(this.response.headers["content-type"])
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
ToggleExpandResponse() {
|
||||
this.expandResponse = !this.expandResponse
|
||||
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
|
||||
},
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.responseBodyText
|
||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `response on ${Date()}`.replace(/\./g, "[dot]")
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.$refs.downloadResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$refs.downloadResponse.innerHTML = this.downloadButton
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
this.$refs.copyResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done",
|
||||
})
|
||||
const aux = document.createElement("textarea")
|
||||
const copy = this.responseBodyText
|
||||
aux.innerText = copy
|
||||
document.body.appendChild(aux)
|
||||
aux.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(aux)
|
||||
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
125
components/lenses/renderers/XMLLensRenderer.vue
Normal file
125
components/lenses/renderers/XMLLensRenderer.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="body">{{ $t("response") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="ToggleExpandResponse"
|
||||
ref="ToggleExpandResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="{
|
||||
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ !expandResponse ? "unfold_more" : "unfold_less" }}
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="downloadResponse"
|
||||
ref="downloadResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="$t('download_file')"
|
||||
>
|
||||
<i class="material-icons">save_alt</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="copyResponse"
|
||||
ref="copyResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="$t('copy_response')"
|
||||
>
|
||||
<i class="material-icons">content_copy</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="response-details-wrapper">
|
||||
<Editor
|
||||
:value="responseBodyText"
|
||||
:lang="'xml'"
|
||||
:options="{
|
||||
maxLines: responseBodyMaxLines,
|
||||
minLines: '16',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<script>
|
||||
import AceEditor from "../../ui/ace-editor"
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Editor: AceEditor,
|
||||
},
|
||||
mixins: [TextContentRendererMixin],
|
||||
props: {
|
||||
response: {},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expandResponse: false,
|
||||
responseBodyMaxLines: 16,
|
||||
doneButton: '<i class="material-icons">done</i>',
|
||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
||||
copyButton: '<i class="material-icons">content_copy</i>',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
responseType() {
|
||||
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
ToggleExpandResponse() {
|
||||
this.expandResponse = !this.expandResponse
|
||||
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
|
||||
},
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.responseBodyText
|
||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `response on ${Date()}`.replace(/\./g, "[dot]")
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.$refs.downloadResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$refs.downloadResponse.innerHTML = this.downloadButton
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
this.$refs.copyResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done",
|
||||
})
|
||||
const aux = document.createElement("textarea")
|
||||
const copy = this.responseBodyText
|
||||
aux.innerText = copy
|
||||
document.body.appendChild(aux)
|
||||
aux.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(aux)
|
||||
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,10 @@
|
||||
export default {
|
||||
props: {
|
||||
response: {},
|
||||
},
|
||||
computed: {
|
||||
responseBodyText() {
|
||||
return new TextDecoder("utf-8").decode(this.response.body)
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
export default {
|
||||
name: "textareaAutoHeight",
|
||||
update({ scrollHeight, clientHeight, style }) {
|
||||
if (scrollHeight !== clientHeight) {
|
||||
style.minHeight = `${scrollHeight}px`
|
||||
}
|
||||
},
|
||||
}
|
||||
8
helpers/lenses/htmlLens.js
Normal file
8
helpers/lenses/htmlLens.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const htmlLens = {
|
||||
lensName: "HTML",
|
||||
supportedContentTypes: ["text/html"],
|
||||
renderer: "htmlres",
|
||||
rendererImport: () => import("~/components/lenses/renderers/HTMLLensRenderer"),
|
||||
}
|
||||
|
||||
export default htmlLens
|
||||
16
helpers/lenses/imageLens.js
Normal file
16
helpers/lenses/imageLens.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const imageLens = {
|
||||
lensName: "Image",
|
||||
supportedContentTypes: [
|
||||
"image/gif",
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/bmp",
|
||||
"image/svg+xml",
|
||||
"image/x-icon",
|
||||
"image/vnd.microsoft.icon",
|
||||
],
|
||||
renderer: "imageres",
|
||||
rendererImport: () => import("~/components/lenses/renderers/ImageLensRenderer"),
|
||||
}
|
||||
|
||||
export default imageLens
|
||||
8
helpers/lenses/jsonLens.js
Normal file
8
helpers/lenses/jsonLens.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const jsonLens = {
|
||||
lensName: "JSON",
|
||||
supportedContentTypes: ["application/json", "application/hal+json", "application/vnd.api+json"],
|
||||
renderer: "json",
|
||||
rendererImport: () => import("~/components/lenses/renderers/JSONLensRenderer"),
|
||||
}
|
||||
|
||||
export default jsonLens
|
||||
37
helpers/lenses/lenses.js
Normal file
37
helpers/lenses/lenses.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import jsonLens from "./jsonLens"
|
||||
import rawLens from "./rawLens"
|
||||
import imageLens from "./imageLens"
|
||||
import htmlLens from "./htmlLens"
|
||||
import xmlLens from "./xmlLens"
|
||||
|
||||
const lenses = [jsonLens, imageLens, htmlLens, xmlLens, rawLens]
|
||||
|
||||
export function getSuitableLenses(response) {
|
||||
const result = []
|
||||
|
||||
if (response && response.headers && response.headers["content-type"]) {
|
||||
const properContentType = response.headers["content-type"].split(";")[0]
|
||||
|
||||
for (const lens of lenses) {
|
||||
if (
|
||||
lens.supportedContentTypes === null ||
|
||||
lens.supportedContentTypes.includes(properContentType)
|
||||
) {
|
||||
result.push(lens)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We don't know the content type, so lets just add rawLens
|
||||
result.push(rawLens)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export function getLensRenderers() {
|
||||
const response = {}
|
||||
for (const lens of lenses) {
|
||||
response[lens.renderer] = lens.rendererImport
|
||||
}
|
||||
return response
|
||||
}
|
||||
8
helpers/lenses/rawLens.js
Normal file
8
helpers/lenses/rawLens.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const rawLens = {
|
||||
lensName: "Raw",
|
||||
supportedContentTypes: null,
|
||||
renderer: "raw",
|
||||
rendererImport: () => import("~/components/lenses/renderers/RawLensRenderer"),
|
||||
}
|
||||
|
||||
export default rawLens
|
||||
8
helpers/lenses/xmlLens.js
Normal file
8
helpers/lenses/xmlLens.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const xmlLens = {
|
||||
lensName: "XML",
|
||||
supportedContentTypes: ["application/xml", "image/svg+xml", "text/xml", "application/rss+xml"],
|
||||
renderer: "xmlres",
|
||||
rendererImport: () => import("~/components/lenses/renderers/XMLLensRenderer"),
|
||||
}
|
||||
|
||||
export default xmlLens
|
||||
@@ -1,4 +1,5 @@
|
||||
import axios from "axios"
|
||||
import { decodeB64StringToArrayBuffer } from "../utils/b64"
|
||||
|
||||
let cancelSource = axios.CancelToken.source()
|
||||
|
||||
@@ -13,11 +14,19 @@ const axiosWithProxy = async (req, { state }) => {
|
||||
try {
|
||||
const { data } = await axios.post(
|
||||
state.postwoman.settings.PROXY_URL || "https://postwoman.apollosoftware.xyz/",
|
||||
req,
|
||||
{
|
||||
...req,
|
||||
wantsBinary: true,
|
||||
},
|
||||
{
|
||||
cancelToken: cancelSource.token,
|
||||
}
|
||||
)
|
||||
|
||||
if (data.isBinary) {
|
||||
data.data = decodeB64StringToArrayBuffer(data.data)
|
||||
}
|
||||
|
||||
return data
|
||||
} catch (e) {
|
||||
// Check if the throw is due to a cancellation
|
||||
@@ -34,28 +43,9 @@ const axiosWithoutProxy = async (req, _store) => {
|
||||
const res = await axios({
|
||||
...req,
|
||||
cancelToken: cancelSource.token,
|
||||
transformResponse: [
|
||||
(data, headers) => {
|
||||
// If the response has a JSON content type, try parsing it
|
||||
if (
|
||||
headers["content-type"] &&
|
||||
(headers["content-type"].startsWith("application/json") ||
|
||||
headers["content-type"].startsWith("application/vnd.api+json") ||
|
||||
headers["content-type"].startsWith("application/hal+json"))
|
||||
) {
|
||||
try {
|
||||
const jsonData = JSON.parse(data)
|
||||
return jsonData
|
||||
} catch (e) {
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
// Else return the string itself without any transformations
|
||||
return data
|
||||
},
|
||||
],
|
||||
responseType: "arraybuffer",
|
||||
})
|
||||
|
||||
return res
|
||||
} catch (e) {
|
||||
if (axios.isCancel(e)) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { decodeB64StringToArrayBuffer } from "../utils/b64"
|
||||
|
||||
export const hasExtensionInstalled = () =>
|
||||
typeof window.__POSTWOMAN_EXTENSION_HOOK__ !== "undefined"
|
||||
|
||||
@@ -17,13 +19,24 @@ const extensionWithProxy = async (req, { state }) => {
|
||||
const { data } = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest({
|
||||
method: "post",
|
||||
url: state.postwoman.settings.PROXY_URL || "https://postwoman.apollosoftware.xyz/",
|
||||
data: req,
|
||||
data: {
|
||||
...req,
|
||||
wantsBinary: true,
|
||||
},
|
||||
})
|
||||
|
||||
if (data.isBinary) {
|
||||
data.data = decodeB64StringToArrayBuffer(data.data)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
const extensionWithoutProxy = async (req, _store) => {
|
||||
const res = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest(req)
|
||||
const res = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest({
|
||||
...req,
|
||||
wantsBinary: true,
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
31
helpers/utils/b64.js
Normal file
31
helpers/utils/b64.js
Normal file
@@ -0,0 +1,31 @@
|
||||
export const decodeB64StringToArrayBuffer = (input) => {
|
||||
const bytes = Math.floor((input.length / 4) * 3)
|
||||
const ab = new ArrayBuffer(bytes)
|
||||
const uarray = new Uint8Array(ab)
|
||||
|
||||
const keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
|
||||
|
||||
let chr1, chr2, chr3
|
||||
let enc1, enc2, enc3, enc4
|
||||
let j = 0
|
||||
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "")
|
||||
|
||||
for (let i = 0; i < bytes; i += 3) {
|
||||
//get the 3 octets in 4 ASCII chars
|
||||
enc1 = keyStr.indexOf(input.charAt(j++))
|
||||
enc2 = keyStr.indexOf(input.charAt(j++))
|
||||
enc3 = keyStr.indexOf(input.charAt(j++))
|
||||
enc4 = keyStr.indexOf(input.charAt(j++))
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4)
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2)
|
||||
chr3 = ((enc3 & 3) << 6) | enc4
|
||||
|
||||
uarray[i] = chr1
|
||||
if (enc3 != 64) uarray[i + 1] = chr2
|
||||
if (enc4 != 64) uarray[i + 2] = chr3
|
||||
}
|
||||
|
||||
return ab
|
||||
}
|
||||
@@ -285,5 +285,7 @@
|
||||
"mqtt_topic_title": "Publish / Subscribe topic",
|
||||
"mqtt_publish": "Publish",
|
||||
"mqtt_subscribe": "Subscribe",
|
||||
"mqtt_unsubscribe": "Unsubscribe"
|
||||
"mqtt_unsubscribe": "Unsubscribe",
|
||||
"pre_request_script": "Pre-request Script",
|
||||
"tests": "Tests"
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="orange" :label="$t('headers')" ref="headers">
|
||||
<ul>
|
||||
<ul v-if="headers.length !== 0">
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="headerList">{{ $t("header_list") }}</label>
|
||||
@@ -37,14 +37,6 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea
|
||||
id="headerList"
|
||||
readonly
|
||||
v-textarea-auto-height="headerString"
|
||||
v-model="headerString"
|
||||
:placeholder="$t('add_one_header')"
|
||||
rows="1"
|
||||
></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(header, index) in headers" :key="`${header.value}_${index}`">
|
||||
@@ -339,7 +331,6 @@
|
||||
<script>
|
||||
import axios from "axios"
|
||||
import * as gql from "graphql"
|
||||
import textareaAutoHeight from "~/directives/textareaAutoHeight"
|
||||
import { commonHeaders } from "~/helpers/headers"
|
||||
import AceEditor from "~/components/ui/ace-editor"
|
||||
import QueryEditor from "~/components/graphql/queryeditor"
|
||||
@@ -347,9 +338,6 @@ import { getPlatformSpecialKey } from "~/helpers/platformutils"
|
||||
import { sendNetworkRequest } from "~/helpers/network"
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
textareaAutoHeight,
|
||||
},
|
||||
components: {
|
||||
"pw-section": () => import("~/components/layout/section"),
|
||||
"gql-field": () => import("~/components/graphql/field"),
|
||||
|
||||
703
pages/index.vue
703
pages/index.vue
@@ -2,39 +2,6 @@
|
||||
<div class="page">
|
||||
<div class="content">
|
||||
<div class="page-columns inner-left">
|
||||
<pw-section v-if="showPreRequestScript" class="orange" label="Pre-Request" ref="preRequest">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="generatedCode">{{ $t("javascript_code") }}</label>
|
||||
<div>
|
||||
<a
|
||||
href="https://github.com/liyasthomas/postwoman/wiki/Pre-Request-Scripts"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon" v-tooltip="$t('wiki')">
|
||||
<i class="material-icons">help_outline</i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<Editor
|
||||
v-model="preRequestScript"
|
||||
:lang="'javascript'"
|
||||
:options="{
|
||||
maxLines: '16',
|
||||
minLines: '8',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<pw-section class="blue" :label="$t('request')" ref="request">
|
||||
<ul>
|
||||
<li class="shrink">
|
||||
@@ -297,15 +264,18 @@
|
||||
<div v-if="!rawInput">
|
||||
<ul>
|
||||
<li>
|
||||
<label for="reqParamList">{{ $t("parameter_list") }}</label>
|
||||
<textarea
|
||||
id="reqParamList"
|
||||
readonly
|
||||
v-textarea-auto-height="rawRequestBody"
|
||||
v-model="rawRequestBody"
|
||||
:placeholder="$t('add_one_parameter')"
|
||||
rows="1"
|
||||
></textarea>
|
||||
<div class="flex-wrap">
|
||||
<label for="reqParamList">{{ $t("parameter_list") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="clearContent('bodyParams', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(param, index) in bodyParams" :key="index">
|
||||
@@ -364,7 +334,18 @@
|
||||
<div v-else>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="rawBody">{{ $t("raw_request_body") }}</label>
|
||||
<div class="flex-wrap">
|
||||
<label for="rawBody">{{ $t("raw_request_body") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="clearContent('rawParams', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Editor
|
||||
v-model="rawParams"
|
||||
:lang="rawInputEditorLang"
|
||||
@@ -402,40 +383,6 @@
|
||||
>
|
||||
<i class="material-icons">code</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
id="preRequestScriptButton"
|
||||
v-tooltip.bottom="{
|
||||
content: !showPreRequestScript
|
||||
? $t('show_prerequest_script')
|
||||
: $t('hide_prerequest_script'),
|
||||
}"
|
||||
@click="showPreRequestScript = !showPreRequestScript"
|
||||
>
|
||||
<i
|
||||
class="material-icons"
|
||||
:class="showPreRequestScript"
|
||||
v-if="!showPreRequestScript"
|
||||
>
|
||||
playlist_add
|
||||
</i>
|
||||
<i class="material-icons" :class="showPreRequestScript" v-else>
|
||||
close
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
id="preRequestScriptButto"
|
||||
v-tooltip.bottom="{
|
||||
content: !testsEnabled ? 'Enable Tests' : 'Disable Tests',
|
||||
}"
|
||||
@click="testsEnabled = !testsEnabled"
|
||||
>
|
||||
<i class="material-icons" :class="testsEnabled" v-if="!testsEnabled">
|
||||
playlist_add_check
|
||||
</i>
|
||||
<i class="material-icons" :class="testsEnabled" v-else>close</i>
|
||||
</button>
|
||||
</span>
|
||||
<span>
|
||||
<button
|
||||
@@ -471,73 +418,105 @@
|
||||
</div>
|
||||
</pw-section>
|
||||
|
||||
<pw-section v-if="testsEnabled" class="orange" label="Tests" ref="postRequestTests">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="generatedCode">{{ $t("javascript_code") }}</label>
|
||||
<div>
|
||||
<a
|
||||
href="https://github.com/liyasthomas/postwoman/wiki/Post-Requests-Tests"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon" v-tooltip="$t('wiki')">
|
||||
<i class="material-icons">help_outline</i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<Editor
|
||||
v-model="testScript"
|
||||
:lang="'javascript'"
|
||||
:options="{
|
||||
maxLines: '16',
|
||||
minLines: '8',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
/>
|
||||
<div v-if="testReports">
|
||||
<div class="flex-wrap">
|
||||
<label>Test Reports</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="clearContent('tests', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(testReport, index) in testReports" :key="index">
|
||||
<div v-if="testReport.startBlock" class="info">
|
||||
<h4>{{ testReport.startBlock }}</h4>
|
||||
</div>
|
||||
<p v-else-if="testReport.result" class="flex-wrap info">
|
||||
<span :class="testReport.styles.class">
|
||||
<i class="material-icons">
|
||||
{{ testReport.styles.icon }}
|
||||
</i>
|
||||
<span> {{ testReport.result }}</span>
|
||||
<span v-if="testReport.message">
|
||||
<label> • {{ testReport.message }}</label>
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
<div v-else-if="testReport.endBlock"><hr /></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<section id="options">
|
||||
<tabs>
|
||||
<tab :id="'authentication'" :label="$t('authentication')" :selected="true">
|
||||
<tab
|
||||
:id="'params'"
|
||||
:label="
|
||||
$t('parameters') + `${params.length !== 0 ? ' \xA0 • \xA0 ' + params.length : ''}`
|
||||
"
|
||||
:selected="true"
|
||||
>
|
||||
<pw-section class="pink" label="Parameters" ref="parameters">
|
||||
<ul v-if="params.length !== 0">
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="paramList">{{ $t("parameter_list") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="clearContent('parameters', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(param, index) in params" :key="index">
|
||||
<li>
|
||||
<input
|
||||
:placeholder="$t('parameter_count', { count: index + 1 })"
|
||||
:name="'param' + index"
|
||||
:value="param.key"
|
||||
@change="
|
||||
$store.commit('setKeyParams', {
|
||||
index,
|
||||
value: $event.target.value,
|
||||
})
|
||||
"
|
||||
autofocus
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
:placeholder="$t('value_count', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="param.value"
|
||||
@change="
|
||||
$store.commit('setValueParams', {
|
||||
index,
|
||||
value: $event.target.value,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<span class="select-wrapper">
|
||||
<select
|
||||
:name="'type' + index"
|
||||
@change="
|
||||
$store.commit('setTypeParams', {
|
||||
index,
|
||||
value: $event.target.value,
|
||||
})
|
||||
"
|
||||
>
|
||||
<option value="query" :selected="param.type === 'query'">{{
|
||||
$t("query")
|
||||
}}</option>
|
||||
<option value="path" :selected="param.type === 'path'">{{
|
||||
$t("path")
|
||||
}}</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="removeRequestParam(index)"
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
id="param"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="addRequestParam">
|
||||
<i class="material-icons">add</i>
|
||||
<span>{{ $t("add_new") }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
</tab>
|
||||
|
||||
<tab :id="'authentication'" :label="$t('authentication')">
|
||||
<pw-section class="cyan" :label="$t('authentication')" ref="authentication">
|
||||
<ul>
|
||||
<li>
|
||||
@@ -741,9 +720,14 @@
|
||||
</pw-section>
|
||||
</tab>
|
||||
|
||||
<tab :id="'headers'" :label="$t('headers')">
|
||||
<tab
|
||||
:id="'headers'"
|
||||
:label="
|
||||
$t('headers') + `${headers.length !== 0 ? ' \xA0 • \xA0 ' + headers.length : ''}`
|
||||
"
|
||||
>
|
||||
<pw-section class="orange" label="Headers" ref="headers">
|
||||
<ul>
|
||||
<ul v-if="headers.length !== 0">
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="headerList">{{ $t("header_list") }}</label>
|
||||
@@ -757,14 +741,6 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea
|
||||
id="headerList"
|
||||
readonly
|
||||
v-textarea-auto-height="headerString"
|
||||
v-model="headerString"
|
||||
:placeholder="$t('add_one_header')"
|
||||
rows="1"
|
||||
></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(header, index) in headers" :key="`${header.value}_${index}`">
|
||||
@@ -822,119 +798,123 @@
|
||||
</pw-section>
|
||||
</tab>
|
||||
|
||||
<tab :id="'params'" :label="$t('parameters')">
|
||||
<pw-section class="pink" label="Parameters" ref="parameters">
|
||||
<tab :id="'pre_request_script'" :label="$t('pre_request_script')">
|
||||
<pw-section
|
||||
v-if="showPreRequestScript"
|
||||
class="orange"
|
||||
:label="$t('pre_request_script')"
|
||||
ref="preRequest"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="paramList">{{ $t("parameter_list") }}</label>
|
||||
<label for="generatedCode">{{ $t("javascript_code") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="clearContent('parameters', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
<a
|
||||
href="https://github.com/liyasthomas/postwoman/wiki/Pre-Request-Scripts"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
<button class="icon" v-tooltip="$t('wiki')">
|
||||
<i class="material-icons">help_outline</i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<textarea
|
||||
id="paramList"
|
||||
readonly
|
||||
v-textarea-auto-height="queryString"
|
||||
v-model="queryString"
|
||||
:placeholder="$t('add_one_parameter')"
|
||||
rows="1"
|
||||
></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(param, index) in params" :key="index">
|
||||
<li>
|
||||
<input
|
||||
:placeholder="$t('parameter_count', { count: index + 1 })"
|
||||
:name="'param' + index"
|
||||
:value="param.key"
|
||||
@change="
|
||||
$store.commit('setKeyParams', {
|
||||
index,
|
||||
value: $event.target.value,
|
||||
})
|
||||
"
|
||||
autofocus
|
||||
<Editor
|
||||
v-model="preRequestScript"
|
||||
:lang="'javascript'"
|
||||
:options="{
|
||||
maxLines: '16',
|
||||
minLines: '8',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<input
|
||||
:placeholder="$t('value_count', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="param.value"
|
||||
@change="
|
||||
$store.commit('setValueParams', {
|
||||
index,
|
||||
value: $event.target.value,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<span class="select-wrapper">
|
||||
<select
|
||||
:name="'type' + index"
|
||||
@change="
|
||||
$store.commit('setTypeParams', {
|
||||
index,
|
||||
value: $event.target.value,
|
||||
})
|
||||
"
|
||||
>
|
||||
<option value="query" :selected="param.type === 'query'">{{
|
||||
$t("query")
|
||||
}}</option>
|
||||
<option value="path" :selected="param.type === 'path'">{{
|
||||
$t("path")
|
||||
}}</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
<div>
|
||||
<li>
|
||||
<button
|
||||
class="icon"
|
||||
@click="removeRequestParam(index)"
|
||||
v-tooltip.bottom="$t('delete')"
|
||||
id="param"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</pw-section>
|
||||
</tab>
|
||||
|
||||
<tab
|
||||
:id="'tests'"
|
||||
:label="
|
||||
$t('tests') +
|
||||
`${testReports.length !== 0 ? ' \xA0 • \xA0 ' + testReports.length : ''}`
|
||||
"
|
||||
>
|
||||
<pw-section
|
||||
v-if="testsEnabled"
|
||||
class="orange"
|
||||
:label="$t('tests')"
|
||||
ref="postRequestTests"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<button class="icon" @click="addRequestParam">
|
||||
<i class="material-icons">add</i>
|
||||
<span>{{ $t("add_new") }}</span>
|
||||
</button>
|
||||
<div class="flex-wrap">
|
||||
<label for="generatedCode">{{ $t("javascript_code") }}</label>
|
||||
<div>
|
||||
<a
|
||||
href="https://github.com/liyasthomas/postwoman/wiki/Post-Requests-Tests"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<button class="icon" v-tooltip="$t('wiki')">
|
||||
<i class="material-icons">help_outline</i>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<Editor
|
||||
v-model="testScript"
|
||||
:lang="'javascript'"
|
||||
:options="{
|
||||
maxLines: '16',
|
||||
minLines: '8',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
/>
|
||||
<div v-if="testReports.length !== 0">
|
||||
<div class="flex-wrap">
|
||||
<label>Test Reports</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="clearContent('tests', $event)"
|
||||
v-tooltip.bottom="$t('clear')"
|
||||
>
|
||||
<i class="material-icons">clear_all</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(testReport, index) in testReports" :key="index">
|
||||
<div v-if="testReport.startBlock" class="info">
|
||||
<h4>{{ testReport.startBlock }}</h4>
|
||||
</div>
|
||||
<p v-else-if="testReport.result" class="flex-wrap info">
|
||||
<span :class="testReport.styles.class">
|
||||
<i class="material-icons">
|
||||
{{ testReport.styles.icon }}
|
||||
</i>
|
||||
<span> {{ testReport.result }}</span>
|
||||
<span v-if="testReport.message">
|
||||
<label> • {{ testReport.message }}</label>
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
<div v-else-if="testReport.endBlock"><hr /></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
</tab>
|
||||
</tabs>
|
||||
|
||||
<!-- <div class="flex-wrap">
|
||||
<span></span>
|
||||
<button
|
||||
class="icon hide-on-small-screen"
|
||||
@click="activeSidebar = !activeSidebar"
|
||||
v-tooltip="{
|
||||
content: activeSidebar ? 'Hide Sidebar' : 'Show Sidebar'
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ activeSidebar ? "last_page" : "first_page" }}
|
||||
</i>
|
||||
</button>
|
||||
</div> -->
|
||||
</section>
|
||||
|
||||
<pw-section class="purple" id="response" :label="$t('response')" ref="response">
|
||||
@@ -952,83 +932,9 @@
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="response.body">
|
||||
<li>
|
||||
<div class="flex-wrap">
|
||||
<label for="body">{{ $t("response") }}</label>
|
||||
<div>
|
||||
<button
|
||||
class="icon"
|
||||
@click="ToggleExpandResponse"
|
||||
ref="ToggleExpandResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="{
|
||||
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
|
||||
}"
|
||||
>
|
||||
<i class="material-icons">
|
||||
{{ !expandResponse ? "unfold_more" : "unfold_less" }}
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="downloadResponse"
|
||||
ref="downloadResponse"
|
||||
v-if="response.body && canDownloadResponse"
|
||||
v-tooltip="$t('download_file')"
|
||||
>
|
||||
<i class="material-icons">save_alt</i>
|
||||
</button>
|
||||
<button
|
||||
class="icon"
|
||||
@click="copyResponse"
|
||||
ref="copyResponse"
|
||||
v-if="response.body"
|
||||
v-tooltip="$t('copy_response')"
|
||||
>
|
||||
<i class="material-icons">content_copy</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="response-details-wrapper">
|
||||
<Editor
|
||||
:value="responseBodyText"
|
||||
:lang="responseBodyType"
|
||||
:options="{
|
||||
maxLines: responseBodyMaxLines,
|
||||
minLines: '16',
|
||||
fontSize: '16px',
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
/>
|
||||
<iframe
|
||||
:class="{ hidden: !previewEnabled }"
|
||||
class="covers-response"
|
||||
ref="previewFrame"
|
||||
src="about:blank"
|
||||
></iframe>
|
||||
</div>
|
||||
<div class="align-right" v-if="response.body && responseType === 'text/html'">
|
||||
<button class="icon" @click.prevent="togglePreview">
|
||||
<i class="material-icons">
|
||||
{{ !previewEnabled ? "visibility" : "visibility_off" }}
|
||||
</i>
|
||||
<span>
|
||||
{{ previewEnabled ? $t("hide_preview") : $t("preview_html") }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-for="(value, key) in response.headers" :key="key" class="response-headers">
|
||||
<li>
|
||||
<label :for="key">{{ key }}</label>
|
||||
<input :id="key" :value="value" :name="key" readonly />
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="response.body && response.body !== $t('loading')">
|
||||
<response-renderer :response="response" />
|
||||
</div>
|
||||
</pw-section>
|
||||
</div>
|
||||
|
||||
@@ -1343,7 +1249,6 @@ import section from "~/components/layout/section"
|
||||
import url from "url"
|
||||
import querystring from "querystring"
|
||||
import { commonHeaders } from "~/helpers/headers"
|
||||
import textareaAutoHeight from "~/directives/textareaAutoHeight"
|
||||
import parseCurlCommand from "~/assets/js/curlparser.js"
|
||||
import getEnvironmentVariablesFromScript from "~/helpers/preRequest"
|
||||
import runTestScriptWithVariables from "~/helpers/postwomanTesting"
|
||||
@@ -1408,9 +1313,6 @@ const parseHeaders = (xhr) => {
|
||||
export const findStatusGroup = (responseStatus) =>
|
||||
statusCategories.find(({ statusCodeRegex }) => statusCodeRegex.test(responseStatus))
|
||||
export default {
|
||||
directives: {
|
||||
textareaAutoHeight,
|
||||
},
|
||||
components: {
|
||||
"pw-section": section,
|
||||
"pw-toggle": () => import("~/components/ui/toggle"),
|
||||
@@ -1426,15 +1328,16 @@ export default {
|
||||
login: () => import("~/components/firebase/login"),
|
||||
tabs: () => import("~/components/ui/tabs"),
|
||||
tab: () => import("~/components/ui/tab"),
|
||||
"response-renderer": () => import("~/components/lenses/ResponseBodyRenderer"),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
showPreRequestScript: false,
|
||||
testsEnabled: false,
|
||||
showPreRequestScript: true,
|
||||
testsEnabled: true,
|
||||
testScript: "// pw.expect('variable').toBe('value');",
|
||||
preRequestScript: "// pw.env.set('variable', 'value');",
|
||||
testReports: null,
|
||||
testReports: [],
|
||||
copyButton: '<i class="material-icons">content_copy</i>',
|
||||
downloadButton: '<i class="material-icons">save_alt</i>',
|
||||
doneButton: '<i class="material-icons">done</i>',
|
||||
@@ -1445,9 +1348,7 @@ export default {
|
||||
body: "",
|
||||
},
|
||||
validContentTypes: knownContentTypes,
|
||||
previewEnabled: false,
|
||||
paramsWatchEnabled: true,
|
||||
expandResponse: false,
|
||||
showTokenList: false,
|
||||
showTokenRequest: false,
|
||||
showTokenRequestList: false,
|
||||
@@ -1455,9 +1356,6 @@ export default {
|
||||
showRequestModal: false,
|
||||
editRequest: {},
|
||||
urlExcludes: {},
|
||||
responseBodyText: "",
|
||||
responseBodyType: "text",
|
||||
responseBodyMaxLines: 16,
|
||||
activeSidebar: true,
|
||||
fb,
|
||||
customMethod: false,
|
||||
@@ -1513,27 +1411,6 @@ export default {
|
||||
}
|
||||
this.setRouteQueryState()
|
||||
},
|
||||
"response.body": function (val) {
|
||||
if (
|
||||
this.response.body === this.$t("waiting_send_req") ||
|
||||
this.response.body === this.$t("loading")
|
||||
) {
|
||||
this.responseBodyText = this.response.body
|
||||
this.responseBodyType = "text"
|
||||
} else {
|
||||
if (isJSONContentType(this.responseType)) {
|
||||
this.responseBodyText = JSON.stringify(this.response.body, null, 2)
|
||||
this.responseBodyType =
|
||||
this.response.body.constructor.name === "Object" ? "json" : "json5"
|
||||
} else if (this.responseType === "text/html") {
|
||||
this.responseBodyText = this.response.body
|
||||
this.responseBodyType = "html"
|
||||
} else {
|
||||
this.responseBodyText = this.response.body
|
||||
this.responseBodyType = "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
params: {
|
||||
handler: function (newValue) {
|
||||
if (!this.paramsWatchEnabled) {
|
||||
@@ -1589,7 +1466,6 @@ export default {
|
||||
this.showRequestModal = true
|
||||
},
|
||||
method() {
|
||||
// this.$store.commit('setState', { 'value': ["POST", "PUT", "PATCH", "DELETE"].includes(this.method) ? 'application/json' : '', 'attribute': 'contentType' })
|
||||
this.contentType = ["POST", "PUT", "PATCH", "DELETE"].includes(this.method)
|
||||
? "application/json"
|
||||
: ""
|
||||
@@ -1609,14 +1485,6 @@ export default {
|
||||
isJSONContentType(this.contentType)
|
||||
)
|
||||
},
|
||||
canDownloadResponse() {
|
||||
return (
|
||||
this.response &&
|
||||
this.response.headers &&
|
||||
this.response.headers["content-type"] &&
|
||||
isJSONContentType(this.response.headers["content-type"])
|
||||
)
|
||||
},
|
||||
uri: {
|
||||
get() {
|
||||
return this.$store.state.request.uri ? this.$store.state.request.uri : this.url + this.path
|
||||
@@ -2138,9 +2006,6 @@ export default {
|
||||
// Start showing the loading bar as soon as possible.
|
||||
// The nuxt axios module will hide it when the request is made.
|
||||
this.$nuxt.$loading.start()
|
||||
if (this.$refs.response.$el.classList.contains("hidden")) {
|
||||
this.$refs.response.$el.classList.toggle("hidden")
|
||||
}
|
||||
this.previewEnabled = false
|
||||
this.response.status = this.$t("fetching")
|
||||
this.response.body = this.$t("loading")
|
||||
@@ -2473,67 +2338,6 @@ export default {
|
||||
document.execCommand("copy")
|
||||
setTimeout(() => (this.$refs.copyRequestCode.innerHTML = this.copyButton), 1000)
|
||||
},
|
||||
ToggleExpandResponse() {
|
||||
this.expandResponse = !this.expandResponse
|
||||
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity
|
||||
},
|
||||
copyResponse() {
|
||||
this.$refs.copyResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("copied_to_clipboard"), {
|
||||
icon: "done",
|
||||
})
|
||||
const aux = document.createElement("textarea")
|
||||
const copy = isJSONContentType(this.responseType)
|
||||
? JSON.stringify(this.response.body, null, 2)
|
||||
: this.response.body
|
||||
aux.innerText = copy
|
||||
document.body.appendChild(aux)
|
||||
aux.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(aux)
|
||||
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000)
|
||||
},
|
||||
downloadResponse() {
|
||||
const dataToWrite = JSON.stringify(this.response.body, null, 2)
|
||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
a.download = `${this.url + this.path} [${this.method}] on ${Date()}`.replace(/\./g, "[dot]")
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.$refs.downloadResponse.innerHTML = this.doneButton
|
||||
this.$toast.success(this.$t("download_started"), {
|
||||
icon: "done",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$refs.downloadResponse.innerHTML = this.downloadButton
|
||||
}, 1000)
|
||||
},
|
||||
togglePreview() {
|
||||
this.previewEnabled = !this.previewEnabled
|
||||
if (this.previewEnabled) {
|
||||
// If you want to add 'preview' support for other response types,
|
||||
// just add them here.
|
||||
if (this.responseType === "text/html") {
|
||||
// If the preview already has that URL loaded, let's not bother re-loading it all.
|
||||
if (this.$refs.previewFrame.getAttribute("data-previewing-url") === this.url) return
|
||||
// Use DOMParser to parse document HTML.
|
||||
const previewDocument = new DOMParser().parseFromString(
|
||||
this.response.body,
|
||||
this.responseType
|
||||
)
|
||||
// Inject <base href="..."> tag to head, to fix relative CSS/HTML paths.
|
||||
previewDocument.head.innerHTML =
|
||||
`<base href="${this.url}">` + previewDocument.head.innerHTML
|
||||
// Finally, set the iframe source to the resulting HTML.
|
||||
this.$refs.previewFrame.srcdoc = previewDocument.documentElement.outerHTML
|
||||
this.$refs.previewFrame.setAttribute("data-previewing-url", this.url)
|
||||
}
|
||||
}
|
||||
},
|
||||
setRouteQueryState() {
|
||||
const flat = (key) => (this[key] !== "" ? `${key}=${this[key]}&` : "")
|
||||
const deep = (key) => {
|
||||
@@ -2629,6 +2433,15 @@ export default {
|
||||
},
|
||||
clearContent(name, { target }) {
|
||||
switch (name) {
|
||||
case "bodyParams":
|
||||
this.bodyParams = []
|
||||
break
|
||||
case "rawParams":
|
||||
this.rawParams = "{}"
|
||||
break
|
||||
case "parameters":
|
||||
this.params = []
|
||||
break
|
||||
case "auth":
|
||||
this.auth = "None"
|
||||
this.httpUser = ""
|
||||
@@ -2638,12 +2451,6 @@ export default {
|
||||
this.tokens = []
|
||||
this.tokenReqs = []
|
||||
break
|
||||
case "headers":
|
||||
this.headers = []
|
||||
break
|
||||
case "parameters":
|
||||
this.params = []
|
||||
break
|
||||
case "access_token":
|
||||
this.accessTokenName = ""
|
||||
this.oidcDiscoveryUrl = ""
|
||||
@@ -2652,28 +2459,29 @@ export default {
|
||||
this.clientId = ""
|
||||
this.scope = ""
|
||||
break
|
||||
case "headers":
|
||||
this.headers = []
|
||||
break
|
||||
case "tests":
|
||||
this.testReports = []
|
||||
break
|
||||
case "tokens":
|
||||
this.tokens = []
|
||||
break
|
||||
case "tokenReqs":
|
||||
this.tokenReqs = []
|
||||
case "tests":
|
||||
this.testReports = null
|
||||
break
|
||||
default:
|
||||
this.method = "GET"
|
||||
this.url = "https://httpbin.org"
|
||||
this.path = "/get"
|
||||
this.uri = this.url + this.path
|
||||
this.label = ""
|
||||
this.bodyParams = []
|
||||
this.rawParams = "{}"
|
||||
this.files = []
|
||||
this.params = []
|
||||
this.auth = "None"
|
||||
this.httpUser = ""
|
||||
this.httpPassword = ""
|
||||
this.bearerToken = ""
|
||||
this.headers = []
|
||||
this.params = []
|
||||
this.bodyParams = []
|
||||
this.rawParams = ""
|
||||
this.showTokenRequest = false
|
||||
this.tokens = []
|
||||
this.tokenReqs = []
|
||||
@@ -2683,7 +2491,8 @@ export default {
|
||||
this.accessTokenUrl = ""
|
||||
this.clientId = ""
|
||||
this.scope = ""
|
||||
this.files = []
|
||||
this.headers = []
|
||||
this.testReports = []
|
||||
}
|
||||
target.innerHTML = this.doneButton
|
||||
this.$toast.info(this.$t("cleared"), {
|
||||
|
||||
@@ -14,21 +14,21 @@ describe("Authentication", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("Enable username and password in URL with toggler", () => {
|
||||
cy.visit("/", { retryOnStatusCodeFailure: true })
|
||||
.get("#auth")
|
||||
.select("Basic Auth")
|
||||
.get('input[name="http_basic_user"]', { timeout: 500 })
|
||||
.type("foo")
|
||||
.get('input[name="http_basic_passwd"]', { timeout: 500 })
|
||||
.type("bar")
|
||||
.url()
|
||||
.should("not.contain", "foo")
|
||||
.should("not.contain", "bar")
|
||||
.get(".toggle")
|
||||
.click()
|
||||
.url()
|
||||
.should("contain", "foo")
|
||||
.should("contain", "bar")
|
||||
})
|
||||
// it("Enable username and password in URL with toggler", () => {
|
||||
// cy.visit("/", { retryOnStatusCodeFailure: true })
|
||||
// .get("#auth")
|
||||
// .select("Basic Auth")
|
||||
// .get('input[name="http_basic_user"]', { timeout: 500 })
|
||||
// .type("foo")
|
||||
// .get('input[name="http_basic_passwd"]', { timeout: 500 })
|
||||
// .type("bar")
|
||||
// .url()
|
||||
// .should("not.contain", "foo")
|
||||
// .should("not.contain", "bar")
|
||||
// .get(".toggle")
|
||||
// .click()
|
||||
// .url()
|
||||
// .should("contain", "foo")
|
||||
// .should("contain", "bar")
|
||||
// })
|
||||
})
|
||||
|
||||
@@ -11,15 +11,15 @@
|
||||
// })
|
||||
// })
|
||||
|
||||
describe("Proxy enabled - external request", () => {
|
||||
it("Enable proxy and make a GET request to Postwoman API", () => {
|
||||
cy.enableProxy("/?url=https://postwoman.io&path=/.netlify/functions/api")
|
||||
.get("#send")
|
||||
.click()
|
||||
.get("#response-details-wrapper", { timeout: 24000 })
|
||||
.should("be.visible")
|
||||
.should(($wrapper) => {
|
||||
expect($wrapper).to.contain("Hello World")
|
||||
})
|
||||
})
|
||||
})
|
||||
// describe("Proxy enabled - external request", () => {
|
||||
// it("Enable proxy and make a GET request to Postwoman API", () => {
|
||||
// cy.enableProxy("/?url=https://postwoman.io&path=/.netlify/functions/api")
|
||||
// .get("#send")
|
||||
// .click()
|
||||
// .get("#response-details-wrapper", { timeout: 24000 })
|
||||
// .should("be.visible")
|
||||
// .should(($wrapper) => {
|
||||
// expect($wrapper).to.contain("Hello World")
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
|
||||
Reference in New Issue
Block a user