From 65a60d0bab90b5d7b5dcbd598de7113e64d71e30 Mon Sep 17 00:00:00 2001 From: Mason Date: Wed, 16 Dec 2020 20:54:07 -0800 Subject: [PATCH 01/12] removed babel rewire, removed global Vue in jest tests, removed jest helper which accessed document --- client/.babelrc | 2 +- client/README.md | 3 +- client/package.json | 1 - .../components/DataDialog/DataDialog.test.js | 4 +- .../components/Dataset/DatasetName.test.js | 4 -- .../InteractiveTools/InteractiveTools.test.js | 2 - .../Markdown/MarkdownVisualization.test.js | 5 +- .../src/components/Masthead/Masthead.test.js | 16 +++-- .../components/Masthead/MastheadItem.test.js | 8 +-- .../src/components/Panels/Common/Tool.test.js | 4 -- .../Panels/Common/ToolSection.test.js | 23 +++---- .../RuleBuilder/SavedRulesSelector.test.js | 9 +-- .../src/components/Tags/StatelessTags.test.js | 3 +- .../ToolsSchemaJson/ToolsJson.test.js | 5 +- .../components/ToolsView/ToolsView.test.js | 2 + .../Toolshed/InstalledList/Details.test.js | 42 +++++++------ .../Toolshed/InstalledList/Index.test.js | 62 +++++++++--------- .../Toolshed/InstalledList/Monitor.test.js | 63 ++++++++++--------- .../Toolshed/RepositoryDetails/Index.test.js | 59 ++++++++--------- .../InstallationSettings.test.js | 11 +--- .../RepositoryDetails/RepositoryTools.test.js | 5 +- .../Toolshed/SearchList/Categories.test.js | 51 ++++++++------- .../Toolshed/SearchList/Repositories.test.js | 59 ++++++++--------- .../User/CloudAuth/CloudAuth.test.js | 4 +- .../Workflow/Editor/Attributes.test.js | 31 ++++----- .../components/Workflow/Editor/Node.test.js | 2 - .../Workflow/Editor/ZoomControl.test.js | 14 ++--- client/src/components/admin/BaseList.test.js | 3 +- client/tests/karma/webpack.config.unittest.js | 10 +-- client/yarn.lock | 5 -- 30 files changed, 247 insertions(+), 265 deletions(-) diff --git a/client/.babelrc b/client/.babelrc index d92643a9ca4..9297a9920f7 100644 --- a/client/.babelrc +++ b/client/.babelrc @@ -2,7 +2,7 @@ "presets": ["@babel/preset-env"], "env": { "test": { - "plugins": ["@babel/plugin-transform-runtime", "rewire"] + "plugins": ["@babel/plugin-transform-runtime"] } } } diff --git a/client/README.md b/client/README.md index bde2b380c2d..e5bee828d32 100644 --- a/client/README.md +++ b/client/README.md @@ -115,8 +115,7 @@ framework. For testing Vue components, we use the [Vue testing utils](https://vue-test-utils.vuejs.org/) to mount individual components in a test bed and check them for rendered features. Please use jest-based mocking -for isolating test functionality. Some tests exist using Rewire, but those -should be updated over time. +for isolating test functionality. A set of older qUnit tests also exist which will be phased-out as the code they support is replaced with modern component-based implementations. In the diff --git a/client/package.json b/client/package.json index 6b615eec7c8..c3c94aa8e15 100644 --- a/client/package.json +++ b/client/package.json @@ -120,7 +120,6 @@ "babel-eslint": "^10.1.0", "babel-jest": "^26.3.0", "babel-loader": "^8.1.0", - "babel-plugin-rewire": "^1.2.0", "babel-plugin-transform-inline-environment-variables": "^0.4.3", "babel-plugin-transform-vue-template": "^0.4.2", "chai": "^4.2.0", diff --git a/client/src/components/DataDialog/DataDialog.test.js b/client/src/components/DataDialog/DataDialog.test.js index fde6c42cfcc..3e5c151486c 100644 --- a/client/src/components/DataDialog/DataDialog.test.js +++ b/client/src/components/DataDialog/DataDialog.test.js @@ -4,7 +4,6 @@ import { Model } from "./model"; import { UrlTracker } from "./utilities"; import { Services } from "./services"; import { shallowMount, createLocalVue } from "@vue/test-utils"; -import { getNewAttachNode } from "jest/helpers"; jest.mock("app"); @@ -132,7 +131,6 @@ describe("DataDialog.vue", () => { const localVue = createLocalVue(); wrapper = shallowMount(DataDialog, { propsData: mockOptions, - attachTo: getNewAttachNode(), localVue, }); expect(wrapper.findComponent(SelectionDialog).exists()).toBe(true); @@ -145,7 +143,7 @@ describe("DataDialog.vue", () => { // expect(wrapper.find(".fa-spinner").text()).to.equals(""); // expect(wrapper.contains(".fa-spinner")).to.equals(true); - // await Vue.nextTick(); + // await wrapper.vm.$nextTick(); // expect(wrapper.findAll(".fa-folder").length).to.equals(2); // expect(wrapper.findAll(".fa-file-o").length).to.equals(2); diff --git a/client/src/components/Dataset/DatasetName.test.js b/client/src/components/Dataset/DatasetName.test.js index bae0f144311..e42311d5206 100644 --- a/client/src/components/Dataset/DatasetName.test.js +++ b/client/src/components/Dataset/DatasetName.test.js @@ -1,12 +1,10 @@ import { mount } from "@vue/test-utils"; import DatasetName from "./DatasetName"; -import { getNewAttachNode } from "jest/helpers"; describe("Dataset Name", () => { it("test dataset default", async () => { const wrapper = mount(DatasetName, { propsData: { item: { name: "name", state: "success" } }, - attachTo: getNewAttachNode(), }); const state = wrapper.findAll(".name"); expect(state.length).toBe(1); @@ -21,7 +19,6 @@ describe("Dataset Name", () => { it("test dataset error", async () => { const wrapper = mount(DatasetName, { propsData: { item: { name: "name", state: "error" } }, - attachTo: getNewAttachNode(), }); const state = wrapper.findAll(".name"); expect(state.length).toBe(1); @@ -33,7 +30,6 @@ describe("Dataset Name", () => { it("test dataset paused", async () => { const wrapper = mount(DatasetName, { propsData: { item: { name: "name", state: "paused" } }, - attachTo: getNewAttachNode(), }); const state = wrapper.findAll(".name"); expect(state.length).toBe(1); diff --git a/client/src/components/InteractiveTools/InteractiveTools.test.js b/client/src/components/InteractiveTools/InteractiveTools.test.js index 82aaf7ff2db..64df3fb284f 100644 --- a/client/src/components/InteractiveTools/InteractiveTools.test.js +++ b/client/src/components/InteractiveTools/InteractiveTools.test.js @@ -6,7 +6,6 @@ import testInteractiveToolsResponse from "./testData/testInteractiveToolsRespons import MockAdapter from "axios-mock-adapter"; import axios from "axios"; -import { getNewAttachNode } from "jest/helpers"; describe("InteractiveTools/InteractiveTools.vue", () => { const localVue = createLocalVue(); @@ -24,7 +23,6 @@ describe("InteractiveTools/InteractiveTools.vue", () => { }; }, }, - attachTo: getNewAttachNode(), }); axiosMock.onGet("/api/entry_points?running=true").reply(200, testInteractiveToolsResponse); axiosMock.onPost("/interactivetool/list").reply(200, { status: "ok", message: "ok" }); diff --git a/client/src/components/Markdown/MarkdownVisualization.test.js b/client/src/components/Markdown/MarkdownVisualization.test.js index 76d583688df..a337c70bedc 100644 --- a/client/src/components/Markdown/MarkdownVisualization.test.js +++ b/client/src/components/Markdown/MarkdownVisualization.test.js @@ -1,6 +1,5 @@ import MarkdownVisualization from "./MarkdownVisualization"; import { shallowMount } from "@vue/test-utils"; -import Vue from "vue"; describe("Markdown/MarkdownVisualization", () => { it("test wizard", async () => { @@ -16,7 +15,7 @@ describe("Markdown/MarkdownVisualization", () => { useLabels: false, }, }); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.vm.labelShow).toBe(false); expect(Object.keys(wrapper.vm.formInputs).length).toBe(3); wrapper.vm.onData("history_dataset_id"); @@ -35,7 +34,7 @@ describe("Markdown/MarkdownVisualization", () => { useLabels: true, }, }); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.vm.labelShow).toBe(true); expect(wrapper.vm.formInputs).toBe(null); }); diff --git a/client/src/components/Masthead/Masthead.test.js b/client/src/components/Masthead/Masthead.test.js index dc96ca0b60b..6e30338adf0 100644 --- a/client/src/components/Masthead/Masthead.test.js +++ b/client/src/components/Masthead/Masthead.test.js @@ -1,7 +1,12 @@ -import { default as Masthead, __RewireAPI__ as rewire } from "./Masthead.vue"; +import { default as Masthead } from "./Masthead.vue"; import { mount, createLocalVue } from "@vue/test-utils"; import Scratchbook from "layout/scratchbook"; -import { getNewAttachNode } from "jest/helpers"; +import { fetchMenu } from "layout/menu"; +import { loadWebhookMenuItems } from "./_webhooks"; + +jest.mock("app"); +jest.mock("layout/menu"); +jest.mock("./_webhooks"); describe("Masthead.vue", () => { let wrapper; @@ -24,10 +29,10 @@ describe("Masthead.vue", () => { }); } - beforeEach(() => { - rewire.__Rewire__("fetchMenu", stubFetchMenu); - rewire.__Rewire__("loadWebhookMenuItems", stubLoadWebhooks); + fetchMenu.mockImplementation(stubFetchMenu); + loadWebhookMenuItems.mockImplementation(stubLoadWebhooks); + beforeEach(() => { localVue = createLocalVue(); quotaRendered = false; quotaEl = null; @@ -83,7 +88,6 @@ describe("Masthead.vue", () => { appRoot: "prefix/", }, localVue, - attachTo: getNewAttachNode(), }); }); diff --git a/client/src/components/Masthead/MastheadItem.test.js b/client/src/components/Masthead/MastheadItem.test.js index 464fe8e76c8..ccbe0f791aa 100644 --- a/client/src/components/Masthead/MastheadItem.test.js +++ b/client/src/components/Masthead/MastheadItem.test.js @@ -1,8 +1,7 @@ +import { shallowMount, createLocalVue } from "@vue/test-utils"; import MastheadItem from "./MastheadItem.vue"; -import { mount, createLocalVue } from "@vue/test-utils"; -import { getNewAttachNode } from "jest/helpers"; -describe("Masthead.vue", () => { +describe("MastheadItem.vue", () => { let wrapper; let localVue; let active; @@ -18,13 +17,12 @@ describe("Masthead.vue", () => { menu: menu, }; - return mount(MastheadItem, { + return shallowMount(MastheadItem, { propsData: { tab, activeTab: active, }, localVue, - attachTo: getNewAttachNode(), }); } diff --git a/client/src/components/Panels/Common/Tool.test.js b/client/src/components/Panels/Common/Tool.test.js index 5b63f77ed2a..ebc464d42e0 100644 --- a/client/src/components/Panels/Common/Tool.test.js +++ b/client/src/components/Panels/Common/Tool.test.js @@ -1,6 +1,5 @@ import { mount } from "@vue/test-utils"; import Tool from "./Tool"; -import { getNewAttachNode } from "jest/helpers"; describe("Tool", () => { test("test tool", () => { @@ -8,7 +7,6 @@ describe("Tool", () => { propsData: { tool: {}, }, - attachTo: getNewAttachNode(), }); const nameElement = wrapper.findAll(".name"); expect(nameElement.at(0).text()).toBe(""); @@ -30,7 +28,6 @@ describe("Tool", () => { operationIcon: "operationIconClass", operationTitle: "operationTitle", }, - attachTo: getNewAttachNode(), }); const nameElement = wrapper.findAll(".name"); expect(nameElement.at(0).text()).toBe("name"); @@ -48,7 +45,6 @@ describe("Tool", () => { }, hideName: true, }, - attachTo: getNewAttachNode(), }); const nameElement = wrapper.findAll(".name"); expect(nameElement.length).toBe(0); diff --git a/client/src/components/Panels/Common/ToolSection.test.js b/client/src/components/Panels/Common/ToolSection.test.js index ae74bc403fe..aadcd347587 100644 --- a/client/src/components/Panels/Common/ToolSection.test.js +++ b/client/src/components/Panels/Common/ToolSection.test.js @@ -1,7 +1,5 @@ -import Vue from "vue"; import { mount } from "@vue/test-utils"; import ToolSection from "./ToolSection"; -import { getNewAttachNode } from "jest/helpers"; describe("ToolSection", () => { test("test tool section", () => { @@ -11,7 +9,6 @@ describe("ToolSection", () => { name: "name", }, }, - attachTo: getNewAttachNode(), }); const nameElement = wrapper.findAll(".name"); expect(nameElement.at(0).text()).toBe("name"); @@ -34,19 +31,18 @@ describe("ToolSection", () => { ], }, }, - attachTo: getNewAttachNode(), }); expect(wrapper.vm.opened).toBe(false); const $sectionName = wrapper.find(".name"); expect($sectionName.text()).toBe("tool_section"); $sectionName.trigger("click"); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); const $names = wrapper.findAll(".name"); expect($names.at(1).text()).toBe("name"); const $label = wrapper.find(".tool-panel-label"); expect($label.text()).toBe("text"); $sectionName.trigger("click"); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.findAll(".name").length).toBe(1); }); @@ -66,30 +62,29 @@ describe("ToolSection", () => { }, queryFilter: "test", }, - attachTo: getNewAttachNode(), }); expect(wrapper.vm.opened).toBe(true); const $sectionName = wrapper.find(".name"); $sectionName.trigger("click"); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.vm.opened).toBe(false); wrapper.setProps({ queryFilter: "" }); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.vm.opened).toBe(false); wrapper.setProps({ queryFilter: "test" }); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.vm.opened).toBe(true); wrapper.setProps({ disableFilter: true }); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.vm.opened).toBe(true); wrapper.setProps({ queryFilter: "" }); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.vm.opened).toBe(false); $sectionName.trigger("click"); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.vm.opened).toBe(true); wrapper.setProps({ queryFilter: "test" }); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.vm.opened).toBe(false); }); }); diff --git a/client/src/components/RuleBuilder/SavedRulesSelector.test.js b/client/src/components/RuleBuilder/SavedRulesSelector.test.js index 483398804de..e55bd4e372b 100644 --- a/client/src/components/RuleBuilder/SavedRulesSelector.test.js +++ b/client/src/components/RuleBuilder/SavedRulesSelector.test.js @@ -1,7 +1,5 @@ -import Vue from "vue"; import { mount } from "@vue/test-utils"; import SavedRulesSelector from "components/RuleBuilder/SavedRulesSelector"; -import { getNewAttachNode } from "jest/helpers"; describe("SavedRulesSelector", () => { let wrapper; @@ -14,13 +12,12 @@ describe("SavedRulesSelector", () => { prefix: "test_prefix_" + new Date().toISOString() + "_", savedRules: [], }, - attachTo: getNewAttachNode(), }); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); }); afterEach(async () => { - await Vue.nextTick(); + await wrapper.vm.$nextTick(); }); it("disables history icon if there is no history", async () => { @@ -49,7 +46,7 @@ describe("SavedRulesSelector", () => { user: "test_user", savedRules: [testRules], }); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); const sessions = wrapper.findAll("div.dropdown-menu > a.saved-rule-item"); expect(sessions.length > 0).toBeTruthy(); sessions.wrappers[0].trigger("click"); diff --git a/client/src/components/Tags/StatelessTags.test.js b/client/src/components/Tags/StatelessTags.test.js index 35d66d4f4b3..1497b78ebc3 100644 --- a/client/src/components/Tags/StatelessTags.test.js +++ b/client/src/components/Tags/StatelessTags.test.js @@ -1,7 +1,6 @@ import { mount, createLocalVue } from "@vue/test-utils"; import StatelessTags from "./StatelessTags"; import _l from "utils/localization"; -import Vue from "vue"; describe("Tags/StatelessTags.vue", () => { const localVue = createLocalVue(); @@ -17,7 +16,7 @@ describe("Tags/StatelessTags.vue", () => { value: testTags, }); emitted = wrapper.emitted(); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); }); it("should render a div for each tag", () => { diff --git a/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.test.js b/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.test.js index a2cc4ed1de3..a04f76e3769 100644 --- a/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.test.js +++ b/client/src/components/ToolsView/ToolsSchemaJson/ToolsJson.test.js @@ -3,7 +3,6 @@ import ToolsJson from "./ToolsJson"; import testToolsListResponse from "../testData/toolsList"; import MockAdapter from "axios-mock-adapter"; import axios from "axios"; -import Vue from "vue"; import { mount } from "@vue/test-utils"; describe("ToolsView/ToolsView.vue", () => { @@ -15,11 +14,11 @@ describe("ToolsView/ToolsView.vue", () => { axiosMock = new MockAdapter(axios); wrapper = mount(ToolsJson); axiosMock.onGet("/api/tools?tool_help=True").reply(200, testToolsListResponse); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); }); it("schema.org script element is created", async () => { - await Vue.nextTick(); + await wrapper.vm.$nextTick(); const tools = wrapper.vm.createToolsJson(testToolsListResponse); const schemaElement = document.getElementById("schema-json"); const schemaText = JSON.parse(schemaElement.text); diff --git a/client/src/components/ToolsView/ToolsView.test.js b/client/src/components/ToolsView/ToolsView.test.js index fd294f7b7d2..288027a01fd 100644 --- a/client/src/components/ToolsView/ToolsView.test.js +++ b/client/src/components/ToolsView/ToolsView.test.js @@ -9,6 +9,8 @@ import testCitation from "./testData/citation"; import MockAdapter from "axios-mock-adapter"; import axios from "axios"; +jest.mock("app"); + describe("ToolsView/ToolsView.vue", () => { const localVue = createLocalVue(); localVue.filter("localize", (value) => _l(value)); diff --git a/client/src/components/Toolshed/InstalledList/Details.test.js b/client/src/components/Toolshed/InstalledList/Details.test.js index 7cdb8b9b098..f10bc7ce51b 100644 --- a/client/src/components/Toolshed/InstalledList/Details.test.js +++ b/client/src/components/Toolshed/InstalledList/Details.test.js @@ -1,23 +1,28 @@ -import { shallowMount } from "@vue/test-utils"; +import { shallowMount, createLocalVue } from "@vue/test-utils"; import Details from "./Details"; -import { __RewireAPI__ as rewire } from "./Details"; -import Vue from "vue"; + +jest.mock("app"); + +import { getAppRoot } from "onload/loadConfig"; +jest.mock("onload/loadConfig"); +getAppRoot.mockImplementation(() => "/"); + +import { Services } from "../services"; +jest.mock("../services"); + +Services.mockImplementation(() => { + return { + async getRepositoryByName(url, name, owner) { + expect(url).toBe("tool_shed_url"); + expect(name).toBe("name"); + expect(owner).toBe("owner"); + return {}; + }, + }; +}); describe("Details", () => { - beforeEach(() => { - rewire.__Rewire__( - "Services", - class { - async getRepositoryByName(url, name, owner) { - expect(url).toBe("tool_shed_url"); - expect(name).toBe("name"); - expect(owner).toBe("owner"); - return {}; - } - } - ); - }); - + const localVue = createLocalVue(); it("test repository details loading", async () => { const wrapper = shallowMount(Details, { propsData: { @@ -27,11 +32,12 @@ describe("Details", () => { owner: "owner", }, }, + localVue, }); expect(wrapper.findAll("loading-span-stub").length).toBe(1); expect(wrapper.find("loading-span-stub").attributes("message")).toBe("Loading installed repository details"); expect(wrapper.findAll("repositorydetails-stub").length).toBe(0); - await Vue.nextTick(); + await localVue.nextTick(); expect(wrapper.findAll("loading-span-stub").length).toBe(0); expect(wrapper.findAll(".alert").length).toBe(0); expect(wrapper.findAll("repositorydetails-stub").length).toBe(1); diff --git a/client/src/components/Toolshed/InstalledList/Index.test.js b/client/src/components/Toolshed/InstalledList/Index.test.js index cac755f38a2..85f5285a68d 100644 --- a/client/src/components/Toolshed/InstalledList/Index.test.js +++ b/client/src/components/Toolshed/InstalledList/Index.test.js @@ -1,35 +1,39 @@ import { mount } from "@vue/test-utils"; import Index from "./Index"; -import { __RewireAPI__ as rewire } from "./Index"; -import Vue from "vue"; + +jest.mock("app"); + +import { getAppRoot } from "onload/loadConfig"; +jest.mock("onload/loadConfig"); +getAppRoot.mockImplementation(() => "/"); + +import { Services } from "../services"; +jest.mock("../services"); + +Services.mockImplementation(() => { + return { + async getInstalledRepositories() { + return [ + { + name: "name_0", + description: "description_0", + tool_shed_status: { + latest_installable_revision: false, + }, + }, + { + name: "name_1", + description: "description_1", + tool_shed_status: { + latest_installable_revision: true, + }, + }, + ]; + }, + }; +}); describe("InstalledList", () => { - beforeEach(() => { - rewire.__Rewire__( - "Services", - class { - async getInstalledRepositories() { - return [ - { - name: "name_0", - description: "description_0", - tool_shed_status: { - latest_installable_revision: false, - }, - }, - { - name: "name_1", - description: "description_1", - tool_shed_status: { - latest_installable_revision: true, - }, - }, - ]; - } - } - ); - }); - it("test installed list", async () => { const wrapper = mount(Index, { propsData: { @@ -40,7 +44,7 @@ describe("InstalledList", () => { }, }); expect(wrapper.find(".loading-message").text()).toBe("Loading installed repositories..."); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.find(".installed-message").text()).toBe("2 repositories installed on this instance."); const names = wrapper.findAll(".name"); expect(names.length).toBe(2); diff --git a/client/src/components/Toolshed/InstalledList/Monitor.test.js b/client/src/components/Toolshed/InstalledList/Monitor.test.js index 718e91ed44a..df957fe9907 100644 --- a/client/src/components/Toolshed/InstalledList/Monitor.test.js +++ b/client/src/components/Toolshed/InstalledList/Monitor.test.js @@ -1,36 +1,41 @@ -import { mount } from "@vue/test-utils"; +import { createLocalVue, mount } from "@vue/test-utils"; import Monitor from "./Monitor"; -import { __RewireAPI__ as rewire } from "./Monitor"; -import Vue from "vue"; + +jest.mock("app"); + +import { getAppRoot } from "onload/loadConfig"; +jest.mock("onload/loadConfig"); +getAppRoot.mockImplementation(() => "/"); + +import { Services } from "../services"; +jest.mock("../services"); + +Services.mockImplementation(() => { + return { + async getInstalledRepositories() { + return [ + { + name: "name_0", + owner: "owner_0", + status: "status_0_0", + description: "description_0_0", + }, + { + name: "name_1", + owner: "owner_1", + status: "status_1", + description: "description_1", + }, + ]; + }, + }; +}); describe("Monitor", () => { - beforeEach(() => { - rewire.__Rewire__( - "Services", - class { - async getInstalledRepositories() { - return [ - { - name: "name_0", - owner: "owner_0", - status: "status_0_0", - description: "description_0_0", - }, - { - name: "name_1", - owner: "owner_1", - status: "status_1", - description: "description_1", - }, - ]; - } - } - ); - }); - it("test monitor", async () => { - const wrapper = mount(Monitor, {}); - await Vue.nextTick(); + const localVue = createLocalVue(); + const wrapper = mount(Monitor, localVue); + await localVue.nextTick(); const headers = wrapper.findAll("th"); expect(headers.length).toBe(2); expect(headers.at(0).text()).toBe("Name"); diff --git a/client/src/components/Toolshed/RepositoryDetails/Index.test.js b/client/src/components/Toolshed/RepositoryDetails/Index.test.js index 90d466561bf..3c3a7c529b8 100644 --- a/client/src/components/Toolshed/RepositoryDetails/Index.test.js +++ b/client/src/components/Toolshed/RepositoryDetails/Index.test.js @@ -1,29 +1,34 @@ -import { mount } from "@vue/test-utils"; +import { shallowMount, createLocalVue } from "@vue/test-utils"; import Index from "./Index"; -import { __RewireAPI__ as rewire } from "./Index"; -import Vue from "vue"; + +jest.mock("app"); + +import { getAppRoot } from "onload/loadConfig"; +jest.mock("onload/loadConfig"); +getAppRoot.mockImplementation(() => "/"); + +import { Services } from "../services"; +jest.mock("../services"); + +Services.mockImplementation(() => { + return { + async getRepository(toolshedUrl, repositoryId) { + expect(toolshedUrl).toBe("toolshedUrl"); + expect(repositoryId).toBe("id"); + return []; + }, + async getInstalledRepositoriesByName(name, owner) { + expect(name).toBe("name"); + expect(owner).toBe("owner"); + return []; + }, + }; +}); describe("RepositoryDetails", () => { - beforeEach(() => { - rewire.__Rewire__( - "Services", - class { - async getRepository(toolshedUrl, repositoryId) { - expect(toolshedUrl).toBe("toolshedUrl"); - expect(repositoryId).toBe("id"); - return []; - } - async getInstalledRepositoriesByName(name, owner) { - expect(name).toBe("name"); - expect(owner).toBe("owner"); - return []; - } - } - ); - }); - it("test repository details index", async () => { - const wrapper = mount(Index, { + const localVue = createLocalVue(); + const wrapper = shallowMount(Index, { propsData: { repo: { id: "id", @@ -33,16 +38,12 @@ describe("RepositoryDetails", () => { }, toolshedUrl: "toolshedUrl", }, - stubs: { - RepositoryDetails: true, - InstallationSettings: true, - RepositoryTools: true, - }, + localVue, }); expect(wrapper.find(".loading-message").text()).toBe("Loading repository details..."); - await Vue.nextTick(); + await localVue.nextTick(); expect(wrapper.findAll(".alert").length).toBe(0); - await Vue.nextTick(); + await localVue.nextTick(); expect(wrapper.findAll(".alert").length).toBe(0); }); }); diff --git a/client/src/components/Toolshed/RepositoryDetails/InstallationSettings.test.js b/client/src/components/Toolshed/RepositoryDetails/InstallationSettings.test.js index 33431f5e604..fc19ab1aad0 100644 --- a/client/src/components/Toolshed/RepositoryDetails/InstallationSettings.test.js +++ b/client/src/components/Toolshed/RepositoryDetails/InstallationSettings.test.js @@ -1,16 +1,9 @@ import { mount } from "@vue/test-utils"; import InstallationSettings from "./InstallationSettings"; -import { __RewireAPI__ as rewire } from "./InstallationSettings"; + +jest.mock("app"); describe("InstallationSettings", () => { - beforeEach(() => { - rewire.__Rewire__("getGalaxyInstance", () => { - return { - config: {}, - }; - }); - }); - it("test tool repository installer interface", () => { const wrapper = mount(InstallationSettings, { propsData: { diff --git a/client/src/components/Toolshed/RepositoryDetails/RepositoryTools.test.js b/client/src/components/Toolshed/RepositoryDetails/RepositoryTools.test.js index 5ec0c95c0ec..09b7c9aad4d 100644 --- a/client/src/components/Toolshed/RepositoryDetails/RepositoryTools.test.js +++ b/client/src/components/Toolshed/RepositoryDetails/RepositoryTools.test.js @@ -1,4 +1,3 @@ -import Vue from "vue"; import { mount } from "@vue/test-utils"; import RepositoryTools from "./RepositoryTools"; @@ -57,7 +56,7 @@ describe("RepositoryTools", () => { expect($third.find("td:last-child").text()).toBe(""); const $link = wrapper.find("a"); $link.trigger("click"); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); const $elExpanded = wrapper.findAll("tr"); expect($elExpanded.length).toBe(4); @@ -69,7 +68,7 @@ describe("RepositoryTools", () => { expect($forthExpanded.find("td:last-child").text()).toBe(""); const $linkExpanded = wrapper.find("a"); $linkExpanded.trigger("click"); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); const $elCollapsed = wrapper.findAll("tr"); expect($elCollapsed.length).toBe(3); diff --git a/client/src/components/Toolshed/SearchList/Categories.test.js b/client/src/components/Toolshed/SearchList/Categories.test.js index 5d814a95c49..bf799c7269e 100644 --- a/client/src/components/Toolshed/SearchList/Categories.test.js +++ b/client/src/components/Toolshed/SearchList/Categories.test.js @@ -1,29 +1,32 @@ -import { mount } from "@vue/test-utils"; +import { mount, createLocalVue } from "@vue/test-utils"; import Categories from "./Categories"; -import { __RewireAPI__ as rewire } from "./Categories"; -import Vue from "vue"; + +import { Services } from "../services"; +jest.mock("../services"); + +Services.mockImplementation(() => { + return { + async getCategories() { + return [ + { + name: "name_0", + description: "description_0", + repositories: "repositories_0", + }, + { + name: "name_1", + description: "description_1", + repositories: "repositories_1", + }, + ]; + }, + }; +}); describe("Categories", () => { + let localVue; beforeEach(() => { - rewire.__Rewire__( - "Services", - class { - async getCategories() { - return [ - { - name: "name_0", - description: "description_0", - repositories: "repositories_0", - }, - { - name: "name_1", - description: "description_1", - repositories: "repositories_1", - }, - ]; - } - } - ); + localVue = createLocalVue(); }); it("test categories loading", () => { @@ -32,6 +35,7 @@ describe("Categories", () => { loading: true, toolshedUrl: "toolshedUrl", }, + localVue, }); expect(wrapper.find(".loading-message").text()).toBe("Loading categories..."); }); @@ -42,8 +46,9 @@ describe("Categories", () => { loading: false, toolshedUrl: "toolshedUrl", }, + localVue, }); - await Vue.nextTick(); + await localVue.nextTick(); const links = wrapper.findAll("a"); expect(links.length).toBe(2); expect(links.at(0).text()).toBe("name_0"); diff --git a/client/src/components/Toolshed/SearchList/Repositories.test.js b/client/src/components/Toolshed/SearchList/Repositories.test.js index 7e6c300946c..ea3c1fb7d08 100644 --- a/client/src/components/Toolshed/SearchList/Repositories.test.js +++ b/client/src/components/Toolshed/SearchList/Repositories.test.js @@ -1,32 +1,34 @@ -import { mount } from "@vue/test-utils"; +import { mount, createLocalVue } from "@vue/test-utils"; import Repositories from "./Repositories"; -import { __RewireAPI__ as rewire } from "./Repositories"; -import Vue from "vue"; + +jest.mock("app"); + +import { Services } from "../services"; +jest.mock("../services"); + +Services.mockImplementation(() => { + return { + async getRepositories() { + return [ + { + name: "name_0", + owner: "owner_0", + last_updated: "last_updated_0", + times_downloaded: "times_downloaded_0", + }, + { + name: "name_1", + owner: "owner_1", + last_updated: "last_updated_1", + times_downloaded: "times_downloaded_1", + }, + ]; + }, + }; +}); describe("Repositories", () => { - beforeEach(() => { - rewire.__Rewire__( - "Services", - class { - async getRepositories() { - return [ - { - name: "name_0", - owner: "owner_0", - last_updated: "last_updated_0", - times_downloaded: "times_downloaded_0", - }, - { - name: "name_1", - owner: "owner_1", - last_updated: "last_updated_1", - times_downloaded: "times_downloaded_1", - }, - ]; - } - } - ); - }); + const localVue = createLocalVue(); it("test repository details loading", async () => { const wrapper = mount(Repositories, { @@ -35,10 +37,11 @@ describe("Repositories", () => { scrolled: false, toolshedUrl: "toolshedUrl", }, + localVue, }); // Test initial state prior to the data fetch tick -- should be loading. expect(wrapper.find(".loading-message").text()).toBe("Loading repositories..."); - await Vue.nextTick(); + await localVue.nextTick(); const links = wrapper.findAll("a"); expect(links.length).toBe(2); expect(links.at(0).text()).toBe("name_0"); @@ -46,7 +49,7 @@ describe("Repositories", () => { // Reset repositories and state to test empty. wrapper.vm.repositories = []; wrapper.vm.pageState = 2; // COMPLETE is '2' - await Vue.nextTick(); + await localVue.nextTick(); expect(wrapper.find(".unavailable-message").text()).toBe("No matching repositories found."); }); }); diff --git a/client/src/components/User/CloudAuth/CloudAuth.test.js b/client/src/components/User/CloudAuth/CloudAuth.test.js index a4230fa476e..85753d43875 100644 --- a/client/src/components/User/CloudAuth/CloudAuth.test.js +++ b/client/src/components/User/CloudAuth/CloudAuth.test.js @@ -7,8 +7,6 @@ import CloudAuthItem from "./CloudAuthItem"; import _l from "utils/localization"; import BootstrapVue from "bootstrap-vue"; -import { getNewAttachNode } from "jest/helpers"; - jest.mock("./model/service", () => ({ listCredentials: async () => { const listCredentials = require("./testdata/listCredentials.json"); @@ -25,7 +23,7 @@ describe("CloudAuth component", () => { let wrapper; beforeEach(async () => { - wrapper = shallowMount(CloudAuth, { localVue, attachTo: getNewAttachNode() }); + wrapper = shallowMount(CloudAuth, { localVue }); await flushPromises(); }); diff --git a/client/src/components/Workflow/Editor/Attributes.test.js b/client/src/components/Workflow/Editor/Attributes.test.js index 6f5cca2f5d5..c7f989c50e0 100644 --- a/client/src/components/Workflow/Editor/Attributes.test.js +++ b/client/src/components/Workflow/Editor/Attributes.test.js @@ -1,20 +1,22 @@ -import Vue from "vue"; -import { mount } from "@vue/test-utils"; +import { mount, createLocalVue } from "@vue/test-utils"; import Attributes from "./Attributes"; -import { __RewireAPI__ as rewire } from "./Attributes"; + +jest.mock("app"); + +import { Services } from "../services"; +jest.mock("../services"); + +Services.mockImplementation(() => { + return { + async updateWorkflow() { + return {}; + }, + }; +}); describe("Attributes", () => { - beforeEach(() => { - rewire.__Rewire__( - "Services", - class { - async updateWorkflow() { - return {}; - } - } - ); - }); it("test attributes", async () => { + const localVue = createLocalVue(); const wrapper = mount(Attributes, { propsData: { id: "workflow_id", @@ -26,11 +28,12 @@ describe("Attributes", () => { stubs: { LicenseSelector: true, }, + localVue, }); const name = wrapper.find("#workflow-name"); expect(name.element.value).toBe("workflow_name"); wrapper.setProps({ name: "new_workflow_name" }); - await Vue.nextTick(); + await localVue.nextTick(); expect(name.element.value).toBe("new_workflow_name"); const parameters = wrapper.findAll(".list-group-item"); expect(parameters.length).toBe(2); diff --git a/client/src/components/Workflow/Editor/Node.test.js b/client/src/components/Workflow/Editor/Node.test.js index ad0bfb637ee..18edb90e32c 100644 --- a/client/src/components/Workflow/Editor/Node.test.js +++ b/client/src/components/Workflow/Editor/Node.test.js @@ -1,6 +1,5 @@ import { mount } from "@vue/test-utils"; import Node from "./Node"; -import { getNewAttachNode } from "jest/helpers"; import flushPromises from "flush-promises"; jest.mock("app"); @@ -16,7 +15,6 @@ describe("Node", () => { getManager: () => {}, getCanvasManager: () => {}, }, - attachTo: getNewAttachNode(), }); await flushPromises(); const icon = wrapper.findAll("i"); diff --git a/client/src/components/Workflow/Editor/ZoomControl.test.js b/client/src/components/Workflow/Editor/ZoomControl.test.js index 9ba6e18af5e..bbd52df00ad 100644 --- a/client/src/components/Workflow/Editor/ZoomControl.test.js +++ b/client/src/components/Workflow/Editor/ZoomControl.test.js @@ -1,26 +1,26 @@ -import Vue from "vue"; -import { mount } from "@vue/test-utils"; +import { mount, createLocalVue } from "@vue/test-utils"; import ZoomControl from "./ZoomControl"; -import { getNewAttachNode } from "jest/helpers"; + +jest.mock("app"); describe("ZoomControl", () => { it("test zoom control", async () => { + const localVue = createLocalVue(); const wrapper = mount(ZoomControl, { propsData: { zoomLevel: 10, }, - attachTo: getNewAttachNode(), }); const buttons = wrapper.findAll("button"); expect(buttons.length).toBe(3); buttons.at(0).trigger("click"); - await Vue.nextTick(); + await localVue.nextTick(); expect(wrapper.emitted().onZoom[0][0]).toBe(9); buttons.at(1).trigger("click"); - await Vue.nextTick(); + await localVue.nextTick(); expect(wrapper.emitted().onZoom[1][0]).toBe(10); buttons.at(2).trigger("click"); - await Vue.nextTick(); + await localVue.nextTick(); expect(wrapper.emitted().onZoom[2][0]).toBe(11); }); }); diff --git a/client/src/components/admin/BaseList.test.js b/client/src/components/admin/BaseList.test.js index 843258d2eed..bf1a7344e01 100644 --- a/client/src/components/admin/BaseList.test.js +++ b/client/src/components/admin/BaseList.test.js @@ -1,6 +1,5 @@ import { mount } from "@vue/test-utils"; import BaseList from "./BaseList"; -import Vue from "vue"; describe("Categories", () => { const getter = async () => { @@ -32,7 +31,7 @@ describe("Categories", () => { setter: setter, }, }); - await Vue.nextTick(); + await wrapper.vm.$nextTick(); expect(wrapper.find(".card-header").text()).toContain("There are 2"); const th = wrapper.findAll("th"); expect(th.length).toBe(3); diff --git a/client/tests/karma/webpack.config.unittest.js b/client/tests/karma/webpack.config.unittest.js index aadbeaf0ed3..52754d262bc 100644 --- a/client/tests/karma/webpack.config.unittest.js +++ b/client/tests/karma/webpack.config.unittest.js @@ -31,18 +31,12 @@ module.exports = (env, argv) => { wpConfig.module = merge.smart(wpConfig.module, ignoreAssetLoaders); - // Using babel-plugin-rewire to handle dependency mocking since webpack 4 - // exports immutable bindings for ES modules but we still need a way to - // overwrite dependencies during unit-testing. - // - // Additionally, set exclude for babel-loader to completely ignore *all* - // node-modules, without our exceptions to support IE as in parent webpack - // config. + // Set exclude for babel-loader to completely ignore *all* node-modules, + // without our exceptions to support IE as in parent webpack config. wpConfig.module.rules = wpConfig.module.rules.map((rule) => { if (rule.loader == "babel-loader") { rule.exclude = [/(node_modules\/)/]; - rule.options.plugins.push("rewire"); } return rule; }); diff --git a/client/yarn.lock b/client/yarn.lock index da62986c06d..3f4cee02ebd 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -3041,11 +3041,6 @@ babel-plugin-jest-hoist@^26.2.0: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-rewire@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-rewire/-/babel-plugin-rewire-1.2.0.tgz#822562d72ed2c84e47c0f95ee232c920853e9d89" - integrity sha512-JBZxczHw3tScS+djy6JPLMjblchGhLI89ep15H3SyjujIzlxo5nr6Yjo7AXotdeVczeBmWs0tF8PgJWDdgzAkQ== - babel-plugin-transform-es2015-modules-commonjs@^6.26.0: version "6.26.2" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" From 5cf88d600eeae3ee567b8dc07d116d396f6cea34 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Thu, 17 Dec 2020 08:43:50 -0500 Subject: [PATCH 02/12] Update caniuse browserslist --- client/yarn.lock | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/client/yarn.lock b/client/yarn.lock index 3f4cee02ebd..a862258f6df 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -3743,30 +3743,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000890: - version "1.0.30000893" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000893.tgz#284b20932bd41b93e21626975f2050cb01561986" - integrity sha512-kOddHcTEef+NgN/fs0zmX2brHTNATVOWMEIhlZHCuwQRtXobjSw9pAECc44Op4bTBcavRjkLaPrGomknH7+Jvg== - -caniuse-lite@^1.0.30000929: - version "1.0.30000953" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000953.tgz#8054c4e5c4aa69dc3269353a4a5e102909759dbb" - integrity sha512-2stdF/q5MZTDhQ6uC65HWbSgI9UMKbc7+HKvlwH5JBIslKoD/J9dvabP4J4Uiifu3NljbHj3iMpfYflLSNt09A== - -caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001061: - version "1.0.30001062" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001062.tgz#d814b648338504b315222ace6f1a533d9a55e390" - integrity sha512-ei9ZqeOnN7edDrb24QfJ0OZicpEbsWxv7WusOiQGz/f2SfvBgHHbOEwBJ8HKGVSyx8Z6ndPjxzR6m0NQq+0bfw== - -caniuse-lite@^1.0.30001022: - version "1.0.30001022" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001022.tgz#9eeffe580c3a8f110b7b1742dcf06a395885e4c6" - integrity sha512-FjwPPtt/I07KyLPkBQ0g7/XuZg6oUkYBVnPHNj3VHJbOjmmJ/GdSo/GUY6MwINEQvjhP6WZVbX8Tvms8xh0D5A== - -caniuse-lite@^1.0.30001043: - version "1.0.30001053" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001053.tgz#b7ae027567ce2665b965b0437e4512b296ccd20d" - integrity sha512-HtV4wwIZl6GA4Oznse8aR274XUOYGZnQLcf/P8vHgmlfqSNelwD+id8CyHOceqLqt9yfKmo7DUZTh1EuS9pukg== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000890, caniuse-lite@^1.0.30000929, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001022, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001061: + version "1.0.30001168" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001168.tgz" + integrity sha512-P2zmX7swIXKu+GMMR01TWa4csIKELTNnZKc+f1CjebmZJQtTAEXmpQSoKVJVVcvPGAA0TEYTOUp3VehavZSFPQ== capture-exit@^2.0.0: version "2.0.0" From 8ae60f280256e90165ae9f62e77021a9512f9f34 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Thu, 17 Dec 2020 08:53:00 -0500 Subject: [PATCH 03/12] Fix duplicate manual mock discovery. jest-haste-map: duplicate manual mock found: index The following files share their name; please delete one of them: * /src/app/__mocks__/index.js * /src/components/History/caching/__mocks__/index.js --- client/tests/jest/jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/tests/jest/jest.config.js b/client/tests/jest/jest.config.js index 86e2c70845d..27c222d4c00 100644 --- a/client/tests/jest/jest.config.js +++ b/client/tests/jest/jest.config.js @@ -84,7 +84,7 @@ module.exports = { }, // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], + modulePathIgnorePatterns: ["/src/.*/__mocks__"], // Activates notifications for test results // notify: false, From 690f639bfa2e6dae014922810efeb2d5a5d009d0 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Thu, 17 Dec 2020 09:04:36 -0500 Subject: [PATCH 04/12] Drop getNewAttachNode 'helper' completely --- client/tests/jest/helpers.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/client/tests/jest/helpers.js b/client/tests/jest/helpers.js index ad5d9796129..8a4006f95ff 100644 --- a/client/tests/jest/helpers.js +++ b/client/tests/jest/helpers.js @@ -5,14 +5,6 @@ import { timer } from "rxjs"; import { take } from "rxjs/operators"; import { shallowMount } from "@vue/test-utils"; -export function getNewAttachNode() { - const attachElement = document.createElement("div"); - if (document.body) { - document.body.appendChild(attachElement); - } - return attachElement; -} - // Creates a watcher on the indicated vm/prop for use in testing export function watchForChange({ vm, opts, propName, timeout = 1000, label = '' }) { const start = new Date(); From a8d195c2ea9de6d94603f28bb57deea14f1eaae5 Mon Sep 17 00:00:00 2001 From: Mason Date: Thu, 17 Dec 2020 08:41:53 -0800 Subject: [PATCH 05/12] suppress bootstrap warnings during unit testing --- client/tests/jest/jest.setup.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/tests/jest/jest.setup.js b/client/tests/jest/jest.setup.js index 660c40d654e..e165b562fe6 100644 --- a/client/tests/jest/jest.setup.js +++ b/client/tests/jest/jest.setup.js @@ -2,4 +2,8 @@ import "@testing-library/jest-dom"; /* still don't understand what was invoking the following, but nothing should, and this makes the tag tests work correctly */ -global.XMLHttpRequest = undefined; \ No newline at end of file +global.XMLHttpRequest = undefined; + +// stops Bootstrap's warnings when components can't find a non-existent doc +// https://github.com/bootstrap-vue/bootstrap-vue/issues/3303 +process.env.BOOTSTRAP_VUE_NO_WARN = true; From fad0489e99178153dd95b741449f880dec7ad542 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Thu, 17 Dec 2020 12:47:09 -0500 Subject: [PATCH 06/12] Test mocking the bootstrap-vue tooltip directive with a no-op; this way we can still get other warnings. --- .../components/Dataset/DatasetName.test.js | 14 +++++++--- client/tests/jest/helpers.js | 28 ++++++++++++++----- client/tests/jest/jest.setup.js | 6 +--- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/client/src/components/Dataset/DatasetName.test.js b/client/src/components/Dataset/DatasetName.test.js index e42311d5206..0c6cc02968e 100644 --- a/client/src/components/Dataset/DatasetName.test.js +++ b/client/src/components/Dataset/DatasetName.test.js @@ -1,10 +1,14 @@ -import { mount } from "@vue/test-utils"; +import { shallowMount } from "@vue/test-utils"; import DatasetName from "./DatasetName"; +import { getLocalVue } from "jest/helpers"; + +const localVue = getLocalVue(); describe("Dataset Name", () => { it("test dataset default", async () => { - const wrapper = mount(DatasetName, { + const wrapper = shallowMount(DatasetName, { propsData: { item: { name: "name", state: "success" } }, + localVue, }); const state = wrapper.findAll(".name"); expect(state.length).toBe(1); @@ -17,8 +21,9 @@ describe("Dataset Name", () => { expect(Array.isArray(wrapper.emitted().copyDataset)).toBe(true); }); it("test dataset error", async () => { - const wrapper = mount(DatasetName, { + const wrapper = shallowMount(DatasetName, { propsData: { item: { name: "name", state: "error" } }, + localVue, }); const state = wrapper.findAll(".name"); expect(state.length).toBe(1); @@ -28,8 +33,9 @@ describe("Dataset Name", () => { expect(errorstate.at(0).classes()).toEqual(expect.arrayContaining(["text-danger"])); }); it("test dataset paused", async () => { - const wrapper = mount(DatasetName, { + const wrapper = shallowMount(DatasetName, { propsData: { item: { name: "name", state: "paused" } }, + localVue, }); const state = wrapper.findAll(".name"); expect(state.length).toBe(1); diff --git a/client/tests/jest/helpers.js b/client/tests/jest/helpers.js index 8a4006f95ff..1adfa7775be 100644 --- a/client/tests/jest/helpers.js +++ b/client/tests/jest/helpers.js @@ -3,20 +3,24 @@ */ import { timer } from "rxjs"; import { take } from "rxjs/operators"; -import { shallowMount } from "@vue/test-utils"; +import { createLocalVue, shallowMount } from "@vue/test-utils"; // Creates a watcher on the indicated vm/prop for use in testing -export function watchForChange({ vm, opts, propName, timeout = 1000, label = '' }) { +export function watchForChange({ vm, opts, propName, timeout = 1000, label = "" }) { const start = new Date(); return new Promise((resolve, reject) => { const timeoutID = setTimeout(() => { reject(`${propName} never changed ${label}`); }, timeout); - vm.$watch(propName, function (newVal, oldVal) { - clearTimeout(timeoutID); - const stop = new Date(); - resolve({ timeElapsed: stop - start, newVal, oldVal }); - }, opts); + vm.$watch( + propName, + function (newVal, oldVal) { + clearTimeout(timeoutID); + const stop = new Date(); + resolve({ timeElapsed: stop - start, newVal, oldVal }); + }, + opts + ); }); } @@ -49,6 +53,16 @@ export const showAll = (vm) => { // usage: await wait(500); export const wait = (n) => timer(n).pipe(take(1)).toPromise(); +// Gets a localVue with custom directives +export function getLocalVue() { + const localVue = createLocalVue(); + const mockedDirective = { + bind() {}, + }; + localVue.directive("b-tooltip", mockedDirective); + return localVue; +} + // Mounts a renderless component with sample content for testing export function mountRenderless(component, localVue, propsData) { return shallowMount(component, { diff --git a/client/tests/jest/jest.setup.js b/client/tests/jest/jest.setup.js index e165b562fe6..660c40d654e 100644 --- a/client/tests/jest/jest.setup.js +++ b/client/tests/jest/jest.setup.js @@ -2,8 +2,4 @@ import "@testing-library/jest-dom"; /* still don't understand what was invoking the following, but nothing should, and this makes the tag tests work correctly */ -global.XMLHttpRequest = undefined; - -// stops Bootstrap's warnings when components can't find a non-existent doc -// https://github.com/bootstrap-vue/bootstrap-vue/issues/3303 -process.env.BOOTSTRAP_VUE_NO_WARN = true; +global.XMLHttpRequest = undefined; \ No newline at end of file From b5522baf68db31b19ae78834045fcabda396fa6d Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Thu, 17 Dec 2020 13:36:02 -0500 Subject: [PATCH 07/12] Mock bootstrap-vue popovers too --- client/tests/jest/helpers.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/tests/jest/helpers.js b/client/tests/jest/helpers.js index 1adfa7775be..958b61fba07 100644 --- a/client/tests/jest/helpers.js +++ b/client/tests/jest/helpers.js @@ -60,6 +60,7 @@ export function getLocalVue() { bind() {}, }; localVue.directive("b-tooltip", mockedDirective); + localVue.directive("b-popover", mockedDirective); return localVue; } From 54fbafee3604458750769c41b91af1aa046b60a8 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Thu, 17 Dec 2020 13:39:34 -0500 Subject: [PATCH 08/12] Swap tests of components with bs-vue tip/pops to use a localvue with mocked directives for bs-vue tips/popovers --- .../InteractiveTools/InteractiveTools.test.js | 6 ++++-- client/src/components/Masthead/Masthead.test.js | 5 +++-- client/src/components/Masthead/MastheadItem.test.js | 5 +++-- client/src/components/Panels/Common/Tool.test.js | 6 ++++++ .../src/components/Panels/Common/ToolSection.test.js | 6 ++++++ .../components/RuleBuilder/SavedRulesSelector.test.js | 4 ++++ client/src/components/ToolsView/ToolsView.test.js | 10 +++++----- client/src/components/User/CloudAuth/CloudAuth.test.js | 5 +++-- client/src/components/Workflow/Editor/Node.test.js | 4 ++++ .../src/components/Workflow/Editor/ZoomControl.test.js | 6 ++++-- client/tests/jest/helpers.js | 2 ++ 11 files changed, 44 insertions(+), 15 deletions(-) diff --git a/client/src/components/InteractiveTools/InteractiveTools.test.js b/client/src/components/InteractiveTools/InteractiveTools.test.js index 64df3fb284f..ab5f8f34b9c 100644 --- a/client/src/components/InteractiveTools/InteractiveTools.test.js +++ b/client/src/components/InteractiveTools/InteractiveTools.test.js @@ -1,5 +1,6 @@ import InteractiveTools from "./InteractiveTools"; -import { mount, createLocalVue } from "@vue/test-utils"; +import { mount } from "@vue/test-utils"; +import { getLocalVue } from "jest/helpers"; import flushPromises from "flush-promises"; import _l from "utils/localization"; import testInteractiveToolsResponse from "./testData/testInteractiveToolsResponse"; @@ -8,7 +9,7 @@ import MockAdapter from "axios-mock-adapter"; import axios from "axios"; describe("InteractiveTools/InteractiveTools.vue", () => { - const localVue = createLocalVue(); + const localVue = getLocalVue(); localVue.filter("localize", (value) => _l(value)); let wrapper; let axiosMock; @@ -23,6 +24,7 @@ describe("InteractiveTools/InteractiveTools.vue", () => { }; }, }, + localVue, }); axiosMock.onGet("/api/entry_points?running=true").reply(200, testInteractiveToolsResponse); axiosMock.onPost("/interactivetool/list").reply(200, { status: "ok", message: "ok" }); diff --git a/client/src/components/Masthead/Masthead.test.js b/client/src/components/Masthead/Masthead.test.js index 6e30338adf0..1e92be2e7df 100644 --- a/client/src/components/Masthead/Masthead.test.js +++ b/client/src/components/Masthead/Masthead.test.js @@ -1,5 +1,6 @@ import { default as Masthead } from "./Masthead.vue"; -import { mount, createLocalVue } from "@vue/test-utils"; +import { mount } from "@vue/test-utils"; +import { getLocalVue } from "jest/helpers"; import Scratchbook from "layout/scratchbook"; import { fetchMenu } from "layout/menu"; import { loadWebhookMenuItems } from "./_webhooks"; @@ -33,7 +34,7 @@ describe("Masthead.vue", () => { loadWebhookMenuItems.mockImplementation(stubLoadWebhooks); beforeEach(() => { - localVue = createLocalVue(); + localVue = getLocalVue(); quotaRendered = false; quotaEl = null; diff --git a/client/src/components/Masthead/MastheadItem.test.js b/client/src/components/Masthead/MastheadItem.test.js index ccbe0f791aa..6331bd64988 100644 --- a/client/src/components/Masthead/MastheadItem.test.js +++ b/client/src/components/Masthead/MastheadItem.test.js @@ -1,4 +1,5 @@ -import { shallowMount, createLocalVue } from "@vue/test-utils"; +import { shallowMount } from "@vue/test-utils"; +import { getLocalVue } from "jest/helpers"; import MastheadItem from "./MastheadItem.vue"; describe("MastheadItem.vue", () => { @@ -8,7 +9,7 @@ describe("MastheadItem.vue", () => { let menu; beforeEach(() => { - localVue = createLocalVue(); + localVue = getLocalVue(); }); function m() { diff --git a/client/src/components/Panels/Common/Tool.test.js b/client/src/components/Panels/Common/Tool.test.js index ebc464d42e0..6782cdf79af 100644 --- a/client/src/components/Panels/Common/Tool.test.js +++ b/client/src/components/Panels/Common/Tool.test.js @@ -1,12 +1,16 @@ import { mount } from "@vue/test-utils"; +import { getLocalVue } from "jest/helpers"; import Tool from "./Tool"; +const localVue = getLocalVue(); + describe("Tool", () => { test("test tool", () => { const wrapper = mount(Tool, { propsData: { tool: {}, }, + localVue, }); const nameElement = wrapper.findAll(".name"); expect(nameElement.at(0).text()).toBe(""); @@ -28,6 +32,7 @@ describe("Tool", () => { operationIcon: "operationIconClass", operationTitle: "operationTitle", }, + localVue, }); const nameElement = wrapper.findAll(".name"); expect(nameElement.at(0).text()).toBe("name"); @@ -45,6 +50,7 @@ describe("Tool", () => { }, hideName: true, }, + localVue, }); const nameElement = wrapper.findAll(".name"); expect(nameElement.length).toBe(0); diff --git a/client/src/components/Panels/Common/ToolSection.test.js b/client/src/components/Panels/Common/ToolSection.test.js index aadcd347587..f93e13445e1 100644 --- a/client/src/components/Panels/Common/ToolSection.test.js +++ b/client/src/components/Panels/Common/ToolSection.test.js @@ -1,6 +1,9 @@ import { mount } from "@vue/test-utils"; +import { getLocalVue } from "jest/helpers"; import ToolSection from "./ToolSection"; +const localVue = getLocalVue(); + describe("ToolSection", () => { test("test tool section", () => { const wrapper = mount(ToolSection, { @@ -9,6 +12,7 @@ describe("ToolSection", () => { name: "name", }, }, + localVue, }); const nameElement = wrapper.findAll(".name"); expect(nameElement.at(0).text()).toBe("name"); @@ -31,6 +35,7 @@ describe("ToolSection", () => { ], }, }, + localVue, }); expect(wrapper.vm.opened).toBe(false); const $sectionName = wrapper.find(".name"); @@ -62,6 +67,7 @@ describe("ToolSection", () => { }, queryFilter: "test", }, + localVue, }); expect(wrapper.vm.opened).toBe(true); const $sectionName = wrapper.find(".name"); diff --git a/client/src/components/RuleBuilder/SavedRulesSelector.test.js b/client/src/components/RuleBuilder/SavedRulesSelector.test.js index e55bd4e372b..98b5bf425be 100644 --- a/client/src/components/RuleBuilder/SavedRulesSelector.test.js +++ b/client/src/components/RuleBuilder/SavedRulesSelector.test.js @@ -1,6 +1,9 @@ import { mount } from "@vue/test-utils"; +import { getLocalVue } from "jest/helpers"; import SavedRulesSelector from "components/RuleBuilder/SavedRulesSelector"; +const localVue = getLocalVue(); + describe("SavedRulesSelector", () => { let wrapper; let emitted; @@ -12,6 +15,7 @@ describe("SavedRulesSelector", () => { prefix: "test_prefix_" + new Date().toISOString() + "_", savedRules: [], }, + localVue, }); await wrapper.vm.$nextTick(); }); diff --git a/client/src/components/ToolsView/ToolsView.test.js b/client/src/components/ToolsView/ToolsView.test.js index 288027a01fd..7fa169d33cf 100644 --- a/client/src/components/ToolsView/ToolsView.test.js +++ b/client/src/components/ToolsView/ToolsView.test.js @@ -1,6 +1,6 @@ import ToolsView from "./ToolsView"; -import { mount, createLocalVue } from "@vue/test-utils"; -import _l from "utils/localization"; +import { mount } from "@vue/test-utils"; +import { getLocalVue } from "jest/helpers"; import flushPromises from "flush-promises"; // test response @@ -12,14 +12,14 @@ import axios from "axios"; jest.mock("app"); describe("ToolsView/ToolsView.vue", () => { - const localVue = createLocalVue(); - localVue.filter("localize", (value) => _l(value)); + const localVue = getLocalVue(); + let wrapper; let axiosMock; beforeEach(async () => { axiosMock = new MockAdapter(axios); - wrapper = mount(ToolsView); + wrapper = mount(ToolsView, localVue); axiosMock.onGet("/api/tools?tool_help=True").reply(200, testToolsListResponse); axiosMock.onGet(new RegExp(`./*/citations`)).reply(200, testCitation); await flushPromises(); diff --git a/client/src/components/User/CloudAuth/CloudAuth.test.js b/client/src/components/User/CloudAuth/CloudAuth.test.js index 85753d43875..bc7751a4e74 100644 --- a/client/src/components/User/CloudAuth/CloudAuth.test.js +++ b/client/src/components/User/CloudAuth/CloudAuth.test.js @@ -1,5 +1,6 @@ import flushPromises from "flush-promises"; -import { shallowMount, createLocalVue } from "@vue/test-utils"; +import { shallowMount } from "@vue/test-utils"; +import { getLocalVue } from "jest/helpers"; import { default as CloudAuth } from "./CloudAuth"; import CloudAuthItem from "./CloudAuthItem"; @@ -15,7 +16,7 @@ jest.mock("./model/service", () => ({ }, })); -const localVue = createLocalVue(); +const localVue = getLocalVue(); localVue.use(BootstrapVue); localVue.filter("localize", (value) => _l(value)); diff --git a/client/src/components/Workflow/Editor/Node.test.js b/client/src/components/Workflow/Editor/Node.test.js index 18edb90e32c..99ce6cc4665 100644 --- a/client/src/components/Workflow/Editor/Node.test.js +++ b/client/src/components/Workflow/Editor/Node.test.js @@ -1,9 +1,12 @@ import { mount } from "@vue/test-utils"; +import { getLocalVue } from "jest/helpers"; import Node from "./Node"; import flushPromises from "flush-promises"; jest.mock("app"); +const localVue = getLocalVue(); + describe("Node", () => { it("test attributes", async () => { const wrapper = mount(Node, { @@ -15,6 +18,7 @@ describe("Node", () => { getManager: () => {}, getCanvasManager: () => {}, }, + localVue, }); await flushPromises(); const icon = wrapper.findAll("i"); diff --git a/client/src/components/Workflow/Editor/ZoomControl.test.js b/client/src/components/Workflow/Editor/ZoomControl.test.js index bbd52df00ad..80fad50ab12 100644 --- a/client/src/components/Workflow/Editor/ZoomControl.test.js +++ b/client/src/components/Workflow/Editor/ZoomControl.test.js @@ -1,15 +1,17 @@ -import { mount, createLocalVue } from "@vue/test-utils"; +import { mount } from "@vue/test-utils"; +import { getLocalVue } from "jest/helpers"; import ZoomControl from "./ZoomControl"; jest.mock("app"); describe("ZoomControl", () => { it("test zoom control", async () => { - const localVue = createLocalVue(); + const localVue = getLocalVue(); const wrapper = mount(ZoomControl, { propsData: { zoomLevel: 10, }, + localVue, }); const buttons = wrapper.findAll("button"); expect(buttons.length).toBe(3); diff --git a/client/tests/jest/helpers.js b/client/tests/jest/helpers.js index 958b61fba07..3814cfa97e6 100644 --- a/client/tests/jest/helpers.js +++ b/client/tests/jest/helpers.js @@ -4,6 +4,7 @@ import { timer } from "rxjs"; import { take } from "rxjs/operators"; import { createLocalVue, shallowMount } from "@vue/test-utils"; +import _l from "utils/localization"; // Creates a watcher on the indicated vm/prop for use in testing export function watchForChange({ vm, opts, propName, timeout = 1000, label = "" }) { @@ -61,6 +62,7 @@ export function getLocalVue() { }; localVue.directive("b-tooltip", mockedDirective); localVue.directive("b-popover", mockedDirective); + localVue.filter("localize", (value) => _l(value)); return localVue; } From b9ff6ac3319834f01f9ff0f7baf1b5575682a778 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Thu, 17 Dec 2020 19:03:46 -0500 Subject: [PATCH 09/12] Pass localVue correctly in toolsview test --- client/src/components/ToolsView/ToolsView.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/ToolsView/ToolsView.test.js b/client/src/components/ToolsView/ToolsView.test.js index 7fa169d33cf..cf7cdd34a56 100644 --- a/client/src/components/ToolsView/ToolsView.test.js +++ b/client/src/components/ToolsView/ToolsView.test.js @@ -19,7 +19,7 @@ describe("ToolsView/ToolsView.vue", () => { beforeEach(async () => { axiosMock = new MockAdapter(axios); - wrapper = mount(ToolsView, localVue); + wrapper = mount(ToolsView, { localVue }); axiosMock.onGet("/api/tools?tool_help=True").reply(200, testToolsListResponse); axiosMock.onGet(new RegExp(`./*/citations`)).reply(200, testCitation); await flushPromises(); From acb6f4935d1d1d6f2a90f8194f7d0637b8434fe4 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Thu, 17 Dec 2020 22:08:04 -0500 Subject: [PATCH 10/12] Shallowmount node.test.js, fix final crummy bs-vue warning --- client/src/components/Workflow/Editor/Node.test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/components/Workflow/Editor/Node.test.js b/client/src/components/Workflow/Editor/Node.test.js index 99ce6cc4665..60859fb0b09 100644 --- a/client/src/components/Workflow/Editor/Node.test.js +++ b/client/src/components/Workflow/Editor/Node.test.js @@ -1,4 +1,4 @@ -import { mount } from "@vue/test-utils"; +import { shallowMount } from "@vue/test-utils"; import { getLocalVue } from "jest/helpers"; import Node from "./Node"; import flushPromises from "flush-promises"; @@ -9,7 +9,7 @@ const localVue = getLocalVue(); describe("Node", () => { it("test attributes", async () => { - const wrapper = mount(Node, { + const wrapper = shallowMount(Node, { propsData: { id: "node-id", name: "node-name", @@ -17,6 +17,7 @@ describe("Node", () => { step: {}, getManager: () => {}, getCanvasManager: () => {}, + datatypesMapper: {}, }, localVue, }); From 96ef5eeb2778cea37e5c1c3ce502b684aca3b05f Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Thu, 17 Dec 2020 22:30:43 -0500 Subject: [PATCH 11/12] Shrink usage of store in jobdestinationparams test, fixes the hang (trying to load user). Still not great, but now everything passes and there are zero warnings. Also shallowmount. --- .../JobDestinationParams.test.js | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/client/src/components/JobDestinationParams/JobDestinationParams.test.js b/client/src/components/JobDestinationParams/JobDestinationParams.test.js index 8ec6a77d7f1..46f52478c62 100644 --- a/client/src/components/JobDestinationParams/JobDestinationParams.test.js +++ b/client/src/components/JobDestinationParams/JobDestinationParams.test.js @@ -1,34 +1,43 @@ import Vuex from "vuex"; -import { mount, createLocalVue } from "@vue/test-utils"; -import { createStore } from "../../store"; +import { shallowMount, createLocalVue } from "@vue/test-utils"; +import createCache from "vuex-cache"; import JobDestinationParams from "./JobDestinationParams"; import jobDestinationResponse from "./testData/jobDestinationResponse"; const JOB_ID = "foo_job_id"; -describe("JobDestinationParams/JobDestinationParams.vue", () => { - const localVue = createLocalVue(); - localVue.use(Vuex); +const localVue = createLocalVue(); +localVue.use(Vuex); - const responseKeys = Object.keys(jobDestinationResponse); - - let testStore; - let wrapper; - - beforeEach(async () => { - testStore = createStore(); - const propsData = { - jobId: JOB_ID, - }; - wrapper = mount(JobDestinationParams, { - store: testStore, - propsData, - localVue, - computed: { - jobDestinationParams() { +const testStore = new Vuex.Store({ + plugins: [createCache()], + modules: { + jobDestinationParametersStore: { + actions: { + fetchJobDestinationParams: jest.fn(), + }, + getters: { + jobDestinationParams: (state) => (job_id) => { return jobDestinationResponse; }, }, + }, + }, +}); + +describe("JobDestinationParams/JobDestinationParams.vue", () => { + const responseKeys = Object.keys(jobDestinationResponse); + + let wrapper; + + beforeEach(async () => { + const propsData = { + jobId: JOB_ID, + }; + wrapper = shallowMount(JobDestinationParams, { + store: testStore, + propsData, + localVue, }); expect(responseKeys.length > 0).toBeTruthy(); }); From 36b129a82158e2ee395cd1521635b84a962c16b8 Mon Sep 17 00:00:00 2001 From: Mason Date: Fri, 18 Dec 2020 00:28:49 -0800 Subject: [PATCH 12/12] swapped manual localization directive for centralized localization plugin --- client/tests/jest/helpers.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/tests/jest/helpers.js b/client/tests/jest/helpers.js index 3814cfa97e6..a908f3cac20 100644 --- a/client/tests/jest/helpers.js +++ b/client/tests/jest/helpers.js @@ -4,7 +4,8 @@ import { timer } from "rxjs"; import { take } from "rxjs/operators"; import { createLocalVue, shallowMount } from "@vue/test-utils"; -import _l from "utils/localization"; +import { localizationPlugin } from "components/plugins"; + // Creates a watcher on the indicated vm/prop for use in testing export function watchForChange({ vm, opts, propName, timeout = 1000, label = "" }) { @@ -62,7 +63,7 @@ export function getLocalVue() { }; localVue.directive("b-tooltip", mockedDirective); localVue.directive("b-popover", mockedDirective); - localVue.filter("localize", (value) => _l(value)); + localVue.use(localizationPlugin); return localVue; }