From 0b00842c501127927d3db8a714f6c0ba3fc31216 Mon Sep 17 00:00:00 2001 From: Osheen Sachdev <45964755+oshhh@users.noreply.github.com> Date: Thu, 11 Mar 2021 08:42:09 +0530 Subject: [PATCH] [Feat: GraphQL sidebar] GraphQL History (#1528) * Create REQUIREMENTS.md * graphql history UI * rest history emit * removed requirements file * add, delete, clear, star history and sync with firstore * use history * empty schema * remove other tabs * computed query, setting headers * binding props Co-authored-by: Liyas Thomas * remove print Co-authored-by: Liyas Thomas * remove dummy data Co-authored-by: Liyas Thomas * add docs tab * date, time attribute --> updatedOn * Removed margin from sidebar Tab * removed v-bind Co-authored-by: Liyas Thomas * use shortcut for v-bind * use shortcut for v-bind * use unfold icons * use template literals in :key * history clear bug * delete history bug * minor translation * remove console logs * remove unused css * add stared style * remove absolute styles * tests for graphql card * tests for rest card * tests for clear history added * mount, clear, use, delete history tests added * Rename card.vue to Card.vue * Rename card.vue to Card.vue * use computed Co-authored-by: Isha Gupta <40794215+IshaGupta18@users.noreply.github.com> Co-authored-by: Liyas Thomas --- .../history/__tests__/GraphqlCard.spec.js | 109 +++++++ components/history/__tests__/History.spec.js | 285 ++++++++++++++++++ components/history/__tests__/RestCard.spec.js | 58 ++++ components/history/graphql/Card.vue | 147 +++++++++ .../{http/History.vue => history/index.vue} | 221 ++++---------- components/history/rest/Card.vue | 160 ++++++++++ helpers/fb.js | 61 ++++ package-lock.json | 9 +- pages/graphql.vue | 196 +++++++----- pages/index.vue | 2 +- 10 files changed, 1019 insertions(+), 229 deletions(-) create mode 100644 components/history/__tests__/GraphqlCard.spec.js create mode 100644 components/history/__tests__/History.spec.js create mode 100644 components/history/__tests__/RestCard.spec.js create mode 100644 components/history/graphql/Card.vue rename components/{http/History.vue => history/index.vue} (61%) create mode 100644 components/history/rest/Card.vue diff --git a/components/history/__tests__/GraphqlCard.spec.js b/components/history/__tests__/GraphqlCard.spec.js new file mode 100644 index 000000000..347ab716b --- /dev/null +++ b/components/history/__tests__/GraphqlCard.spec.js @@ -0,0 +1,109 @@ +import GraphqlCard from "../graphql/Card" +import { mount } from "@vue/test-utils" + +const factory = (props) => { + return mount(GraphqlCard, { + propsData: props, + stubs: { + "v-popover": { + template: "
", + }, + }, + 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: url, + query: query, + star: false, + }, + }) + expect(wrapper).toBeTruthy() + }) + + test("toggle-star emitted on clicking on star button", async () => { + const wrapper = factory({ + entry: { + type: "graphql", + url: url, + query: query, + star: true, + }, + }) + + 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: url, + query: 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: url, + query: 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: url, + query: query, + star: true, + }, + }) + await wrapper.find("button[data-testid=delete_history_entry]").trigger("click") + expect(wrapper.emitted("delete-entry")).toBeTruthy() + }) +}) diff --git a/components/history/__tests__/History.spec.js b/components/history/__tests__/History.spec.js new file mode 100644 index 000000000..43d2b57a0 --- /dev/null +++ b/components/history/__tests__/History.spec.js @@ -0,0 +1,285 @@ +import History from "../" +import { fb } from "~/helpers/fb" +import { shallowMount } from "@vue/test-utils" +import HistoryRestCard from "../rest/Card" + +const restHistory = [ + { + id: "0", + type: "rest", + }, + { + id: "1", + type: "rest", + }, + { + id: "2", + type: "rest", + }, +] + +const graphqlHistory = [ + { + id: "0", + type: "graphql", + }, + { + id: "1", + type: "graphql", + }, + { + id: "2", + type: "graphql", + }, +] + +var localStorageMock = (function () { + var store = { + history: JSON.stringify(restHistory), + graphqlHistory: JSON.stringify(graphqlHistory), + } + return { + getItem: function (key) { + return store[key] + }, + setItem: jest.fn(), + clear: jest.fn(), + removeItem: jest.fn(), + } +})() +Object.defineProperty(window, "localStorage", { value: localStorageMock }) + +jest.mock("~/helpers/fb", () => ({ + __esModule: true, + + fb: { + currentUser: null, + currentHistory: restHistory, + currentGraphqlHistory: graphqlHistory, + clearHistory: jest.fn(), + clearGraphqlHistory: jest.fn(), + deleteHistory: jest.fn(), + deleteGraphqlHistory: jest.fn(), + }, +})) + +const factory = (props) => { + return shallowMount(History, { + propsData: props, + stubs: { + "v-popover": { + template: "
", + }, + HistoryRestCard: { + template: "
", + }, + HistoryGraphqlCard: { + template: "
", + }, + AppSection: { + template: "
", + }, + }, + mocks: { + $t: (text) => text, + $toast: { + error() {}, + }, + }, + directives: { + tooltip() { + /* stub */ + }, + closePopover() { + /* stub */ + }, + }, + }) +} + +beforeEach(() => { + fb.clearHistory.mockClear() + fb.clearGraphqlHistory.mockClear() + fb.deleteHistory.mockClear() + fb.deleteGraphqlHistory.mockClear() + window.localStorage.setItem.mockClear() +}) + +describe("Mount History", () => { + test("Mounts rest history without login", async () => { + const wrapper = factory({ + page: "rest", + }) + expect(wrapper).toBeTruthy() + }) + test("Mounts rest history with login", async () => { + fb.currentUser = "user" + const wrapper = factory({ + page: "rest", + }) + expect(wrapper).toBeTruthy() + }) + + test("Mounts graphql history without login", async () => { + const wrapper = factory({ + page: "rest", + }) + expect(wrapper).toBeTruthy() + }) + test("Mounts graphql history with login", async () => { + fb.currentUser = "user" + const wrapper = factory({ + page: "rest", + }) + expect(wrapper).toBeTruthy() + }) +}) + +describe("Clear History", () => { + test("Clear rest history without login", async () => { + fb.currentUser = null + const wrapper = factory({ + page: "rest", + }) + expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory) + await wrapper.find("button[data-testid='clear_history']").trigger("click") + await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click") + expect(fb.clearHistory).not.toHaveBeenCalled() + expect(window.localStorage.setItem).toHaveBeenCalledWith("history", JSON.stringify([])) + }) + test("Clear rest history with login", async () => { + fb.currentUser = "user" + const wrapper = factory({ + page: "rest", + }) + expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory) + await wrapper.find("button[data-testid='clear_history']").trigger("click") + await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click") + expect(fb.clearHistory).toHaveBeenCalledTimes(1) + expect(window.localStorage.setItem).toHaveBeenCalledWith("history", JSON.stringify([])) + }) + test("Dont confirm Clear rest history", async () => { + fb.currentUser = "user" + const wrapper = factory({ + page: "rest", + }) + expect(wrapper.vm.filteredHistory).toStrictEqual(restHistory) + await wrapper.find("button[data-testid='clear_history']").trigger("click") + await wrapper.find("button[data-testid='reject_clear_history']").trigger("click") + expect(fb.clearHistory).not.toHaveBeenCalled() + expect(window.localStorage.setItem).not.toHaveBeenCalledWith("history", JSON.stringify([])) + }) + + test("Clear graphql history without login", async () => { + fb.currentUser = null + const wrapper = factory({ + page: "graphql", + }) + expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory) + await wrapper.find("button[data-testid='clear_history']").trigger("click") + await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click") + expect(fb.clearGraphqlHistory).not.toHaveBeenCalled() + expect(window.localStorage.setItem).toHaveBeenCalledWith("graphqlHistory", JSON.stringify([])) + }) + test("Clear graphql history with login", async () => { + fb.currentUser = "user" + const wrapper = factory({ + page: "graphql", + }) + expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory) + await wrapper.find("button[data-testid='clear_history']").trigger("click") + await wrapper.find("button[data-testid='confirm_clear_history']").trigger("click") + expect(fb.clearGraphqlHistory).toHaveBeenCalledTimes(1) + expect(window.localStorage.setItem).toHaveBeenCalledWith("graphqlHistory", JSON.stringify([])) + }) + test("Dont confirm Clear graphql history", async () => { + fb.currentUser = "user" + const wrapper = factory({ + page: "graphql", + }) + expect(wrapper.vm.filteredHistory).toStrictEqual(graphqlHistory) + await wrapper.find("button[data-testid='clear_history']").trigger("click") + await wrapper.find("button[data-testid='reject_clear_history']").trigger("click") + expect(window.localStorage.setItem).not.toHaveBeenCalledWith( + "graphqlHistory", + JSON.stringify([]) + ) + }) +}) + +describe("Use History", () => { + test("use rest history", async () => { + fb.currentUser = "user" + const wrapper = factory({ + page: "rest", + }) + expect(wrapper.findAll("div[data-testid='rest_card']").length).toEqual(restHistory.length) + var 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")[0]).toStrictEqual([restHistory[index]]) + }) + + test("use graphql history", async () => { + fb.currentUser = "user" + const wrapper = factory({ + page: "graphql", + }) + expect(wrapper.findAll("div[data-testid='graphql_card']").length).toEqual(graphqlHistory.length) + var 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")[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)) + ) + }) +}) diff --git a/components/history/__tests__/RestCard.spec.js b/components/history/__tests__/RestCard.spec.js new file mode 100644 index 000000000..97e1f0fdc --- /dev/null +++ b/components/history/__tests__/RestCard.spec.js @@ -0,0 +1,58 @@ +import RestCard from "../rest/Card" +import { mount } from "@vue/test-utils" + +const factory = (props) => { + return mount(RestCard, { + propsData: props, + stubs: { + "v-popover": { + template: "
", + }, + }, + mocks: { + $t: (text) => text, + }, + directives: { + tooltip() { + /* stub */ + }, + closePopover() { + /* stub */ + }, + }, + }) +} + +const url = "https://dummydata.com/get" +const entry = { + type: "rest", + url: 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 }) + + 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() + }) +}) diff --git a/components/history/graphql/Card.vue b/components/history/graphql/Card.vue new file mode 100644 index 000000000..7a5302c71 --- /dev/null +++ b/components/history/graphql/Card.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/components/http/History.vue b/components/history/index.vue similarity index 61% rename from components/http/History.vue rename to components/history/index.vue index ee87ab1a8..9a77e5954 100644 --- a/components/http/History.vue +++ b/components/history/index.vue @@ -13,127 +13,24 @@ class="divide-y virtual-list divide-dashed divide-brdColor" :class="{ filled: filteredHistory.length }" > -
    -
    - - {{ `${entry.method} \xA0 • \xA0 ${entry.status}` }} - -
  • - -
  • - - - - - - -
    -
    -
  • - -
  • -
    - -
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
    -
    +
      + +

@@ -144,12 +41,17 @@

- -