refactor: monorepo+pnpm (removed husky)

This commit is contained in:
Andrew Bastin
2021-09-10 00:28:28 +05:30
parent 917550ff4d
commit b28f82a881
445 changed files with 81301 additions and 63752 deletions

View File

@@ -0,0 +1,107 @@
import { mount } from "@vue/test-utils"
import GraphqlCard from "../graphql/Card"
const factory = (props) => {
return mount(GraphqlCard, {
propsData: props,
mocks: {
$t: (text) => text,
},
directives: {
tooltip() {
/* stub */
},
closePopover() {
/* stub */
},
},
})
}
const url = "https://dummydata.com"
const query = `query getUser($uid: String!) {
user(uid: $uid) {
name
}
}`
describe("GraphqlCard", () => {
test("Mounts properly if props are given", () => {
const wrapper = factory({
entry: {
type: "graphql",
url,
query,
star: false,
},
})
expect(wrapper).toBeTruthy()
})
// test("toggle-star emitted on clicking on star button", async () => {
// const wrapper = factory({
// entry: {
// type: "graphql",
// url,
// query,
// star: true,
// },
// })
// await wrapper.find("button[data-testid='star_button']").trigger("click")
// expect(wrapper.emitted("toggle-star")).toBeTruthy()
// })
test("query expands on clicking the show more button", async () => {
const wrapper = factory({
entry: {
type: "graphql",
url,
query,
star: true,
},
})
expect(wrapper.vm.query).toStrictEqual([
`query getUser($uid: String!) {`,
` user(uid: $uid) {`,
`...`,
])
await wrapper.find("button[data-testid='query_expand']").trigger("click")
expect(wrapper.vm.query).toStrictEqual([
`query getUser($uid: String!) {`,
` user(uid: $uid) {`,
` name`,
` }`,
`}`,
])
})
// test("use-entry emit on clicking the restore button", async () => {
// const wrapper = factory({
// entry: {
// type: "graphql",
// url,
// query,
// star: true,
// },
// })
// await wrapper
// .find("button[data-testid='restore_history_entry']")
// .trigger("click")
// expect(wrapper.emitted("use-entry")).toBeTruthy()
// })
// test("delete-entry emit on clicking the delete button", async () => {
// const wrapper = factory({
// entry: {
// type: "graphql",
// url,
// query,
// star: true,
// },
// })
// await wrapper
// .find("button[data-testid=delete_history_entry]")
// .trigger("click")
// expect(wrapper.emitted("delete-entry")).toBeTruthy()
// })
})

View File

@@ -0,0 +1,56 @@
import { mount } from "@vue/test-utils"
import RestCard from "../rest/Card"
const factory = (props) => {
return mount(RestCard, {
propsData: props,
mocks: {
$t: (text) => text,
},
directives: {
tooltip() {
/* stub */
},
closePopover() {
/* stub */
},
},
})
}
const url = "https://dummydata.com/get"
const entry = {
type: "rest",
url,
method: "GET",
status: 200,
star: false,
}
describe("RestCard", () => {
test("Mounts properly if props are given", () => {
const wrapper = factory({ entry })
expect(wrapper).toBeTruthy()
})
// test("toggle-star emitted on clicking on star button", async () => {
// const wrapper = factory({ entry })
// await wrapper.find("button[data-testid='star_button']").trigger("click")
// expect(wrapper.emitted("toggle-star")).toBeTruthy()
// })
// test("use-entry emit on clicking the restore button", async () => {
// const wrapper = factory({ entry })
// await wrapper
// .find("button[data-testid='restore_history_entry']")
// .trigger("click")
// expect(wrapper.emitted("use-entry")).toBeTruthy()
// })
// test("delete-entry emit on clicking the delete button", async () => {
// const wrapper = factory({ entry })
// await wrapper
// .find("button[data-testid=delete_history_entry]")
// .trigger("click")
// expect(wrapper.emitted("delete-entry")).toBeTruthy()
// })
})

View File

@@ -0,0 +1,110 @@
<template>
<div class="flex flex-col group">
<div class="flex items-center">
<span
class="
cursor-pointer
flex flex-1
min-w-0
py-2
pr-2
pl-4
transition
group-hover:text-secondaryDark
"
data-testid="restore_history_entry"
@click="useEntry"
>
<span class="truncate">
{{ entry.request.url }}
</span>
</span>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
svg="trash"
color="red"
:title="$t('action.remove')"
class="hidden group-hover:inline-flex"
data-testid="delete_history_entry"
@click.native="$emit('delete-entry')"
/>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="expand ? $t('hide.more') : $t('show.more')"
:svg="expand ? 'minimize-2' : 'maximize-2'"
class="hidden group-hover:inline-flex"
@click.native="expand = !expand"
/>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="!entry.star ? $t('add.star') : $t('remove.star')"
:svg="entry.star ? 'star-solid' : 'star'"
color="yellow"
:class="{ 'group-hover:inline-flex hidden': !entry.star }"
data-testid="star_button"
@click.native="$emit('toggle-star')"
/>
</div>
<div class="flex flex-col">
<span
v-for="(line, index) in query"
:key="`line-${index}`"
class="cursor-pointer text-secondaryLight px-4 whitespace-pre truncate"
data-testid="restore_history_entry"
@click="useEntry"
>{{ line }}</span
>
</div>
</div>
</template>
<script lang="ts">
import {
computed,
defineComponent,
PropType,
ref,
} from "@nuxtjs/composition-api"
import { makeGQLRequest } from "~/helpers/types/HoppGQLRequest"
import { setGQLSession } from "~/newstore/GQLSession"
import { GQLHistoryEntry } from "~/newstore/history"
export default defineComponent({
props: {
entry: { type: Object as PropType<GQLHistoryEntry>, default: () => {} },
showMore: Boolean,
},
setup(props) {
const expand = ref(false)
const query = computed(() =>
expand.value
? (props.entry.request.query.split("\n") as string[])
: (props.entry.request.query
.split("\n")
.slice(0, 2)
.concat(["..."]) as string[])
)
const useEntry = () => {
setGQLSession({
request: makeGQLRequest({
name: props.entry.request.name,
url: props.entry.request.url,
headers: props.entry.request.headers,
query: props.entry.request.query,
variables: props.entry.request.variables,
}),
schema: "",
response: props.entry.response,
})
}
return {
expand,
query,
useEntry,
}
},
})
</script>

View File

@@ -0,0 +1,167 @@
<template>
<AppSection label="history">
<div
class="
bg-primary
border-b border-dividerLight
flex
top-sidebarPrimaryStickyFold
z-10
sticky
"
>
<input
v-model="filterText"
type="search"
autocomplete="off"
class="bg-transparent flex w-full p-4 py-2"
:placeholder="$t('action.search')"
/>
<div class="flex">
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
to="https://docs.hoppscotch.io/features/history"
blank
:title="$t('app.wiki')"
svg="help-circle"
/>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
data-testid="clear_history"
:disabled="history.length === 0"
svg="trash-2"
:title="$t('action.clear_all')"
@click.native="confirmRemove = true"
/>
</div>
</div>
<div class="flex flex-col">
<div v-for="(entry, index) in filteredHistory" :key="`entry-${index}`">
<HistoryRestCard
v-if="page == 'rest'"
:id="index"
:entry="entry"
:show-more="showMore"
@toggle-star="toggleStar(entry)"
@delete-entry="deleteHistory(entry)"
@use-entry="useHistory(entry)"
/>
<HistoryGraphqlCard
v-if="page == 'graphql'"
:entry="entry"
:show-more="showMore"
@toggle-star="toggleStar(entry)"
@delete-entry="deleteHistory(entry)"
@use-entry="useHistory(entry)"
/>
</div>
</div>
<div
v-if="!(filteredHistory.length !== 0 || history.length === 0)"
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
>
<i class="opacity-75 pb-2 material-icons">manage_search</i>
<span class="text-center">
{{ $t("state.nothing_found") }} "{{ filterText }}"
</span>
</div>
<div
v-if="history.length === 0"
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
>
<i class="opacity-75 pb-2 material-icons">schedule</i>
<span class="text-center">
{{ $t("empty.history") }}
</span>
</div>
<SmartConfirmModal
:show="confirmRemove"
:title="$t('confirm.remove_history')"
@hide-modal="confirmRemove = false"
@resolve="clearHistory"
/>
</AppSection>
</template>
<script lang="ts">
import { defineComponent, PropType } from "@nuxtjs/composition-api"
import { useReadonlyStream } from "~/helpers/utils/composables"
import {
restHistory$,
graphqlHistory$,
clearRESTHistory,
clearGraphqlHistory,
toggleGraphqlHistoryEntryStar,
toggleRESTHistoryEntryStar,
deleteGraphqlHistoryEntry,
deleteRESTHistoryEntry,
RESTHistoryEntry,
GQLHistoryEntry,
} from "~/newstore/history"
import { setRESTRequest } from "~/newstore/RESTSession"
export default defineComponent({
props: {
page: { type: String as PropType<"rest" | "graphql">, default: null },
},
setup(props) {
return {
history: useReadonlyStream<RESTHistoryEntry[] | GQLHistoryEntry[]>(
props.page === "rest" ? restHistory$ : graphqlHistory$,
[]
),
}
},
data() {
return {
filterText: "",
showMore: false,
confirmRemove: false,
}
},
computed: {
filteredHistory(): any[] {
const filteringHistory = this.history as Array<
RESTHistoryEntry | GQLHistoryEntry
>
return filteringHistory.filter(
(entry: RESTHistoryEntry | GQLHistoryEntry) => {
const filterText = this.filterText.toLowerCase()
return Object.keys(entry).some((key) => {
let value = entry[key as keyof typeof entry]
if (value) {
value = typeof value !== "string" ? value.toString() : value
return value.toLowerCase().includes(filterText)
}
return false
})
}
)
},
},
methods: {
clearHistory() {
if (this.page === "rest") clearRESTHistory()
else clearGraphqlHistory()
this.$toast.success(this.$t("state.history_deleted").toString(), {
icon: "delete",
})
},
useHistory(entry: any) {
if (this.page === "rest") setRESTRequest(entry.request)
},
deleteHistory(entry: any) {
if (this.page === "rest") deleteRESTHistoryEntry(entry)
else deleteGraphqlHistoryEntry(entry)
this.$toast.success(this.$t("state.deleted").toString(), {
icon: "delete",
})
},
toggleStar(entry: any) {
if (this.page === "rest") toggleRESTHistoryEntryStar(entry)
else toggleGraphqlHistoryEntryStar(entry)
},
},
})
</script>

View File

@@ -0,0 +1,100 @@
<template>
<div class="flex items-center group">
<span
class="cursor-pointer flex px-2 w-16 justify-center items-center truncate"
:class="entryStatus.className"
data-testid="restore_history_entry"
:title="duration"
@click="$emit('use-entry')"
>
{{ entry.request.method }}
</span>
<span
class="
cursor-pointer
flex flex-1
min-w-0
py-2
pr-2
transition
group-hover:text-secondaryDark
"
data-testid="restore_history_entry"
:title="duration"
@click="$emit('use-entry')"
>
<span class="truncate">
{{ entry.request.endpoint }}
</span>
</span>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
svg="trash"
color="red"
:title="$t('action.remove')"
class="hidden group-hover:inline-flex"
data-testid="delete_history_entry"
@click.native="$emit('delete-entry')"
/>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="!entry.star ? $t('add.star') : $t('remove.star')"
:class="{ 'group-hover:inline-flex hidden': !entry.star }"
:svg="entry.star ? 'star-solid' : 'star'"
color="yellow"
data-testid="star_button"
@click.native="$emit('toggle-star')"
/>
</div>
</template>
<script lang="ts">
import {
computed,
defineComponent,
PropType,
useContext,
} from "@nuxtjs/composition-api"
import findStatusGroup from "~/helpers/findStatusGroup"
import { RESTHistoryEntry } from "~/newstore/history"
export default defineComponent({
props: {
entry: { type: Object as PropType<RESTHistoryEntry>, default: () => {} },
showMore: Boolean,
},
setup(props) {
const {
app: { i18n },
} = useContext()
const $t = i18n.t.bind(i18n)
const duration = computed(() => {
if (props.entry.responseMeta.duration) {
const responseDuration = props.entry.responseMeta.duration
if (!responseDuration) return ""
return responseDuration > 0
? `${$t("request.duration").toString()}: ${responseDuration}ms`
: $t("error.no_duration").toString()
} else return $t("error.no_duration").toString()
})
const entryStatus = computed(() => {
const foundStatusGroup = findStatusGroup(
props.entry.responseMeta.statusCode
)
return (
foundStatusGroup || {
className: "",
}
)
})
return {
duration,
entryStatus,
}
},
})
</script>