refactor: lint

This commit is contained in:
liyasthomas
2021-05-17 19:17:57 +05:30
parent e424d06026
commit d9ddc184cb
21 changed files with 455 additions and 364 deletions

View File

@@ -2,25 +2,30 @@
<div class="flex-col"> <div class="flex-col">
<div class="show-on-large-screen"> <div class="show-on-large-screen">
<input <input
v-model="message"
:aria-label="$t('label')" :aria-label="$t('label')"
type="text" type="text"
autofocus autofocus
v-model="message"
:placeholder="$t('paste_a_note')" :placeholder="$t('paste_a_note')"
@keyup.enter="formPost"
class="rounded-t-lg" class="rounded-t-lg"
@keyup.enter="formPost"
/> />
</div> </div>
<div class="border-b show-on-large-screen border-brdColor"> <div class="border-b show-on-large-screen border-brdColor">
<input <input
v-model="label"
:aria-label="$t('label')" :aria-label="$t('label')"
type="text" type="text"
autofocus autofocus
v-model="label"
:placeholder="$t('label')" :placeholder="$t('label')"
@keyup.enter="formPost" @keyup.enter="formPost"
/> />
<button class="icon" :disabled="!(this.message || this.label)" value="Save" @click="formPost"> <button
class="icon"
:disabled="!(message || label)"
value="Save"
@click="formPost"
>
<i class="material-icons">add</i> <i class="material-icons">add</i>
<span>Add</span> <span>Add</span>
</button> </button>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<button class="icon" @click="logout" v-close-popover> <button v-close-popover class="icon" @click="logout">
<i class="material-icons">exit_to_app</i> <i class="material-icons">exit_to_app</i>
<span>{{ $t("logout") }}</span> <span>{{ $t("logout") }}</span>
</button> </button>

View File

@@ -1,5 +1,7 @@
import feeds from "../Feeds"
import { shallowMount } from "@vue/test-utils" import { shallowMount } from "@vue/test-utils"
import feeds from "../Feeds"
import { fb } from "~/helpers/fb"
jest.mock("~/helpers/fb", () => ({ jest.mock("~/helpers/fb", () => ({
__esModule: true, __esModule: true,
@@ -27,8 +29,6 @@ jest.mock("~/helpers/fb", () => ({
}, },
})) }))
import { fb } from "~/helpers/fb"
const factory = () => const factory = () =>
shallowMount(feeds, { shallowMount(feeds, {
mocks: { mocks: {
@@ -53,7 +53,9 @@ describe("feeds", () => {
test("renders all the current feeds", () => { test("renders all the current feeds", () => {
const wrapper = factory() const wrapper = factory()
expect(wrapper.findAll("div[data-test='list-item']").wrappers).toHaveLength(4) expect(wrapper.findAll("div[data-test='list-item']").wrappers).toHaveLength(
4
)
}) })
test("feeds with no label displays the 'no_label' message", () => { test("feeds with no label displays the 'no_label' message", () => {
@@ -63,7 +65,7 @@ describe("feeds", () => {
wrapper wrapper
.findAll("label[data-test='list-label']") .findAll("label[data-test='list-label']")
.wrappers.map((e) => e.text()) .wrappers.map((e) => e.text())
.filter((text) => text == "no_label") .filter((text) => text === "no_label")
).toHaveLength(2) ).toHaveLength(2)
}) })
@@ -74,30 +76,28 @@ describe("feeds", () => {
wrapper wrapper
.findAll("li[data-test='list-message']") .findAll("li[data-test='list-message']")
.wrappers.map((e) => e.text()) .wrappers.map((e) => e.text())
.filter((text) => text == "empty") .filter((text) => text === "empty")
).toHaveLength(2) ).toHaveLength(2)
}) })
test("labels in the list are proper", () => { test("labels in the list are proper", () => {
const wrapper = factory() const wrapper = factory()
expect(wrapper.findAll("label[data-test='list-label']").wrappers.map((e) => e.text())).toEqual([ expect(
"First", wrapper
"Second", .findAll("label[data-test='list-label']")
"no_label", .wrappers.map((e) => e.text())
"no_label", ).toEqual(["First", "Second", "no_label", "no_label"])
])
}) })
test("messages in the list are proper", () => { test("messages in the list are proper", () => {
const wrapper = factory() const wrapper = factory()
expect(wrapper.findAll("li[data-test='list-message']").wrappers.map((e) => e.text())).toEqual([ expect(
"First Message", wrapper
"empty", .findAll("li[data-test='list-message']")
"Third Message", .wrappers.map((e) => e.text())
"empty", ).toEqual(["First Message", "empty", "Third Message", "empty"])
])
}) })
test("clicking on the delete button deletes the feed", async () => { test("clicking on the delete button deletes the feed", async () => {

View File

@@ -1,5 +1,7 @@
import inputform from "../Inputform"
import { shallowMount } from "@vue/test-utils" import { shallowMount } from "@vue/test-utils"
import inputform from "../Inputform"
import { fb } from "~/helpers/fb"
jest.mock("~/helpers/fb", () => ({ jest.mock("~/helpers/fb", () => ({
__esModule: true, __esModule: true,
@@ -9,8 +11,6 @@ jest.mock("~/helpers/fb", () => ({
}, },
})) }))
import { fb } from "~/helpers/fb"
const factory = () => const factory = () =>
shallowMount(inputform, { shallowMount(inputform, {
mocks: { mocks: {

View File

@@ -1,5 +1,7 @@
import logout from "../Logout"
import { shallowMount, createLocalVue } from "@vue/test-utils" import { shallowMount, createLocalVue } from "@vue/test-utils"
import logout from "../Logout"
import { fb } from "~/helpers/fb"
jest.mock("~/helpers/fb", () => ({ jest.mock("~/helpers/fb", () => ({
__esModule: true, __esModule: true,
@@ -9,8 +11,6 @@ jest.mock("~/helpers/fb", () => ({
}, },
})) }))
import { fb } from "~/helpers/fb"
const $toast = { const $toast = {
info: jest.fn(), info: jest.fn(),
show: jest.fn(), show: jest.fn(),
@@ -53,7 +53,9 @@ describe("logout", () => {
}) })
test("failed signout request fires a error toast", async () => { test("failed signout request fires a error toast", async () => {
fb.signOutUser.mockImplementationOnce(() => Promise.reject("test reject")) fb.signOutUser.mockImplementationOnce(() =>
Promise.reject(new Error("test reject"))
)
const wrapper = factory() const wrapper = factory()
const button = wrapper.find("button") const button = wrapper.find("button")

View File

@@ -1,5 +1,5 @@
import GraphqlCard from "../graphql/Card"
import { mount } from "@vue/test-utils" import { mount } from "@vue/test-utils"
import GraphqlCard from "../graphql/Card"
const factory = (props) => { const factory = (props) => {
return mount(GraphqlCard, { return mount(GraphqlCard, {
@@ -35,20 +35,20 @@ describe("GraphqlCard", () => {
const wrapper = factory({ const wrapper = factory({
entry: { entry: {
type: "graphql", type: "graphql",
url: url, url,
query: query, query,
star: false, star: false,
}, },
}) })
expect(wrapper).toBeTruthy() expect(wrapper).toBeTruthy()
}) })
test("toggle-star emitted on clicking on star button", async () => { test("toggle-star emitted on clicking on star button", () => {
const wrapper = factory({ const wrapper = factory({
entry: { entry: {
type: "graphql", type: "graphql",
url: url, url,
query: query, query,
star: true, star: true,
}, },
}) })
@@ -61,8 +61,8 @@ describe("GraphqlCard", () => {
const wrapper = factory({ const wrapper = factory({
entry: { entry: {
type: "graphql", type: "graphql",
url: url, url,
query: query, query,
star: true, star: true,
}, },
}) })
@@ -85,12 +85,14 @@ describe("GraphqlCard", () => {
const wrapper = factory({ const wrapper = factory({
entry: { entry: {
type: "graphql", type: "graphql",
url: url, url,
query: query, query,
star: true, star: true,
}, },
}) })
await wrapper.find("button[data-testid='restore_history_entry']").trigger("click") await wrapper
.find("button[data-testid='restore_history_entry']")
.trigger("click")
expect(wrapper.emitted("use-entry")).toBeTruthy() expect(wrapper.emitted("use-entry")).toBeTruthy()
}) })
@@ -98,12 +100,14 @@ describe("GraphqlCard", () => {
const wrapper = factory({ const wrapper = factory({
entry: { entry: {
type: "graphql", type: "graphql",
url: url, url,
query: query, query,
star: true, star: true,
}, },
}) })
await wrapper.find("button[data-testid=delete_history_entry]").trigger("click") await wrapper
.find("button[data-testid=delete_history_entry]")
.trigger("click")
expect(wrapper.emitted("delete-entry")).toBeTruthy() expect(wrapper.emitted("delete-entry")).toBeTruthy()
}) })
}) })

View File

@@ -1,44 +1,44 @@
import { shallowMount } from "@vue/test-utils"
import History from "../" import History from "../"
import { fb } from "~/helpers/fb" import { fb } from "~/helpers/fb"
import { shallowMount } from "@vue/test-utils"
const restHistory = [ const restHistory = [
{ {
id: "0", id: 0,
type: "rest", type: "rest",
}, },
{ {
id: "1", id: 1,
type: "rest", type: "rest",
}, },
{ {
id: "2", id: 2,
type: "rest", type: "rest",
}, },
] ]
const graphqlHistory = [ const graphqlHistory = [
{ {
id: "0", id: 0,
type: "graphql", type: "graphql",
}, },
{ {
id: "1", id: 1,
type: "graphql", type: "graphql",
}, },
{ {
id: "2", id: 2,
type: "graphql", type: "graphql",
}, },
] ]
var localStorageMock = (function () { const localStorageMock = (function () {
var store = { const store = {
history: JSON.stringify(restHistory), history: JSON.stringify(restHistory),
graphqlHistory: JSON.stringify(graphqlHistory), graphqlHistory: JSON.stringify(graphqlHistory),
} }
return { return {
getItem: function (key) { getItem(key) {
return store[key] return store[key]
}, },
setItem: jest.fn(), setItem: jest.fn(),
@@ -105,13 +105,13 @@ beforeEach(() => {
}) })
describe("Mount History", () => { describe("Mount History", () => {
test("Mounts rest history without login", async () => { test("Mounts rest history without login", () => {
const wrapper = factory({ const wrapper = factory({
page: "rest", page: "rest",
}) })
expect(wrapper).toBeTruthy() expect(wrapper).toBeTruthy()
}) })
test("Mounts rest history with login", async () => { test("Mounts rest history with login", () => {
fb.currentUser = "user" fb.currentUser = "user"
const wrapper = factory({ const wrapper = factory({
page: "rest", page: "rest",
@@ -119,13 +119,13 @@ describe("Mount History", () => {
expect(wrapper).toBeTruthy() expect(wrapper).toBeTruthy()
}) })
test("Mounts graphql history without login", async () => { test("Mounts graphql history without login", () => {
const wrapper = factory({ const wrapper = factory({
page: "rest", page: "rest",
}) })
expect(wrapper).toBeTruthy() expect(wrapper).toBeTruthy()
}) })
test("Mounts graphql history with login", async () => { test("Mounts graphql history with login", () => {
fb.currentUser = "user" fb.currentUser = "user"
const wrapper = factory({ const wrapper = factory({
page: "rest", page: "rest",
@@ -142,9 +142,14 @@ describe("Clear History", () => {
}) })
expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory) expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click") await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click") await wrapper
.find("button[data-testid='confirm_clear_history']")
.trigger("click")
expect(fb.clearHistory).not.toHaveBeenCalled() expect(fb.clearHistory).not.toHaveBeenCalled()
expect(window.localStorage.setItem).toHaveBeenCalledWith("history", JSON.stringify([])) expect(window.localStorage.setItem).toHaveBeenCalledWith(
"history",
JSON.stringify([])
)
}) })
test("Clear rest history with login", async () => { test("Clear rest history with login", async () => {
fb.currentUser = "user" fb.currentUser = "user"
@@ -153,9 +158,14 @@ describe("Clear History", () => {
}) })
expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory) expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click") await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click") await wrapper
.find("button[data-testid='confirm_clear_history']")
.trigger("click")
expect(fb.clearHistory).toHaveBeenCalledTimes(1) expect(fb.clearHistory).toHaveBeenCalledTimes(1)
expect(window.localStorage.setItem).toHaveBeenCalledWith("history", JSON.stringify([])) expect(window.localStorage.setItem).toHaveBeenCalledWith(
"history",
JSON.stringify([])
)
}) })
test("Dont confirm Clear rest history", async () => { test("Dont confirm Clear rest history", async () => {
fb.currentUser = "user" fb.currentUser = "user"
@@ -164,9 +174,14 @@ describe("Clear History", () => {
}) })
expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory) expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click") await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='reject_clear_history']").trigger("click") await wrapper
.find("button[data-testid='reject_clear_history']")
.trigger("click")
expect(fb.clearHistory).not.toHaveBeenCalled() expect(fb.clearHistory).not.toHaveBeenCalled()
expect(window.localStorage.setItem).not.toHaveBeenCalledWith("history", JSON.stringify([])) expect(window.localStorage.setItem).not.toHaveBeenCalledWith(
"history",
JSON.stringify([])
)
}) })
test("Clear graphql history without login", async () => { test("Clear graphql history without login", async () => {
@@ -176,9 +191,14 @@ describe("Clear History", () => {
}) })
expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory) expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click") await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click") await wrapper
.find("button[data-testid='confirm_clear_history']")
.trigger("click")
expect(fb.clearGraphqlHistory).not.toHaveBeenCalled() expect(fb.clearGraphqlHistory).not.toHaveBeenCalled()
expect(window.localStorage.setItem).toHaveBeenCalledWith("graphqlHistory", JSON.stringify([])) expect(window.localStorage.setItem).toHaveBeenCalledWith(
"graphqlHistory",
JSON.stringify([])
)
}) })
test("Clear graphql history with login", async () => { test("Clear graphql history with login", async () => {
fb.currentUser = "user" fb.currentUser = "user"
@@ -187,9 +207,14 @@ describe("Clear History", () => {
}) })
expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory) expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click") await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click") await wrapper
.find("button[data-testid='confirm_clear_history']")
.trigger("click")
expect(fb.clearGraphqlHistory).toHaveBeenCalledTimes(1) expect(fb.clearGraphqlHistory).toHaveBeenCalledTimes(1)
expect(window.localStorage.setItem).toHaveBeenCalledWith("graphqlHistory", JSON.stringify([])) expect(window.localStorage.setItem).toHaveBeenCalledWith(
"graphqlHistory",
JSON.stringify([])
)
}) })
test("Dont confirm Clear graphql history", async () => { test("Dont confirm Clear graphql history", async () => {
fb.currentUser = "user" fb.currentUser = "user"
@@ -198,7 +223,9 @@ describe("Clear History", () => {
}) })
expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory) expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory)
await wrapper.find("button[data-testid='clear_history']").trigger("click") await wrapper.find("button[data-testid='clear_history']").trigger("click")
await wrapper.find("button[data-testid='reject_clear_history']").trigger("click") await wrapper
.find("button[data-testid='reject_clear_history']")
.trigger("click")
expect(window.localStorage.setItem).not.toHaveBeenCalledWith( expect(window.localStorage.setItem).not.toHaveBeenCalledWith(
"graphqlHistory", "graphqlHistory",
JSON.stringify([]) JSON.stringify([])
@@ -207,78 +234,39 @@ describe("Clear History", () => {
}) })
describe("Use History", () => { describe("Use History", () => {
test("use rest history", async () => { test("use rest history", () => {
fb.currentUser = "user" fb.currentUser = "user"
const wrapper = factory({ const wrapper = factory({
page: "rest", page: "rest",
}) })
expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(restHistory.length) expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(
var index = restHistory.length - 1 restHistory.length
wrapper.findAll("div[data-testid='rest_card']").at(index).vm.$emit("use-entry") )
const index = restHistory.length - 1
wrapper
.findAll("div[data-testid='rest_card']")
.at(index)
.vm.$emit("use-entry")
expect(wrapper.emitted("useHistory")).toBeTruthy() expect(wrapper.emitted("useHistory")).toBeTruthy()
expect(wrapper.emitted("useHistory")[0]).toStrictEqual([restHistory[index]]) expect(wrapper.emitted("useHistory")[0]).toStrictEqual([restHistory[index]])
}) })
test("use graphql history", async () => { test("use graphql history", () => {
fb.currentUser = "user" fb.currentUser = "user"
const wrapper = factory({ const wrapper = factory({
page: "graphql", page: "graphql",
}) })
expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(graphqlHistory.length) expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(
var index = restHistory.length - 1 graphqlHistory.length
wrapper.findAll("div[data-testid='graphql_card']").at(index).vm.$emit("use-entry") )
const index = restHistory.length - 1
wrapper
.findAll("div[data-testid='graphql_card']")
.at(index)
.vm.$emit("use-entry")
expect(wrapper.emitted("useHistory")).toBeTruthy() expect(wrapper.emitted("useHistory")).toBeTruthy()
expect(wrapper.emitted("useHistory")[0]).toStrictEqual([graphqlHistory[index]]) expect(wrapper.emitted("useHistory")[0]).toStrictEqual([
}) graphqlHistory[index],
}) ])
describe("Delete History", () => {
test("delete rest history with login", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "rest",
})
expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(restHistory.length)
var index = 1
wrapper.findAll("div[data-testid='rest_card']").at(index).vm.$emit("delete-entry")
expect(fb.deleteHistory).toBeCalledWith(restHistory[index])
})
test("delete rest history without login", async () => {
fb.currentUser = null
const wrapper = factory({
page: "rest",
})
expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(restHistory.length)
var index = 1
wrapper.findAll("div[data-testid='rest_card']").at(index).vm.$emit("delete-entry")
expect(window.localStorage.setItem).toBeCalledWith(
"history",
JSON.stringify(restHistory.filter((entry) => entry.id != index))
)
})
test("delete graphql history with login", async () => {
fb.currentUser = "user"
const wrapper = factory({
page: "graphql",
})
expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(graphqlHistory.length)
var index = 1
wrapper.findAll("div[data-testid='graphql_card']").at(index).vm.$emit("delete-entry")
expect(fb.deleteGraphqlHistory).toBeCalledWith(graphqlHistory[index])
})
test("delete graphql history without login", async () => {
fb.currentUser = null
const wrapper = factory({
page: "graphql",
})
expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(graphqlHistory.length)
var index = 1
wrapper.findAll("div[data-testid='graphql_card']").at(index).vm.$emit("delete-entry")
expect(window.localStorage.setItem).toBeCalledWith(
"graphqlHistory",
JSON.stringify(graphqlHistory.filter((entry) => entry.id != index))
)
}) })
}) })

View File

@@ -1,5 +1,5 @@
import RestCard from "../rest/Card"
import { mount } from "@vue/test-utils" import { mount } from "@vue/test-utils"
import RestCard from "../rest/Card"
const factory = (props) => { const factory = (props) => {
return mount(RestCard, { return mount(RestCard, {
@@ -26,7 +26,7 @@ const factory = (props) => {
const url = "https://dummydata.com/get" const url = "https://dummydata.com/get"
const entry = { const entry = {
type: "rest", type: "rest",
url: url, url,
method: "GET", method: "GET",
status: 200, status: 200,
star: false, star: false,
@@ -37,7 +37,7 @@ describe("RestCard", () => {
expect(wrapper).toBeTruthy() expect(wrapper).toBeTruthy()
}) })
test("toggle-star emitted on clicking on star button", async () => { test("toggle-star emitted on clicking on star button", () => {
const wrapper = factory({ entry }) const wrapper = factory({ entry })
wrapper.find("button[data-testid='star_button']").trigger("click") wrapper.find("button[data-testid='star_button']").trigger("click")
@@ -46,13 +46,17 @@ describe("RestCard", () => {
test("use-entry emit on clicking the restore button", async () => { test("use-entry emit on clicking the restore button", async () => {
const wrapper = factory({ entry }) const wrapper = factory({ entry })
await wrapper.find("button[data-testid='restore_history_entry']").trigger("click") await wrapper
.find("button[data-testid='restore_history_entry']")
.trigger("click")
expect(wrapper.emitted("use-entry")).toBeTruthy() expect(wrapper.emitted("use-entry")).toBeTruthy()
}) })
test("delete-entry emit on clicking the delete button", async () => { test("delete-entry emit on clicking the delete button", async () => {
const wrapper = factory({ entry }) const wrapper = factory({ entry })
await wrapper.find("button[data-testid=delete_history_entry]").trigger("click") await wrapper
.find("button[data-testid=delete_history_entry]")
.trigger("click")
expect(wrapper.emitted("delete-entry")).toBeTruthy() expect(wrapper.emitted("delete-entry")).toBeTruthy()
}) })
}) })

View File

@@ -13,42 +13,46 @@
/> />
</li> </li>
<button <button
v-tooltip="{
content: !entry.star ? $t('add_star') : $t('remove_star'),
}"
data-testid="star_button" data-testid="star_button"
class="icon" class="icon"
:class="{ stared: entry.star }" :class="{ stared: entry.star }"
@click="$emit('toggle-star')" @click="$emit('toggle-star')"
v-tooltip="{
content: !entry.star ? $t('add_star') : $t('remove_star'),
}"
> >
<i class="material-icons"> <i class="material-icons">
{{ entry.star ? "star" : "star_border" }} {{ entry.star ? "star" : "star_border" }}
</i> </i>
</button> </button>
<button <button
data-testid="query_expand"
class="icon"
@click="expand = !expand"
v-tooltip="{ v-tooltip="{
content: expand ? $t('hide_more') : $t('show_more'), content: expand ? $t('hide_more') : $t('show_more'),
}" }"
data-testid="query_expand"
class="icon"
@click="expand = !expand"
> >
<i class="material-icons"> <i class="material-icons">
{{ expand ? "unfold_less" : "unfold_more" }} {{ expand ? "unfold_less" : "unfold_more" }}
</i> </i>
</button> </button>
<v-popover> <v-popover>
<button data-testid="options" class="tooltip-target icon" v-tooltip="$t('options')"> <button
v-tooltip="$t('options')"
data-testid="options"
class="tooltip-target icon"
>
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
<template slot="popover"> <template slot="popover">
<div> <div>
<button <button
v-close-popover
data-testid="restore_history_entry" data-testid="restore_history_entry"
class="icon" class="icon"
@click="$emit('use-entry')"
:aria-label="$t('restore')" :aria-label="$t('restore')"
v-close-popover @click="$emit('use-entry')"
> >
<i class="material-icons">restore</i> <i class="material-icons">restore</i>
<span>{{ $t("restore") }}</span> <span>{{ $t("restore") }}</span>
@@ -56,11 +60,11 @@
</div> </div>
<div> <div>
<button <button
v-close-popover
data-testid="delete_history_entry" data-testid="delete_history_entry"
class="icon" class="icon"
@click="$emit('delete-entry')"
:aria-label="$t('delete')" :aria-label="$t('delete')"
v-close-popover @click="$emit('delete-entry')"
> >
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span> <span>{{ $t("delete") }}</span>
@@ -86,11 +90,11 @@
<div v-if="showMore" class="show-on-large-screen"> <div v-if="showMore" class="show-on-large-screen">
<li> <li>
<input <input
v-tooltip="entry.date"
:aria-label="$t('time')" :aria-label="$t('time')"
type="text" type="text"
readonly readonly
:value="entry.time" :value="entry.time"
v-tooltip="entry.date"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor" class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/> />
</li> </li>
@@ -119,24 +123,10 @@
</div> </div>
</template> </template>
<style scoped lang="scss">
.stared {
color: #f8e81c !important;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>
<script> <script>
export default { export default {
props: { props: {
entry: Object, entry: { type: Object, default: () => {} },
showMore: Boolean, showMore: Boolean,
}, },
data() { data() {
@@ -153,3 +143,17 @@ export default {
}, },
} }
</script> </script>
<style scoped lang="scss">
.stared {
color: #f8e81c !important;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@@ -20,13 +20,13 @@
</li> </li>
<span> <span>
<button <button
v-tooltip="{
content: !entry.star ? $t('add_star') : $t('remove_star'),
}"
data-testid="star_button" data-testid="star_button"
class="icon" class="icon"
:class="{ stared: entry.star }" :class="{ stared: entry.star }"
@click="$emit('toggle-star')" @click="$emit('toggle-star')"
v-tooltip="{
content: !entry.star ? $t('add_star') : $t('remove_star'),
}"
> >
<i class="material-icons"> <i class="material-icons">
{{ entry.star ? "star" : "star_border" }} {{ entry.star ? "star" : "star_border" }}
@@ -48,17 +48,17 @@
</button> </button>
</li> --> </li> -->
<v-popover> <v-popover>
<button class="tooltip-target icon" v-tooltip="$t('options')"> <button v-tooltip="$t('options')" class="tooltip-target icon">
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
<template slot="popover"> <template slot="popover">
<div> <div>
<button <button
v-close-popover
data-testid="restore_history_entry" data-testid="restore_history_entry"
class="icon" class="icon"
@click="$emit('use-entry')"
:aria-label="$t('edit')" :aria-label="$t('edit')"
v-close-popover @click="$emit('use-entry')"
> >
<i class="material-icons">restore</i> <i class="material-icons">restore</i>
<span>{{ $t("restore") }}</span> <span>{{ $t("restore") }}</span>
@@ -66,11 +66,11 @@
</div> </div>
<div> <div>
<button <button
v-close-popover
data-testid="delete_history_entry" data-testid="delete_history_entry"
class="icon" class="icon"
@click="$emit('delete-entry')"
:aria-label="$t('delete')" :aria-label="$t('delete')"
v-close-popover @click="$emit('delete-entry')"
> >
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
<span>{{ $t("delete") }}</span> <span>{{ $t("delete") }}</span>
@@ -95,11 +95,11 @@
<div v-if="showMore" class="show-on-large-screen"> <div v-if="showMore" class="show-on-large-screen">
<li> <li>
<input <input
v-tooltip="entry.date"
:aria-label="$t('time')" :aria-label="$t('time')"
type="text" type="text"
readonly readonly
:value="entry.time" :value="entry.time"
v-tooltip="entry.date"
class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor" class="pt-0 mt-0 text-sm bg-transparent text-fgLightColor"
/> />
</li> </li>
@@ -128,26 +128,12 @@
</div> </div>
</template> </template>
<style scoped lang="scss">
.stared {
color: #f8e81c !important;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>
<script> <script>
import findStatusGroup from "~/helpers/findStatusGroup" import findStatusGroup from "~/helpers/findStatusGroup"
export default { export default {
props: { props: {
entry: Object, entry: { type: Object, default: () => {} },
showMore: Boolean, showMore: Boolean,
}, },
data() { data() {
@@ -167,3 +153,17 @@ export default {
}, },
} }
</script> </script>
<style scoped lang="scss">
.stared {
color: #f8e81c !important;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@@ -1,7 +1,12 @@
<template> <template>
<div> <div>
<p v-for="(value, key) in headers" :key="key"> <p v-for="(value, key) in headers" :key="key">
<input :value="`${key} → ${value}`" :name="key" class="bg-transparent" readonly /> <input
:value="`${key} → ${value}`"
:name="key"
class="bg-transparent"
readonly
/>
</p> </p>
</div> </div>
</template> </template>
@@ -9,7 +14,7 @@
<script> <script>
export default { export default {
props: { props: {
headers: {}, headers: { type: Object, default: () => {} },
}, },
} }
</script> </script>

View File

@@ -2,8 +2,8 @@
<SmartTabs styles="m-4"> <SmartTabs styles="m-4">
<SmartTab <SmartTab
v-for="(lens, index) in validLenses" v-for="(lens, index) in validLenses"
:key="lens.lensName"
:id="lens.lensName" :id="lens.lensName"
:key="lens.lensName"
:label="lens.lensName" :label="lens.lensName"
:selected="index === 0" :selected="index === 0"
> >
@@ -28,7 +28,7 @@ export default {
...getLensRenderers(), ...getLensRenderers(),
}, },
props: { props: {
response: {}, response: { type: Object, default: () => {} },
}, },
computed: { computed: {
validLenses() { validLenses() {

View File

@@ -4,13 +4,15 @@
<label for="body">{{ $t("response_body") }}</label> <label for="body">{{ $t("response_body") }}</label>
<div> <div>
<button <button
v-if="response.body"
ref="ToggleExpandResponse"
v-tooltip="{
content: !expandResponse
? $t('expand_response')
: $t('collapse_response'),
}"
class="icon" class="icon"
@click="ToggleExpandResponse" @click="ToggleExpandResponse"
ref="ToggleExpandResponse"
v-if="response.body"
v-tooltip="{
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
}"
> >
<i class="material-icons"> <i class="material-icons">
{{ !expandResponse ? "unfold_more" : "unfold_less" }} {{ !expandResponse ? "unfold_more" : "unfold_less" }}
@@ -18,31 +20,31 @@
</button> </button>
<button <button
v-if="response.body" v-if="response.body"
class="icon"
@click.prevent="togglePreview"
v-tooltip="{ v-tooltip="{
content: previewEnabled ? $t('hide_preview') : $t('preview_html'), content: previewEnabled ? $t('hide_preview') : $t('preview_html'),
}" }"
class="icon"
@click.prevent="togglePreview"
> >
<i class="material-icons"> <i class="material-icons">
{{ !previewEnabled ? "visibility" : "visibility_off" }} {{ !previewEnabled ? "visibility" : "visibility_off" }}
</i> </i>
</button> </button>
<button <button
v-if="response.body"
ref="downloadResponse"
v-tooltip="$t('download_file')"
class="icon" class="icon"
@click="downloadResponse" @click="downloadResponse"
ref="downloadResponse"
v-if="response.body"
v-tooltip="$t('download_file')"
> >
<i class="material-icons">save_alt</i> <i class="material-icons">save_alt</i>
</button> </button>
<button <button
v-if="response.body"
ref="copyResponse"
v-tooltip="$t('copy_response')"
class="icon" class="icon"
@click="copyResponse" @click="copyResponse"
ref="copyResponse"
v-if="response.body"
v-tooltip="$t('copy_response')"
> >
<i class="material-icons">content_copy</i> <i class="material-icons">content_copy</i>
</button> </button>
@@ -64,9 +66,9 @@
styles="rounded-b-lg" styles="rounded-b-lg"
/> />
<iframe <iframe
ref="previewFrame"
:class="{ hidden: !previewEnabled }" :class="{ hidden: !previewEnabled }"
class="covers-response" class="covers-response"
ref="previewFrame"
src="about:blank" src="about:blank"
></iframe> ></iframe>
</div> </div>
@@ -79,7 +81,7 @@ import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
export default { export default {
mixins: [TextContentRendererMixin], mixins: [TextContentRendererMixin],
props: { props: {
response: {}, response: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {
@@ -94,7 +96,8 @@ export default {
methods: { methods: {
ToggleExpandResponse() { ToggleExpandResponse() {
this.expandResponse = !this.expandResponse this.expandResponse = !this.expandResponse
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity this.responseBodyMaxLines =
this.responseBodyMaxLines === Infinity ? 16 : Infinity
}, },
downloadResponse() { downloadResponse() {
const dataToWrite = this.responseBodyText const dataToWrite = this.responseBodyText
@@ -128,19 +131,30 @@ export default {
aux.select() aux.select()
document.execCommand("copy") document.execCommand("copy")
document.body.removeChild(aux) document.body.removeChild(aux)
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000) setTimeout(
() => (this.$refs.copyResponse.innerHTML = this.copyButton),
1000
)
}, },
togglePreview() { togglePreview() {
this.previewEnabled = !this.previewEnabled this.previewEnabled = !this.previewEnabled
if (this.previewEnabled) { if (this.previewEnabled) {
if (this.$refs.previewFrame.getAttribute("data-previewing-url") === this.url) return if (
this.$refs.previewFrame.getAttribute("data-previewing-url") ===
this.url
)
return
// Use DOMParser to parse document HTML. // Use DOMParser to parse document HTML.
const previewDocument = new DOMParser().parseFromString(this.responseBodyText, "text/html") const previewDocument = new DOMParser().parseFromString(
this.responseBodyText,
"text/html"
)
// Inject <base href="..."> tag to head, to fix relative CSS/HTML paths. // Inject <base href="..."> tag to head, to fix relative CSS/HTML paths.
previewDocument.head.innerHTML = previewDocument.head.innerHTML =
`<base href="${this.url}">` + previewDocument.head.innerHTML `<base href="${this.url}">` + previewDocument.head.innerHTML
// Finally, set the iframe source to the resulting HTML. // Finally, set the iframe source to the resulting HTML.
this.$refs.previewFrame.srcdoc = previewDocument.documentElement.outerHTML this.$refs.previewFrame.srcdoc =
previewDocument.documentElement.outerHTML
this.$refs.previewFrame.setAttribute("data-previewing-url", this.url) this.$refs.previewFrame.setAttribute("data-previewing-url", this.url)
} }
}, },

View File

@@ -4,11 +4,11 @@
<label for="body">{{ $t("response_body") }}</label> <label for="body">{{ $t("response_body") }}</label>
<div> <div>
<button <button
v-if="response.body"
ref="downloadResponse"
v-tooltip="$t('download_file')"
class="icon" class="icon"
@click="downloadResponse" @click="downloadResponse"
ref="downloadResponse"
v-if="response.body"
v-tooltip="$t('download_file')"
> >
<i class="material-icons">save_alt</i> <i class="material-icons">save_alt</i>
</button> </button>
@@ -23,7 +23,7 @@
<script> <script>
export default { export default {
props: { props: {
response: {}, response: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {
@@ -34,13 +34,15 @@ export default {
}, },
computed: { computed: {
responseType() { responseType() {
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase() return (this.response.headers["content-type"] || "")
.split(";")[0]
.toLowerCase()
}, },
}, },
watch: { watch: {
response: { response: {
immediate: true, immediate: true,
handler(newValue) { handler() {
this.imageSource = "" this.imageSource = ""
const buf = this.response.body const buf = this.response.body

View File

@@ -4,46 +4,45 @@
<label for="body">{{ $t("response_body") }}</label> <label for="body">{{ $t("response_body") }}</label>
<div> <div>
<button <button
v-if="response.body"
ref="ToggleExpandResponse"
v-tooltip="{
content: !expandResponse
? $t('expand_response')
: $t('collapse_response'),
}"
class="icon" class="icon"
@click="ToggleExpandResponse" @click="ToggleExpandResponse"
ref="ToggleExpandResponse"
v-if="response.body"
v-tooltip="{
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
}"
> >
<i class="material-icons"> <i class="material-icons">
{{ !expandResponse ? "unfold_more" : "unfold_less" }} {{ !expandResponse ? "unfold_more" : "unfold_less" }}
</i> </i>
</button> </button>
<button <button
v-if="response.body && canDownloadResponse"
ref="downloadResponse"
v-tooltip="$t('download_file')"
class="icon" class="icon"
@click="downloadResponse" @click="downloadResponse"
ref="downloadResponse"
v-if="response.body && canDownloadResponse"
v-tooltip="$t('download_file')"
> >
<i class="material-icons">save_alt</i> <i class="material-icons">save_alt</i>
</button> </button>
<button <button
v-if="response.body"
ref="copyResponse"
v-tooltip="$t('copy_response')"
class="icon" class="icon"
@click="copyResponse" @click="copyResponse"
ref="copyResponse"
v-if="response.body"
v-tooltip="$t('copy_response')"
> >
<i class="material-icons">content_copy</i> <i class="material-icons">content_copy</i>
</button> </button>
</div> </div>
</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"> <div id="response-details-wrapper">
<SmartAceEditor <SmartAceEditor
:value="jsonBodyText" :value="jsonBodyText"
:lang="'json'" :lang="'json'"
:provideJSONOutline="true" :provide-j-s-o-n-outline="true"
:options="{ :options="{
maxLines: responseBodyMaxLines, maxLines: responseBodyMaxLines,
minLines: '16', minLines: '16',
@@ -60,18 +59,17 @@
</template> </template>
<script> <script>
import { isJSONContentType } from "~/helpers/utils/contenttypes"
import TextContentRendererMixin from "./mixins/TextContentRendererMixin" import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
import { isJSONContentType } from "~/helpers/utils/contenttypes"
export default { export default {
mixins: [TextContentRendererMixin], mixins: [TextContentRendererMixin],
props: { props: {
response: {}, response: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {
expandResponse: false, expandResponse: false,
jsonInvalid: false,
responseBodyMaxLines: 16, responseBodyMaxLines: 16,
doneButton: '<i class="material-icons">done</i>', doneButton: '<i class="material-icons">done</i>',
downloadButton: '<i class="material-icons">save_alt</i>', downloadButton: '<i class="material-icons">save_alt</i>',
@@ -81,16 +79,16 @@ export default {
computed: { computed: {
jsonBodyText() { jsonBodyText() {
try { try {
this.jsonInvalid = false
return JSON.stringify(JSON.parse(this.responseBodyText), null, 2) return JSON.stringify(JSON.parse(this.responseBodyText), null, 2)
} catch (e) { } catch (e) {
// Most probs invalid JSON was returned, so drop prettification (should we warn ?) // Most probs invalid JSON was returned, so drop prettification (should we warn ?)
this.jsonInvalid = true
return this.responseBodyText return this.responseBodyText
} }
}, },
responseType() { responseType() {
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase() return (this.response.headers["content-type"] || "")
.split(";")[0]
.toLowerCase()
}, },
canDownloadResponse() { canDownloadResponse() {
return ( return (
@@ -104,7 +102,8 @@ export default {
methods: { methods: {
ToggleExpandResponse() { ToggleExpandResponse() {
this.expandResponse = !this.expandResponse this.expandResponse = !this.expandResponse
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity this.responseBodyMaxLines =
this.responseBodyMaxLines === Infinity ? 16 : Infinity
}, },
downloadResponse() { downloadResponse() {
const dataToWrite = this.responseBodyText const dataToWrite = this.responseBodyText
@@ -138,7 +137,10 @@ export default {
aux.select() aux.select()
document.execCommand("copy") document.execCommand("copy")
document.body.removeChild(aux) document.body.removeChild(aux)
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000) setTimeout(
() => (this.$refs.copyResponse.innerHTML = this.copyButton),
1000
)
}, },
}, },
} }

View File

@@ -4,33 +4,35 @@
<label for="body">{{ $t("response_body") }}</label> <label for="body">{{ $t("response_body") }}</label>
<div> <div>
<button <button
v-if="response.body"
ref="ToggleExpandResponse"
v-tooltip="{
content: !expandResponse
? $t('expand_response')
: $t('collapse_response'),
}"
class="icon" class="icon"
@click="ToggleExpandResponse" @click="ToggleExpandResponse"
ref="ToggleExpandResponse"
v-if="response.body"
v-tooltip="{
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
}"
> >
<i class="material-icons"> <i class="material-icons">
{{ !expandResponse ? "unfold_more" : "unfold_less" }} {{ !expandResponse ? "unfold_more" : "unfold_less" }}
</i> </i>
</button> </button>
<button <button
v-if="response.body && canDownloadResponse"
ref="downloadResponse"
v-tooltip="$t('download_file')"
class="icon" class="icon"
@click="downloadResponse" @click="downloadResponse"
ref="downloadResponse"
v-if="response.body && canDownloadResponse"
v-tooltip="$t('download_file')"
> >
<i class="material-icons">save_alt</i> <i class="material-icons">save_alt</i>
</button> </button>
<button <button
v-if="response.body"
ref="copyResponse"
v-tooltip="$t('copy_response')"
class="icon" class="icon"
@click="copyResponse" @click="copyResponse"
ref="copyResponse"
v-if="response.body"
v-tooltip="$t('copy_response')"
> >
<i class="material-icons">content_copy</i> <i class="material-icons">content_copy</i>
</button> </button>
@@ -56,13 +58,13 @@
</template> </template>
<script> <script>
import { isJSONContentType } from "~/helpers/utils/contenttypes"
import TextContentRendererMixin from "./mixins/TextContentRendererMixin" import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
import { isJSONContentType } from "~/helpers/utils/contenttypes"
export default { export default {
mixins: [TextContentRendererMixin], mixins: [TextContentRendererMixin],
props: { props: {
response: {}, response: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {
@@ -75,7 +77,9 @@ export default {
}, },
computed: { computed: {
responseType() { responseType() {
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase() return (this.response.headers["content-type"] || "")
.split(";")[0]
.toLowerCase()
}, },
canDownloadResponse() { canDownloadResponse() {
return ( return (
@@ -89,7 +93,8 @@ export default {
methods: { methods: {
ToggleExpandResponse() { ToggleExpandResponse() {
this.expandResponse = !this.expandResponse this.expandResponse = !this.expandResponse
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity this.responseBodyMaxLines =
this.responseBodyMaxLines === Infinity ? 16 : Infinity
}, },
downloadResponse() { downloadResponse() {
const dataToWrite = this.responseBodyText const dataToWrite = this.responseBodyText
@@ -123,7 +128,10 @@ export default {
aux.select() aux.select()
document.execCommand("copy") document.execCommand("copy")
document.body.removeChild(aux) document.body.removeChild(aux)
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000) setTimeout(
() => (this.$refs.copyResponse.innerHTML = this.copyButton),
1000
)
}, },
}, },
} }

View File

@@ -4,33 +4,35 @@
<label for="body">{{ $t("response_body") }}</label> <label for="body">{{ $t("response_body") }}</label>
<div> <div>
<button <button
v-if="response.body"
ref="ToggleExpandResponse"
v-tooltip="{
content: !expandResponse
? $t('expand_response')
: $t('collapse_response'),
}"
class="icon" class="icon"
@click="ToggleExpandResponse" @click="ToggleExpandResponse"
ref="ToggleExpandResponse"
v-if="response.body"
v-tooltip="{
content: !expandResponse ? $t('expand_response') : $t('collapse_response'),
}"
> >
<i class="material-icons"> <i class="material-icons">
{{ !expandResponse ? "unfold_more" : "unfold_less" }} {{ !expandResponse ? "unfold_more" : "unfold_less" }}
</i> </i>
</button> </button>
<button <button
v-if="response.body"
ref="downloadResponse"
v-tooltip="$t('download_file')"
class="icon" class="icon"
@click="downloadResponse" @click="downloadResponse"
ref="downloadResponse"
v-if="response.body"
v-tooltip="$t('download_file')"
> >
<i class="material-icons">save_alt</i> <i class="material-icons">save_alt</i>
</button> </button>
<button <button
v-if="response.body"
ref="copyResponse"
v-tooltip="$t('copy_response')"
class="icon" class="icon"
@click="copyResponse" @click="copyResponse"
ref="copyResponse"
v-if="response.body"
v-tooltip="$t('copy_response')"
> >
<i class="material-icons">content_copy</i> <i class="material-icons">content_copy</i>
</button> </button>
@@ -61,7 +63,7 @@ import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
export default { export default {
mixins: [TextContentRendererMixin], mixins: [TextContentRendererMixin],
props: { props: {
response: {}, response: { type: Object, default: () => {} },
}, },
data() { data() {
return { return {
@@ -74,13 +76,16 @@ export default {
}, },
computed: { computed: {
responseType() { responseType() {
return (this.response.headers["content-type"] || "").split(";")[0].toLowerCase() return (this.response.headers["content-type"] || "")
.split(";")[0]
.toLowerCase()
}, },
}, },
methods: { methods: {
ToggleExpandResponse() { ToggleExpandResponse() {
this.expandResponse = !this.expandResponse this.expandResponse = !this.expandResponse
this.responseBodyMaxLines = this.responseBodyMaxLines == Infinity ? 16 : Infinity this.responseBodyMaxLines =
this.responseBodyMaxLines === Infinity ? 16 : Infinity
}, },
downloadResponse() { downloadResponse() {
const dataToWrite = this.responseBodyText const dataToWrite = this.responseBodyText
@@ -114,7 +119,10 @@ export default {
aux.select() aux.select()
document.execCommand("copy") document.execCommand("copy")
document.body.removeChild(aux) document.body.removeChild(aux)
setTimeout(() => (this.$refs.copyResponse.innerHTML = this.copyButton), 1000) setTimeout(
() => (this.$refs.copyResponse.innerHTML = this.copyButton),
1000
)
}, },
}, },
} }

View File

@@ -1,10 +1,14 @@
<template> <template>
<div class="flex flex-col"> <div class="flex flex-col">
<label for="log">{{ title }}</label> <label for="log">{{ title }}</label>
<div name="log" class="realtime-log" ref="log"> <div ref="log" name="log" class="realtime-log">
<span v-if="log"> <span v-if="log">
<span v-for="(logEntry, index) in log" :style="{ color: logEntry.color }" :key="index" <span
>@ {{ logEntry.ts }}{{ getSourcePrefix(logEntry.source) }}{{ logEntry.payload }}</span v-for="(logEntry, index) in log"
:key="index"
:style="{ color: logEntry.color }"
>@ {{ logEntry.ts }}{{ getSourcePrefix(logEntry.source)
}}{{ logEntry.payload }}</span
> >
</span> </span>
<span v-else>{{ $t("waiting_for_connection") }}</span> <span v-else>{{ $t("waiting_for_connection") }}</span>
@@ -12,6 +16,30 @@
</div> </div>
</template> </template>
<script>
import { getSourcePrefix } from "~/helpers/utils/string"
export default {
props: {
log: { type: Array, default: () => [] },
title: {
type: String,
default: "",
},
},
updated() {
this.$nextTick(function () {
if (this.$refs.log) {
this.$refs.log.scrollBy(0, this.$refs.log.scrollHeight + 100)
}
})
},
methods: {
getSourcePrefix,
},
}
</script>
<style scoped lang="scss"> <style scoped lang="scss">
.realtime-log { .realtime-log {
@apply p-4; @apply p-4;
@@ -35,21 +63,3 @@
} }
} }
</style> </style>
<script>
import { getSourcePrefix } from "~/helpers/utils/string"
export default {
props: ["log", "title"],
methods: {
getSourcePrefix,
},
updated() {
this.$nextTick(function () {
if (this.$refs.log) {
this.$refs.log.scrollBy(0, this.$refs.log.scrollHeight + 100)
}
})
},
}
</script>

View File

@@ -6,8 +6,8 @@
<label for="mqtt-url">{{ $t("url") }}</label> <label for="mqtt-url">{{ $t("url") }}</label>
<input <input
id="mqtt-url" id="mqtt-url"
type="url"
v-model="url" v-model="url"
type="url"
spellcheck="false" spellcheck="false"
class="md:rounded-bl-lg" class="md:rounded-bl-lg"
:placeholder="$t('url')" :placeholder="$t('url')"
@@ -19,12 +19,14 @@
<button <button
id="connect" id="connect"
:disabled="!validUrl" :disabled="!validUrl"
@click="toggleConnection"
class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg" class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg"
@click="toggleConnection"
> >
{{ this.connectionState ? $t("disconnect") : $t("connect") }} {{ connectionState ? $t("disconnect") : $t("connect") }}
<span> <span>
<i class="material-icons">{{ !connectionState ? "sync" : "sync_disabled" }}</i> <i class="material-icons">{{
!connectionState ? "sync" : "sync_disabled"
}}</i>
</span> </span>
</button> </button>
</li> </li>
@@ -35,20 +37,25 @@
<AppSection :label="$t('communication')" no-legend> <AppSection :label="$t('communication')" no-legend>
<ul> <ul>
<li> <li>
<RealtimeLog :title="$t('log')" :log="this.log" /> <RealtimeLog :title="$t('log')" :log="log" />
</li> </li>
</ul> </ul>
<ul> <ul>
<li> <li>
<label for="pub_topic">{{ $t("mqtt_topic") }}</label> <label for="pub_topic">{{ $t("mqtt_topic") }}</label>
<input id="pub_topic" type="text" v-model="pub_topic" spellcheck="false" /> <input
id="pub_topic"
v-model="pub_topic"
type="text"
spellcheck="false"
/>
</li> </li>
<li> <li>
<label for="mqtt-message">{{ $t("message") }}</label> <label for="mqtt-message">{{ $t("message") }}</label>
<input <input
id="mqtt-message" id="mqtt-message"
type="text"
v-model="msg" v-model="msg"
type="text"
spellcheck="false" spellcheck="false"
class="border-dashed md:border-l border-brdColor" class="border-dashed md:border-l border-brdColor"
/> />
@@ -56,7 +63,12 @@
<div> <div>
<li> <li>
<label for="publish" class="hide-on-small-screen">&nbsp;</label> <label for="publish" class="hide-on-small-screen">&nbsp;</label>
<button id="publish" name="get" :disabled="!canpublish" @click="publish"> <button
id="publish"
name="get"
:disabled="!canpublish"
@click="publish"
>
{{ $t("mqtt_publish") }} {{ $t("mqtt_publish") }}
<span> <span>
<i class="material-icons">send</i> <i class="material-icons">send</i>
@@ -70,8 +82,8 @@
<label for="sub_topic">{{ $t("mqtt_topic") }}</label> <label for="sub_topic">{{ $t("mqtt_topic") }}</label>
<input <input
id="sub_topic" id="sub_topic"
type="text"
v-model="sub_topic" v-model="sub_topic"
type="text"
spellcheck="false" spellcheck="false"
class="md:rounded-bl-lg" class="md:rounded-bl-lg"
/> />
@@ -83,12 +95,18 @@
id="subscribe" id="subscribe"
name="get" name="get"
:disabled="!cansubscribe" :disabled="!cansubscribe"
@click="toggleSubscription"
class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg" class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg"
@click="toggleSubscription"
> >
{{ subscriptionState ? $t("mqtt_unsubscribe") : $t("mqtt_subscribe") }} {{
subscriptionState
? $t("mqtt_unsubscribe")
: $t("mqtt_subscribe")
}}
<span> <span>
<i class="material-icons">{{ subscriptionState ? "sync_disabled" : "sync" }}</i> <i class="material-icons">{{
subscriptionState ? "sync_disabled" : "sync"
}}</i>
</span> </span>
</button> </button>
</li> </li>
@@ -117,6 +135,22 @@ export default {
subscriptionState: false, subscriptionState: false,
} }
}, },
computed: {
validUrl() {
return this.isUrlValid
},
canpublish() {
return this.pub_topic !== "" && this.msg !== "" && this.connectionState
},
cansubscribe() {
return this.sub_topic !== "" && this.connectionState
},
},
watch: {
url() {
this.debouncer()
},
},
mounted() { mounted() {
if (process.browser) { if (process.browser) {
this.worker = this.$worker.createRejexWorker() this.worker = this.$worker.createRejexWorker()
@@ -126,22 +160,6 @@ export default {
destroyed() { destroyed() {
this.worker.terminate() this.worker.terminate()
}, },
watch: {
url(val) {
this.debouncer()
},
},
computed: {
validUrl() {
return this.isUrlValid
},
canpublish() {
return this.pub_topic != "" && this.msg != "" && this.connectionState
},
cansubscribe() {
return this.sub_topic != "" && this.connectionState
},
},
methods: { methods: {
debouncer: debounce(function () { debouncer: debounce(function () {
this.worker.postMessage({ type: "ws", url: this.url }) this.worker.postMessage({ type: "ws", url: this.url })
@@ -158,10 +176,10 @@ export default {
ts: new Date().toLocaleTimeString(), ts: new Date().toLocaleTimeString(),
}, },
] ]
let parseUrl = new URL(this.url) const parseUrl = new URL(this.url)
this.client = new Paho.Client( this.client = new Paho.Client(
parseUrl.hostname, parseUrl.hostname,
parseUrl.port != "" ? Number(parseUrl.port) : 8081, parseUrl.port !== "" ? Number(parseUrl.port) : 8081,
"postwoman" "postwoman"
) )
this.client.connect({ this.client.connect({
@@ -267,7 +285,9 @@ export default {
}) })
} catch (e) { } catch (e) {
this.log.push({ this.log.push({
payload: this.$t("error_occurred") + `while subscribing to topic: ${this.sub_topic}`, payload:
this.$t("error_occurred") +
`while subscribing to topic: ${this.sub_topic}`,
source: "info", source: "info",
color: "#ff5555", color: "#ff5555",
ts: new Date().toLocaleTimeString(), ts: new Date().toLocaleTimeString(),

View File

@@ -1,28 +1,28 @@
<template> <template>
<div class="page"> <div class="page">
<AppSection :label="$t('request')" ref="request" no-legend> <AppSection ref="request" :label="$t('request')" no-legend>
<ul> <ul>
<li> <li>
<label for="server">{{ $t("server") }}</label> <label for="server">{{ $t("server") }}</label>
<input <input
id="server" id="server"
v-model="server"
type="url" type="url"
:class="{ error: !serverValid }" :class="{ error: !serverValid }"
v-model="server"
@keyup.enter="serverValid ? toggleSSEConnection() : null"
class="md:rounded-bl-lg" class="md:rounded-bl-lg"
:placeholder="$t('url')" :placeholder="$t('url')"
@keyup.enter="serverValid ? toggleSSEConnection() : null"
/> />
</li> </li>
<div> <div>
<li> <li>
<label for="start" class="hide-on-small-screen">&nbsp;</label> <label for="start" class="hide-on-small-screen">&nbsp;</label>
<button <button
:disabled="!serverValid"
id="start" id="start"
:disabled="!serverValid"
name="start" name="start"
@click="toggleSSEConnection"
class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg" class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg"
@click="toggleSSEConnection"
> >
{{ !connectionSSEState ? $t("start") : $t("stop") }} {{ !connectionSSEState ? $t("start") : $t("stop") }}
<span> <span>
@@ -36,7 +36,12 @@
</ul> </ul>
</AppSection> </AppSection>
<AppSection :label="$t('communication')" id="response" ref="response" no-legend> <AppSection
id="response"
ref="response"
:label="$t('communication')"
no-legend
>
<ul> <ul>
<li> <li>
<RealtimeLog :title="$t('events')" :log="events.log" /> <RealtimeLog :title="$t('events')" :log="events.log" />
@@ -63,8 +68,13 @@ export default {
}, },
} }
}, },
computed: {
serverValid() {
return this.isUrlValid
},
},
watch: { watch: {
server(val) { server() {
this.debouncer() this.debouncer()
}, },
}, },
@@ -77,11 +87,6 @@ export default {
destroyed() { destroyed() {
this.worker.terminate() this.worker.terminate()
}, },
computed: {
serverValid() {
return this.isUrlValid
},
},
methods: { methods: {
debouncer: debounce(function () { debouncer: debounce(function () {
this.worker.postMessage({ type: "sse", url: this.server }) this.worker.postMessage({ type: "sse", url: this.server })
@@ -106,7 +111,7 @@ export default {
if (typeof EventSource !== "undefined") { if (typeof EventSource !== "undefined") {
try { try {
this.sse = new EventSource(this.server) this.sse = new EventSource(this.server)
this.sse.onopen = (event) => { this.sse.onopen = () => {
this.connectionSSEState = true this.connectionSSEState = true
this.events.log = [ this.events.log = [
{ {
@@ -120,10 +125,10 @@ export default {
icon: "sync", icon: "sync",
}) })
} }
this.sse.onerror = (event) => { this.sse.onerror = () => {
this.handleSSEError() this.handleSSEError()
} }
this.sse.onclose = (event) => { this.sse.onclose = () => {
this.connectionSSEState = false this.connectionSSEState = false
this.events.log.push({ this.events.log.push({
payload: this.$t("disconnected_from", { name: this.server }), payload: this.$t("disconnected_from", { name: this.server }),

View File

@@ -1,29 +1,29 @@
<template> <template>
<div class="page"> <div class="page">
<AppSection :label="$t('request')" ref="request" no-legend> <AppSection ref="request" :label="$t('request')" no-legend>
<ul> <ul>
<li> <li>
<label for="websocket-url">{{ $t("url") }}</label> <label for="websocket-url">{{ $t("url") }}</label>
<input <input
id="websocket-url" id="websocket-url"
v-model="url"
type="url" type="url"
spellcheck="false" spellcheck="false"
:class="{ error: !urlValid }" :class="{ error: !urlValid }"
v-model="url"
@keyup.enter="urlValid ? toggleConnection() : null"
class="md:rounded-bl-lg" class="md:rounded-bl-lg"
:placeholder="$t('url')" :placeholder="$t('url')"
@keyup.enter="urlValid ? toggleConnection() : null"
/> />
</li> </li>
<div> <div>
<li> <li>
<label for="connect" class="hide-on-small-screen">&nbsp;</label> <label for="connect" class="hide-on-small-screen">&nbsp;</label>
<button <button
:disabled="!urlValid"
id="connect" id="connect"
:disabled="!urlValid"
name="connect" name="connect"
@click="toggleConnection"
class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg" class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg"
@click="toggleConnection"
> >
{{ !connectionState ? $t("connect") : $t("disconnect") }} {{ !connectionState ? $t("connect") : $t("disconnect") }}
<span> <span>
@@ -37,7 +37,12 @@
</ul> </ul>
</AppSection> </AppSection>
<AppSection :label="$t('communication')" id="response" ref="response" no-legend> <AppSection
id="response"
ref="response"
:label="$t('communication')"
no-legend
>
<ul> <ul>
<li> <li>
<RealtimeLog :title="$t('log')" :log="communication.log" /> <RealtimeLog :title="$t('log')" :log="communication.log" />
@@ -48,14 +53,14 @@
<label for="websocket-message">{{ $t("message") }}</label> <label for="websocket-message">{{ $t("message") }}</label>
<input <input
id="websocket-message" id="websocket-message"
v-model="communication.input"
name="message" name="message"
type="text" type="text"
v-model="communication.input"
:readonly="!connectionState" :readonly="!connectionState"
class="md:rounded-bl-lg"
@keyup.enter="connectionState ? sendMessage() : null" @keyup.enter="connectionState ? sendMessage() : null"
@keyup.up="connectionState ? walkHistory('up') : null" @keyup.up="connectionState ? walkHistory('up') : null"
@keyup.down="connectionState ? walkHistory('down') : null" @keyup.down="connectionState ? walkHistory('down') : null"
class="md:rounded-bl-lg"
/> />
</li> </li>
<div> <div>
@@ -65,8 +70,8 @@
id="send" id="send"
name="send" name="send"
:disabled="!connectionState" :disabled="!connectionState"
@click="sendMessage"
class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg" class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg"
@click="sendMessage"
> >
{{ $t("send") }} {{ $t("send") }}
<span> <span>
@@ -94,9 +99,19 @@ export default {
log: null, log: null,
input: "", input: "",
}, },
currentIndex: -1, //index of the message log array to put in input box currentIndex: -1, // index of the message log array to put in input box
} }
}, },
computed: {
urlValid() {
return this.isUrlValid
},
},
watch: {
url() {
this.debouncer()
},
},
mounted() { mounted() {
if (process.browser) { if (process.browser) {
this.worker = this.$worker.createRejexWorker() this.worker = this.$worker.createRejexWorker()
@@ -106,16 +121,6 @@ export default {
destroyed() { destroyed() {
this.worker.terminate() this.worker.terminate()
}, },
computed: {
urlValid() {
return this.isUrlValid
},
},
watch: {
url(val) {
this.debouncer()
},
},
methods: { methods: {
debouncer: debounce(function () { debouncer: debounce(function () {
this.worker.postMessage({ type: "ws", url: this.url }) this.worker.postMessage({ type: "ws", url: this.url })
@@ -139,7 +144,7 @@ export default {
] ]
try { try {
this.socket = new WebSocket(this.url) this.socket = new WebSocket(this.url)
this.socket.onopen = (event) => { this.socket.onopen = () => {
this.connectionState = true this.connectionState = true
this.communication.log = [ this.communication.log = [
{ {
@@ -153,10 +158,10 @@ export default {
icon: "sync", icon: "sync",
}) })
} }
this.socket.onerror = (event) => { this.socket.onerror = () => {
this.handleError() this.handleError()
} }
this.socket.onclose = (event) => { this.socket.onclose = () => {
this.connectionState = false this.connectionState = false
this.communication.log.push({ this.communication.log.push({
payload: this.$t("disconnected_from", { name: this.url }), payload: this.$t("disconnected_from", { name: this.url }),
@@ -215,20 +220,24 @@ export default {
this.communication.input = "" this.communication.input = ""
}, },
walkHistory(direction) { walkHistory(direction) {
const clientMessages = this.communication.log.filter(({ source }) => source === "client") const clientMessages = this.communication.log.filter(
({ source }) => source === "client"
)
const length = clientMessages.length const length = clientMessages.length
switch (direction) { switch (direction) {
case "up": case "up":
if (length > 0 && this.currentIndex !== 0) { if (length > 0 && this.currentIndex !== 0) {
//does nothing if message log is empty or the currentIndex is 0 when up arrow is pressed // does nothing if message log is empty or the currentIndex is 0 when up arrow is pressed
if (this.currentIndex === -1) { if (this.currentIndex === -1) {
this.currentIndex = length - 1 this.currentIndex = length - 1
this.communication.input = clientMessages[this.currentIndex].payload this.communication.input =
clientMessages[this.currentIndex].payload
} else if (this.currentIndex === 0) { } else if (this.currentIndex === 0) {
this.communication.input = clientMessages[0].payload this.communication.input = clientMessages[0].payload
} else if (this.currentIndex > 0) { } else if (this.currentIndex > 0) {
this.currentIndex = this.currentIndex - 1 this.currentIndex = this.currentIndex - 1
this.communication.input = clientMessages[this.currentIndex].payload this.communication.input =
clientMessages[this.currentIndex].payload
} }
} }
break break
@@ -239,7 +248,8 @@ export default {
this.communication.input = "" this.communication.input = ""
} else if (this.currentIndex < length - 1) { } else if (this.currentIndex < length - 1) {
this.currentIndex = this.currentIndex + 1 this.currentIndex = this.currentIndex + 1
this.communication.input = clientMessages[this.currentIndex].payload this.communication.input =
clientMessages[this.currentIndex].payload
} }
} }
break break