From 46fcd341075d5640375d9c7660cab6b28225c862 Mon Sep 17 00:00:00 2001 From: Mantra Date: Tue, 5 Nov 2024 17:43:46 +0000 Subject: [PATCH] first batch of tests --- .../unit/components/basic/basicApp.test.ts | 79 ++++--- .../unit/components/code/codeEditor.test.ts | 222 ++++++++---------- .../unit/components/code/codeTab.test.ts | 18 +- .../unit/components/data/dataTab.test.ts | 24 +- .../tests/unit/components/fit/fitApp.test.ts | 88 +++---- .../tests/unit/components/fit/fitPlot.test.ts | 19 +- .../tests/unit/components/fit/fitTab.test.ts | 31 ++- .../tests/unit/components/fit/support.test.ts | 2 +- .../graphConfig/graphConfig.test.ts | 26 +- .../graphConfig/graphConfigs.test.ts | 14 +- .../graphConfigsCollapsible.test.ts | 10 +- .../graphConfig/hiddenVariables.test.ts | 16 +- 12 files changed, 273 insertions(+), 276 deletions(-) diff --git a/app/static/tests/unit/components/basic/basicApp.test.ts b/app/static/tests/unit/components/basic/basicApp.test.ts index b736f0e9..573c8819 100644 --- a/app/static/tests/unit/components/basic/basicApp.test.ts +++ b/app/static/tests/unit/components/basic/basicApp.test.ts @@ -1,45 +1,55 @@ // Mock the import of third party packages to prevent errors -jest.mock("plotly.js-basic-dist-min", () => ({})); -jest.mock("../../../../src/app/components/help/MarkdownItImport.ts", () => { - // eslint-disable-next-line func-names - return function () { - return { - use: jest.fn().mockReturnValue({ - renderer: { rules: {} }, - render: jest.fn() - }) - }; - }; +vi.mock("plotly.js-basic-dist-min", () => { + return { + default: undefined, + update: undefined + } +}); +vi.mock("../../../../src/components/help/MarkdownItImport.ts", () => { + class MarkDownItClass { + constructor() { + return { + use: vi.fn().mockReturnValue({ + renderer: { rules: {} }, + render: vi.fn() + }) + } + } + }; + return { + default: { + default: MarkDownItClass + } + } }); -/* eslint-disable import/first */ import Vuex from "vuex"; import { mount } from "@vue/test-utils"; import { expectLeftWodinTabs, expectRightWodinTabs } from "../../../testUtils"; -import HelpTab from "../../../../src/app/components/help/HelpTab.vue"; -import BasicApp from "../../../../src/app/components/basic/BasicApp.vue"; -import { BasicState } from "../../../../src/app/store/basic/state"; +import HelpTab from "../../../../src/components/help/HelpTab.vue"; +import BasicApp from "../../../../src/components/basic/BasicApp.vue"; +import { BasicState } from "../../../../src/store/basic/state"; import { mockBasicState, mockGraphsState, mockModelState, mockSensitivityState } from "../../../mocks"; -import WodinApp from "../../../../src/app/components/WodinApp.vue"; -import WodinPanels from "../../../../src/app/components/WodinPanels.vue"; -import OptionsTab from "../../../../src/app/components/options/OptionsTab.vue"; -import MultiSensitivityTab from "../../../../src/app/components/multiSensitivity/MultiSensitivityTab.vue"; -import { ModelAction } from "../../../../src/app/store/model/actions"; -import { VisualisationTab } from "../../../../src/app/store/appState/state"; -import { AppStateMutation } from "../../../../src/app/store/appState/mutations"; -import { AppConfig } from "../../../../src/app/types/responseTypes"; -import { getters as graphsGetters } from "../../../../src/app/store/graphs/getters"; - -const mockTooltipDirective = jest.fn(); +import WodinApp from "../../../../src/components/WodinApp.vue"; +import WodinPanels from "../../../../src/components/WodinPanels.vue"; +import OptionsTab from "../../../../src/components/options/OptionsTab.vue"; +import MultiSensitivityTab from "../../../../src/components/multiSensitivity/MultiSensitivityTab.vue"; +import { ModelAction } from "../../../../src/store/model/actions"; +import { VisualisationTab } from "../../../../src/store/appState/state"; +import { AppStateMutation } from "../../../../src/store/appState/mutations"; +import { AppConfig } from "../../../../src/types/responseTypes"; +import { getters as graphsGetters } from "../../../../src/store/graphs/getters"; + +const mockTooltipDirective = vi.fn(); function mockResizeObserver(this: any) { - this.observe = jest.fn(); - this.disconnect = jest.fn(); + this.observe = vi.fn(); + this.disconnect = vi.fn(); } (global.ResizeObserver as any) = mockResizeObserver; describe("BasicApp", () => { - const getWrapper = (mockSetOpenVisualisationTab = jest.fn(), config: Partial = {}) => { + const getWrapper = (mockSetOpenVisualisationTab = vi.fn(), config: Partial = {}) => { const state = mockBasicState({ config: config as any, configured: true }); const store = new Vuex.Store({ @@ -52,7 +62,7 @@ describe("BasicApp", () => { namespaced: true, state: mockModelState(), actions: { - [ModelAction.FetchOdinRunner]: jest.fn() + [ModelAction.FetchOdinRunner]: vi.fn() } }, sensitivity: { @@ -98,7 +108,6 @@ describe("BasicApp", () => { expectRightWodinTabs(wrapper, ["Run", "Sensitivity"]); const rightTabs = wodinPanels.find(".wodin-right #right-tabs"); - const rightTabLinks = rightTabs.findAll("ul li a"); expect(rightTabs.find("div.mt-4 div.run-tab").exists()).toBe(true); }); @@ -122,7 +131,7 @@ describe("BasicApp", () => { }); it("commits change new right tab selected", async () => { - const mockSetOpenTab = jest.fn(); + const mockSetOpenTab = vi.fn(); const wrapper = getWrapper(mockSetOpenTab); const rightTabs = wrapper.findComponent("#right-tabs"); await rightTabs.findAll("li a").at(1)!.trigger("click"); // Click Sensitivity Tab @@ -137,7 +146,7 @@ describe("BasicApp", () => { tabName: "Help" } }; - const wrapper = getWrapper(jest.fn(), helpConfig); + const wrapper = getWrapper(vi.fn(), helpConfig); expectRightWodinTabs(wrapper, ["Help", "Run", "Sensitivity"]); const rightTabs = wrapper.findComponent(WodinPanels).find(".wodin-right #right-tabs"); @@ -148,7 +157,7 @@ describe("BasicApp", () => { const multiSensConfig = { multiSensitivity: true }; - const wrapper = getWrapper(jest.fn(), multiSensConfig); + const wrapper = getWrapper(vi.fn(), multiSensConfig); expectRightWodinTabs(wrapper, ["Run", "Sensitivity", "Multi-sensitivity"]); const rightTabs = wrapper.findComponent(WodinPanels).find(".wodin-right #right-tabs"); @@ -166,7 +175,7 @@ describe("BasicApp", () => { }, multiSensitivity: true }; - const wrapper = getWrapper(jest.fn(), bothConfig); + const wrapper = getWrapper(vi.fn(), bothConfig); expectRightWodinTabs(wrapper, ["Help", "Run", "Sensitivity", "Multi-sensitivity"]); }); }); diff --git a/app/static/tests/unit/components/code/codeEditor.test.ts b/app/static/tests/unit/components/code/codeEditor.test.ts index f9bf1d00..6b02da26 100644 --- a/app/static/tests/unit/components/code/codeEditor.test.ts +++ b/app/static/tests/unit/components/code/codeEditor.test.ts @@ -1,19 +1,27 @@ // Mock the import of monaco loader so we can mock its methods -const mockMonacoEditor = { - onDidChangeModelContent: jest.fn(), - getModel: () => { - return { getLinesContent: jest.fn().mockReturnValueOnce(["new code"]) }; - }, - setValue: jest.fn(), - deltaDecorations: jest.fn() -}; -const mockMonaco = { - editor: { - create: jest.fn().mockReturnValue(mockMonacoEditor) +const { mockMonacoEditor, mockMonaco } = vi.hoisted(() => { + const mockMonacoEditor = { + onDidChangeModelContent: vi.fn(), + getModel: () => { + return { getLinesContent: vi.fn().mockReturnValueOnce(["new code"]) }; + }, + setValue: vi.fn(), + deltaDecorations: vi.fn() + }; + const mockMonaco = { + editor: { + create: vi.fn().mockReturnValue(mockMonacoEditor) + } + }; + return { + mockMonacoEditor, + mockMonaco } -}; -jest.mock("@monaco-editor/loader", () => { - return { init: async () => mockMonaco }; +}); +vi.mock("@monaco-editor/loader", () => { + return { + default: { init: async () => mockMonaco } + }; }); const editorGlyphs: any = { @@ -36,44 +44,38 @@ const monacoOptions = (state: string, message: string) => { glyphMarginHoverMessage: { value: message } }; }; -const expectDeltaDecorationInputs = (line: number, state: string, message: string, done: jest.DoneCallback) => { - setTimeout(() => { - const changeHandler = mockMonacoEditor.onDidChangeModelContent.mock.calls[0][0]; - changeHandler(); - setTimeout(() => { - // 1 for resetting all decorations and 1 for adding state decorations - // x2 (these trigger on mount too) - expect(mockMonacoEditor.deltaDecorations).toHaveBeenCalledTimes(4); - expect(mockMonacoEditor.deltaDecorations.mock.calls[0][1]).toStrictEqual([ - { - range: monacoLineRange(1, true), - options: {} - } - ]); - expect(mockMonacoEditor.deltaDecorations.mock.calls[1][1]).toStrictEqual([ - { - range: monacoLineRange(line), - options: monacoOptions(state, message) - } - ]); - done(); - }, 700); - }); +const expectDeltaDecorationInputs = async (line: number, state: string, message: string) => { + await new Promise(res => setTimeout(res, 700)); + const changeHandler = mockMonacoEditor.onDidChangeModelContent.mock.calls[0][0]; + changeHandler(); + await flushPromises(); + expect(mockMonacoEditor.deltaDecorations.mock.calls[0].at(-1)).toStrictEqual([ + { + range: monacoLineRange(1, true), + options: {} + } + ]); + expect(mockMonacoEditor.deltaDecorations.mock.calls[1].at(-1)).toStrictEqual([ + { + range: monacoLineRange(line), + options: monacoOptions(state, message) + } + ]); }; -/* eslint-disable import/first */ -import { shallowMount } from "@vue/test-utils"; +import { flushPromises, shallowMount } from "@vue/test-utils"; import Vuex from "vuex"; -import CodeEditor from "../../../../src/app/components/code/CodeEditor.vue"; -import { BasicState } from "../../../../src/app/store/basic/state"; -import { BasicConfig } from "../../../../src/app/types/responseTypes"; +import CodeEditor from "../../../../src/components/code/CodeEditor.vue"; +import { BasicState } from "../../../../src/store/basic/state"; +import { BasicConfig } from "../../../../src/types/responseTypes"; import { mockBasicState, mockCodeState, mockModelState } from "../../../mocks"; +import { nextTick } from "vue"; describe("CodeEditor", () => { - const mockHelpDirective = jest.fn(); + const mockHelpDirective = vi.fn(); const getWrapper = ( readOnlyCode = false, - mockUpdateCode = jest.fn(), + mockUpdateCode = vi.fn(), defaultCode = ["default code"], odinModelResponse = { valid: true } ) => { @@ -115,53 +117,45 @@ describe("CodeEditor", () => { }; afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); - it("initialises Monaco Editor", (done) => { + it("initialises Monaco Editor", async () => { const wrapper = getWrapper(); - setTimeout(() => { - const el = wrapper.find("div.editor").element; - expect(mockMonaco.editor.create.mock.calls[0][0]).toBe(el); - expect(mockMonaco.editor.create.mock.calls[0][1]).toStrictEqual({ - value: "line1\nline2", - language: "r", - minimap: { enabled: false }, - readOnly: false, - automaticLayout: true, - glyphMargin: true, - lineNumbersMinChars: 3 - }); - - done(); + await nextTick(); + const el = wrapper.find("div.editor").element; + expect(mockMonaco.editor.create.mock.calls[0][0]).toBe(el); + expect(mockMonaco.editor.create.mock.calls[0][1]).toStrictEqual({ + value: "line1\nline2", + language: "r", + minimap: { enabled: false }, + readOnly: false, + automaticLayout: true, + glyphMargin: true, + lineNumbersMinChars: 3 }); }); - it("Monaco Editor change handler makes a delayed code update dispatch", (done) => { - const mockUpdateCode = jest.fn(); + it("Monaco Editor change handler makes a delayed code update dispatch", async () => { + const mockUpdateCode = vi.fn(); getWrapper(false, mockUpdateCode); - setTimeout(() => { - expect(mockMonacoEditor.onDidChangeModelContent).toHaveBeenCalled(); - const changeHandler = mockMonacoEditor.onDidChangeModelContent.mock.calls[0][0]; + await nextTick(); + expect(mockMonacoEditor.onDidChangeModelContent).toHaveBeenCalled(); + const changeHandler = mockMonacoEditor.onDidChangeModelContent.mock.calls[0][0]; - // Trigger the handler the component has set and check that it invokes the action after 1s - - // it will get the new code we've mocked above in getLines() - changeHandler(); - expect(mockUpdateCode).not.toHaveBeenCalled(); - setTimeout(() => { - expect(mockUpdateCode).toHaveBeenCalled(); - expect(mockUpdateCode.mock.calls[0][1]).toStrictEqual(["new code"]); - done(); - }, 700); - }); + // Trigger the handler the component has set and check that it invokes the action after 1s - + // it will get the new code we've mocked above in getLines() + changeHandler(); + expect(mockUpdateCode).not.toHaveBeenCalled(); + await new Promise(res => setTimeout(res, 700)); + expect(mockUpdateCode).toHaveBeenCalled(); + expect(mockUpdateCode.mock.calls[0][1]).toStrictEqual(["new code"]); }); - it("initialises Monaco Editor as readonly if state is configured with readOnlyCode", (done) => { + it("initialises Monaco Editor as readonly if state is configured with readOnlyCode", async () => { getWrapper(true); - setTimeout(() => { - expect(mockMonaco.editor.create.mock.calls[0][1].readOnly).toBe(true); - done(); - }); + await nextTick(); + expect(mockMonaco.editor.create.mock.calls[0][1].readOnly).toBe(true); }); it("uses help directive on reset button", () => { @@ -172,60 +166,54 @@ describe("CodeEditor", () => { expect(mockHelpDirective.mock.calls[0][1].value).toBe("resetCode"); }); - it("can reset monaco editor", (done) => { - const mockUpdateCode = jest.fn(); + it("can reset monaco editor", async () => { + const mockUpdateCode = vi.fn(); const wrapper = getWrapper(false, mockUpdateCode); - setTimeout(() => { - expect(mockMonacoEditor.setValue).not.toHaveBeenCalled(); - wrapper.find("#reset-btn").trigger("click"); - expect(mockMonacoEditor.setValue).toHaveBeenCalled(); - expect(mockMonacoEditor.setValue).toHaveBeenCalledWith("default code"); - done(); - }); + await nextTick(); + expect(mockMonacoEditor.setValue).not.toHaveBeenCalled(); + wrapper.find("#reset-btn").trigger("click"); + expect(mockMonacoEditor.setValue).toHaveBeenCalled(); + expect(mockMonacoEditor.setValue).toHaveBeenCalledWith("default code"); }); it("does not render reset button when no default code and is not readOnly", () => { - const mockUpdateCode = jest.fn(); + const mockUpdateCode = vi.fn(); const wrapper = getWrapper(false, mockUpdateCode, []); expect(wrapper.find("#reset-btn").exists()).toBe(false); }); it("does not render reset button when is readOnly", () => { - const mockUpdateCode = jest.fn(); + const mockUpdateCode = vi.fn(); const wrapper = getWrapper(true, mockUpdateCode); expect(wrapper.find("#reset-btn").exists()).toBe(false); }); - it("executes deltaDecorations when there is an error", (done) => { - const mockUpdateCode = jest.fn(); + it("executes deltaDecorations when there is an error", async () => { + const mockUpdateCode = vi.fn(); const odinModelResponse = { valid: false, error: { line: [2], message: "error here" } }; getWrapper(false, mockUpdateCode, ["hey code"], odinModelResponse); - expectDeltaDecorationInputs(2, "error", "error here", done); + await expectDeltaDecorationInputs(2, "error", "error here"); }); - it("no input in deltaDecorations when error lines and messages are empty", (done) => { - const mockUpdateCode = jest.fn(); + it("no input in deltaDecorations when error lines and messages are empty", async () => { + const mockUpdateCode = vi.fn(); const odinModelResponse = { valid: false, error: {} }; getWrapper(false, mockUpdateCode, ["hey code"], odinModelResponse); - setTimeout(() => { - const changeHandler = mockMonacoEditor.onDidChangeModelContent.mock.calls[0][0]; - changeHandler(); - setTimeout(() => { - expect(mockMonacoEditor.deltaDecorations).toHaveBeenCalledTimes(4); - expect(mockMonacoEditor.deltaDecorations.mock.calls[1][1]).toStrictEqual([]); - done(); - }, 700); - }); + await nextTick(); + const changeHandler = mockMonacoEditor.onDidChangeModelContent.mock.calls[0][0]; + changeHandler(); + await flushPromises(); + expect(mockMonacoEditor.deltaDecorations.mock.calls[1].at(-1)).toStrictEqual([]); }); - it("executes deltaDecorations when there is an warning", (done) => { - const mockUpdateCode = jest.fn(); + it("executes deltaDecorations when there is an warning", async () => { + const mockUpdateCode = vi.fn(); const odinModelResponse = { valid: true, metadata: { @@ -233,11 +221,11 @@ describe("CodeEditor", () => { } }; getWrapper(false, mockUpdateCode, ["hey code"], odinModelResponse); - expectDeltaDecorationInputs(3, "warning", "warning here", done); + await expectDeltaDecorationInputs(3, "warning", "warning here"); }); - it("no input in deltaDecorations when warning lines and messages are empty", (done) => { - const mockUpdateCode = jest.fn(); + it("no input in deltaDecorations when warning lines and messages are empty", async () => { + const mockUpdateCode = vi.fn(); const odinModelResponse = { valid: true, metadata: { @@ -245,14 +233,10 @@ describe("CodeEditor", () => { } }; getWrapper(false, mockUpdateCode, ["hey code"], odinModelResponse); - setTimeout(() => { - const changeHandler = mockMonacoEditor.onDidChangeModelContent.mock.calls[0][0]; - changeHandler(); - setTimeout(() => { - expect(mockMonacoEditor.deltaDecorations).toHaveBeenCalledTimes(4); - expect(mockMonacoEditor.deltaDecorations.mock.calls[1][1]).toStrictEqual([]); - done(); - }, 700); - }); + await nextTick(); + const changeHandler = mockMonacoEditor.onDidChangeModelContent.mock.calls[0][0]; + changeHandler(); + await new Promise(res => setTimeout(res, 700)); + expect(mockMonacoEditor.deltaDecorations.mock.calls[1].at(-1)).toStrictEqual([]); }); }); diff --git a/app/static/tests/unit/components/code/codeTab.test.ts b/app/static/tests/unit/components/code/codeTab.test.ts index 8b7357df..743ade2a 100644 --- a/app/static/tests/unit/components/code/codeTab.test.ts +++ b/app/static/tests/unit/components/code/codeTab.test.ts @@ -1,14 +1,14 @@ import { shallowMount } from "@vue/test-utils"; import Vuex from "vuex"; import VueFeather from "vue-feather"; -import CodeTab from "../../../../src/app/components/code/CodeTab.vue"; -import CodeEditor from "../../../../src/app/components/code/CodeEditor.vue"; -import { BasicState } from "../../../../src/app/store/basic/state"; +import CodeTab from "../../../../src/components/code/CodeTab.vue"; +import CodeEditor from "../../../../src/components/code/CodeEditor.vue"; +import { BasicState } from "../../../../src/store/basic/state"; import { mockBasicState, mockCodeState, mockModelState } from "../../../mocks"; -import ErrorInfo from "../../../../src/app/components/ErrorInfo.vue"; -import { ModelState } from "../../../../src/app/store/model/state"; -import GenericHelp from "../../../../src/app/components/help/GenericHelp.vue"; -import GraphConfigsCollapsible from "../../../../src/app/components/graphConfig/GraphConfigsCollapsible.vue"; +import ErrorInfo from "../../../../src/components/ErrorInfo.vue"; +import { ModelState } from "../../../../src/store/model/state"; +import GenericHelp from "../../../../src/components/help/GenericHelp.vue"; +import GraphConfigsCollapsible from "@/components/graphConfig/GraphConfigsCollapsible.vue"; describe("CodeTab", () => { const defaultModelState = { @@ -22,10 +22,10 @@ describe("CodeTab", () => { }; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); - const mockCompileModel = jest.fn(); + const mockCompileModel = vi.fn(); const getWrapper = (odinModelState: Partial = defaultModelState, loading = false) => { const store = new Vuex.Store({ diff --git a/app/static/tests/unit/components/data/dataTab.test.ts b/app/static/tests/unit/components/data/dataTab.test.ts index 8996647f..9d838b48 100644 --- a/app/static/tests/unit/components/data/dataTab.test.ts +++ b/app/static/tests/unit/components/data/dataTab.test.ts @@ -1,17 +1,17 @@ import Vuex from "vuex"; import { shallowMount } from "@vue/test-utils"; import VueFeather from "vue-feather"; -import DataTab from "../../../../src/app/components/data/DataTab.vue"; -import ErrorInfo from "../../../../src/app/components/ErrorInfo.vue"; +import DataTab from "../../../../src/components/data/DataTab.vue"; +import ErrorInfo from "../../../../src/components/ErrorInfo.vue"; import { mockFitDataState, mockFitState } from "../../../mocks"; -import { FitState } from "../../../../src/app/store/fit/state"; -import { FitDataState } from "../../../../src/app/store/fitData/state"; +import { FitState } from "../../../../src/store/fit/state"; +import { FitDataState } from "../../../../src/store/fitData/state"; describe("Data Tab", () => { const getWrapper = ( state: Partial = {}, - mockUpload = jest.fn(), - mockUpdateTimeVariable = jest.fn() + mockUpload = vi.fn(), + mockUpdateTimeVariable = vi.fn() ) => { const store = new Vuex.Store({ state: mockFitState(), @@ -78,13 +78,13 @@ describe("Data Tab", () => { }); it("dispatches upload action", async () => { - const mockUpload = jest.fn(); + const mockUpload = vi.fn(); const wrapper = getWrapper({}, mockUpload); const mockFile = { name: "testFile.csv" }; const input = wrapper.find("input"); Object.defineProperty(input.element, "files", { - get: jest.fn().mockReturnValue([mockFile]) + get: vi.fn().mockReturnValue([mockFile]) }); await input.trigger("change"); expect(mockUpload).toHaveBeenCalledTimes(1); @@ -99,7 +99,7 @@ describe("Data Tab", () => { const columns = ["a", "b", "c"]; const timeVariableCandidates = ["a", "b"]; const timeVariable = "b"; - const mockUpdateTimeVar = jest.fn(); + const mockUpdateTimeVar = vi.fn(); const wrapper = getWrapper( { data, @@ -107,7 +107,7 @@ describe("Data Tab", () => { timeVariableCandidates, timeVariable }, - jest.fn(), + vi.fn(), mockUpdateTimeVar ); @@ -123,9 +123,9 @@ describe("Data Tab", () => { const wrapper = getWrapper(); const input = wrapper.find("input"); const el = input.element as HTMLInputElement; - const mockSetValue = jest.fn(); + const mockSetValue = vi.fn(); Object.defineProperty(el, "value", { - get: jest.fn().mockReturnValue("testFile.csv"), + get: vi.fn().mockReturnValue("testFile.csv"), set: mockSetValue }); await input.trigger("click"); diff --git a/app/static/tests/unit/components/fit/fitApp.test.ts b/app/static/tests/unit/components/fit/fitApp.test.ts index b0ed9036..703cfea5 100644 --- a/app/static/tests/unit/components/fit/fitApp.test.ts +++ b/app/static/tests/unit/components/fit/fitApp.test.ts @@ -1,24 +1,33 @@ // Mock the import of third party packages to prevent errors -jest.mock("plotly.js-basic-dist-min", () => ({})); -jest.mock("plotly.js-basic-dist-min", () => ({})); -jest.mock("../../../../src/app/components/help/MarkdownItImport.ts", () => { - // eslint-disable-next-line func-names - return function () { - return { - use: jest.fn().mockReturnValue({ - renderer: { rules: {} }, - render: jest.fn() - }) - }; - }; +vi.mock("plotly.js-basic-dist-min", () => { + return { + default: undefined, + update: undefined + } +}); +vi.mock("../../../../src/components/help/MarkdownItImport.ts", () => { + class MarkDownItClass { + constructor() { + return { + use: vi.fn().mockReturnValue({ + renderer: { rules: {} }, + render: vi.fn() + }) + } + } + }; + return { + default: { + default: MarkDownItClass + } + } }); -/* eslint-disable import/first */ import Vuex from "vuex"; import { mount } from "@vue/test-utils"; import { expectLeftWodinTabs, expectRightWodinTabs } from "../../../testUtils"; -import FitApp from "../../../../src/app/components/fit/FitApp.vue"; -import { FitState } from "../../../../src/app/store/fit/state"; +import FitApp from "../../../../src/components/fit/FitApp.vue"; +import { FitState } from "../../../../src/store/fit/state"; import { mockFitDataState, mockFitState, @@ -27,31 +36,31 @@ import { mockModelState, mockSensitivityState } from "../../../mocks"; -import WodinApp from "../../../../src/app/components/WodinApp.vue"; -import WodinPanels from "../../../../src/app/components/WodinPanels.vue"; -import OptionsTab from "../../../../src/app/components/options/OptionsTab.vue"; -import { ModelAction } from "../../../../src/app/store/model/actions"; -import CodeTab from "../../../../src/app/components/code/CodeTab.vue"; -import DataTab from "../../../../src/app/components/data/DataTab.vue"; -import RunTab from "../../../../src/app/components/run/RunTab.vue"; -import HelpTab from "../../../../src/app/components/help/HelpTab.vue"; -import MultiSensitivityTab from "../../../../src/app/components/multiSensitivity/MultiSensitivityTab.vue"; -import { VisualisationTab } from "../../../../src/app/store/appState/state"; -import { AppStateMutation } from "../../../../src/app/store/appState/mutations"; -import { ModelFitGetter } from "../../../../src/app/store/modelFit/getters"; -import { AppConfig } from "../../../../src/app/types/responseTypes"; -import { getters as graphsGetters } from "../../../../src/app/store/graphs/getters"; +import WodinApp from "../../../../src/components/WodinApp.vue"; +import WodinPanels from "../../../../src/components/WodinPanels.vue"; +import OptionsTab from "../../../../src/components/options/OptionsTab.vue"; +import { ModelAction } from "../../../../src/store/model/actions"; +import CodeTab from "../../../../src/components/code/CodeTab.vue"; +import DataTab from "../../../../src/components/data/DataTab.vue"; +import RunTab from "../../../../src/components/run/RunTab.vue"; +import HelpTab from "../../../../src/components/help/HelpTab.vue"; +import MultiSensitivityTab from "../../../../src/components/multiSensitivity/MultiSensitivityTab.vue"; +import { VisualisationTab } from "../../../../src/store/appState/state"; +import { AppStateMutation } from "../../../../src/store/appState/mutations"; +import { ModelFitGetter } from "../../../../src/store/modelFit/getters"; +import { AppConfig } from "../../../../src/types/responseTypes"; +import { getters as graphsGetters } from "../../../../src/store/graphs/getters"; function mockResizeObserver(this: any) { - this.observe = jest.fn(); - this.disconnect = jest.fn(); + this.observe = vi.fn(); + this.disconnect = vi.fn(); } (global.ResizeObserver as any) = mockResizeObserver; describe("FitApp", () => { - const mockTooltipDirective = jest.fn(); + const mockTooltipDirective = vi.fn(); - const getWrapper = (mockSetOpenVisualisationTab = jest.fn(), config: Partial = {}) => { + const getWrapper = (mockSetOpenVisualisationTab = vi.fn(), config: Partial = {}) => { const state = mockFitState({ config: config as any }); const store = new Vuex.Store({ state, @@ -63,7 +72,7 @@ describe("FitApp", () => { namespaced: true, state: mockModelState(), actions: { - [ModelAction.FetchOdinRunner]: jest.fn() + [ModelAction.FetchOdinRunner]: vi.fn() } }, fitData: { @@ -158,12 +167,10 @@ describe("FitApp", () => { }); it("commits open tab change when change tab", async () => { - const mockSetOpenTab = jest.fn(); + const mockSetOpenTab = vi.fn(); const wrapper = getWrapper(mockSetOpenTab); const rightTabs = wrapper.find("#right-tabs"); - const leftTabs = wrapper.find("#left-tabs"); - const optionsTab = leftTabs.findComponent(OptionsTab); await rightTabs.findAll("li a").at(1)!.trigger("click"); // Click Fit tab expect(mockSetOpenTab).toHaveBeenCalledTimes(1); expect(mockSetOpenTab.mock.calls[0][1]).toBe(VisualisationTab.Fit); @@ -176,9 +183,8 @@ describe("FitApp", () => { tabName: "Help" } }; - const wrapper = getWrapper(jest.fn(), helpConfig); + const wrapper = getWrapper(vi.fn(), helpConfig); const wodinPanels = wrapper.findComponent(WodinPanels); - const rightPanel = wodinPanels.find(".wodin-right"); expectRightWodinTabs(wrapper, ["Help", "Run", "Fit", "Sensitivity"]); const rightTabs = wodinPanels.find(".wodin-right #right-tabs"); @@ -189,7 +195,7 @@ describe("FitApp", () => { const multiSensConfig = { multiSensitivity: true }; - const wrapper = getWrapper(jest.fn(), multiSensConfig); + const wrapper = getWrapper(vi.fn(), multiSensConfig); const wodinPanels = wrapper.findComponent(WodinPanels); expectRightWodinTabs(wrapper, ["Run", "Fit", "Sensitivity", "Multi-sensitivity"]); @@ -208,7 +214,7 @@ describe("FitApp", () => { }, multiSensitivity: true }; - const wrapper = getWrapper(jest.fn(), bothConfig); + const wrapper = getWrapper(vi.fn(), bothConfig); expectRightWodinTabs(wrapper, ["Help", "Run", "Fit", "Sensitivity", "Multi-sensitivity"]); }); }); diff --git a/app/static/tests/unit/components/fit/fitPlot.test.ts b/app/static/tests/unit/components/fit/fitPlot.test.ts index d20abdb3..31943d04 100644 --- a/app/static/tests/unit/components/fit/fitPlot.test.ts +++ b/app/static/tests/unit/components/fit/fitPlot.test.ts @@ -1,17 +1,16 @@ // Mock plotly before import RunTab, which indirectly imports plotly via WodinPlot -jest.mock("plotly.js-basic-dist-min", () => {}); +vi.mock("plotly.js-basic-dist-min", () => ({})); -/* eslint-disable import/first */ import Vuex from "vuex"; import { shallowMount } from "@vue/test-utils"; -import { FitState } from "../../../../src/app/store/fit/state"; -import { FitDataGetter } from "../../../../src/app/store/fitData/getters"; -import WodinPlot from "../../../../src/app/components/WodinPlot.vue"; -import FitPlot from "../../../../src/app/components/fit/FitPlot.vue"; -import { OdinFitResult } from "../../../../src/app/types/wrapperTypes"; +import { FitState } from "../../../../src/store/fit/state"; +import { FitDataGetter } from "../../../../src/store/fitData/getters"; +import WodinPlot from "../../../../src/components/WodinPlot.vue"; +import FitPlot from "../../../../src/components/fit/FitPlot.vue"; +import { OdinFitResult } from "../../../../src/types/wrapperTypes"; describe("FitPlot", () => { - const mockSolution = jest.fn().mockReturnValue({ + const mockSolution = vi.fn().mockReturnValue({ x: [0, 0.5, 1], values: [ { name: "y", y: [5, 6, 7] }, @@ -19,7 +18,7 @@ describe("FitPlot", () => { ] }); - const mockRunSolution = jest.fn().mockReturnValue({ + const mockRunSolution = vi.fn().mockReturnValue({ x: [0, 0.5, 1], values: [ { name: "y", y: [50, 60, 70] }, @@ -135,7 +134,7 @@ describe("FitPlot", () => { }; afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("renders as expected when modelFit has solution", () => { diff --git a/app/static/tests/unit/components/fit/fitTab.test.ts b/app/static/tests/unit/components/fit/fitTab.test.ts index abc2cf4b..35586351 100644 --- a/app/static/tests/unit/components/fit/fitTab.test.ts +++ b/app/static/tests/unit/components/fit/fitTab.test.ts @@ -1,18 +1,17 @@ // Mock plotly before import RunTab, which indirectly imports plotly via FitPlot -jest.mock("plotly.js-basic-dist-min", () => {}); +vi.mock("plotly.js-basic-dist-min", () => ({})); -/* eslint-disable import/first */ import { mount } from "@vue/test-utils"; import Vuex from "vuex"; import VueFeather from "vue-feather"; -import FitTab from "../../../../src/app/components/fit/FitTab.vue"; -import { FitState } from "../../../../src/app/store/fit/state"; -import ActionRequiredMessage from "../../../../src/app/components/ActionRequiredMessage.vue"; -import LoadingSpinner from "../../../../src/app/components/LoadingSpinner.vue"; -import FitPlot from "../../../../src/app/components/fit/FitPlot.vue"; +import FitTab from "../../../../src/components/fit/FitTab.vue"; +import { FitState } from "../../../../src/store/fit/state"; +import ActionRequiredMessage from "../../../../src/components/ActionRequiredMessage.vue"; +import LoadingSpinner from "../../../../src/components/LoadingSpinner.vue"; +import FitPlot from "../../../../src/components/fit/FitPlot.vue"; import { mockFitState, mockGraphsState } from "../../../mocks"; -import { WodinError } from "../../../../src/app/types/responseTypes"; -import ErrorInfo from "../../../../src/app/components/ErrorInfo.vue"; +import { WodinError } from "../../../../src/types/responseTypes"; +import ErrorInfo from "../../../../src/components/ErrorInfo.vue"; describe("Fit Tab", () => { const getWrapper = ( @@ -23,8 +22,8 @@ describe("Fit Tab", () => { converged: boolean | null = true, fitting = false, sumOfSquares: number | null = 2.1, - mockFitModel = jest.fn(), - mockSetFitting = jest.fn(), + mockFitModel = vi.fn(), + mockSetFitting = vi.fn(), error: WodinError | null = null ) => { const store = new Vuex.Store({ @@ -50,7 +49,7 @@ describe("Fit Tab", () => { fitUpdateRequired, error, result: { - solution: jest.fn() + solution: vi.fn() } } as any, getters: { @@ -178,7 +177,7 @@ describe("Fit Tab", () => { it("renders model fit error as expected", () => { const error = { error: "test error", detail: "test detail" }; - const wrapper = getWrapper({}, false, {}, 10, true, false, 2.1, jest.fn(), jest.fn(), error); + const wrapper = getWrapper({}, false, {}, 10, true, false, 2.1, vi.fn(), vi.fn(), error); expect(wrapper.findComponent(ErrorInfo).props("error")).toStrictEqual(error); expect(wrapper.findComponent(ActionRequiredMessage).props("message")).toBe( "An error occurred during model fit." @@ -186,15 +185,15 @@ describe("Fit Tab", () => { }); it("dispatches fit action on click button", async () => { - const mockFitModel = jest.fn(); + const mockFitModel = vi.fn(); const wrapper = getWrapper({}, false, false, null, null, false, null, mockFitModel); await wrapper.find("#fit-btn").trigger("click"); expect(mockFitModel).toHaveBeenCalledTimes(1); }); it("cancel button sets fitting to false", async () => { - const mockSetFitting = jest.fn(); - const wrapper = getWrapper({}, false, false, 1, false, true, 25.6, jest.fn(), mockSetFitting); + const mockSetFitting = vi.fn(); + const wrapper = getWrapper({}, false, false, 1, false, true, 25.6, vi.fn(), mockSetFitting); await wrapper.find("#cancel-fit-btn").trigger("click"); expect(mockSetFitting).toHaveBeenCalledTimes(1); expect(mockSetFitting.mock.calls[0][1]).toBe(false); diff --git a/app/static/tests/unit/components/fit/support.test.ts b/app/static/tests/unit/components/fit/support.test.ts index 54a61dc4..abf55490 100644 --- a/app/static/tests/unit/components/fit/support.test.ts +++ b/app/static/tests/unit/components/fit/support.test.ts @@ -1,4 +1,4 @@ -import { fitRequirementsExplanation, fitUpdateRequiredExplanation } from "../../../../src/app/components/fit/support"; +import { fitRequirementsExplanation, fitUpdateRequiredExplanation } from "../../../../src/components/fit/support"; describe("construct actionable error messages from requirements", () => { const reqsTrue = { diff --git a/app/static/tests/unit/components/graphConfig/graphConfig.test.ts b/app/static/tests/unit/components/graphConfig/graphConfig.test.ts index 88bd729f..9477817e 100644 --- a/app/static/tests/unit/components/graphConfig/graphConfig.test.ts +++ b/app/static/tests/unit/components/graphConfig/graphConfig.test.ts @@ -1,16 +1,16 @@ import Vuex from "vuex"; import { shallowMount } from "@vue/test-utils"; -import { BasicState } from "../../../../src/app/store/basic/state"; -import GraphConfig from "../../../../src/app/components/graphConfig/GraphConfig.vue"; -import { GraphsAction } from "../../../../src/app/store/graphs/actions"; -import { GraphsState } from "../../../../src/app/store/graphs/state"; -import { GraphsMutation } from "../../../../src/app/store/graphs/mutations"; -import GraphSettings from "../../../../src/app/components/GraphSettings.vue"; +import { BasicState } from "../../../../src/store/basic/state"; +import GraphConfig from "../../../../src/components/graphConfig/GraphConfig.vue"; +import { GraphsAction } from "../../../../src/store/graphs/actions"; +import { GraphsState } from "../../../../src/store/graphs/state"; +import { GraphsMutation } from "../../../../src/store/graphs/mutations"; +import GraphSettings from "../../../../src/components/GraphSettings.vue"; describe("GraphConfig", () => { - const mockUpdateSelectedVariables = jest.fn(); - const mockDeleteGraph = jest.fn(); - const mockTooltipDirective = jest.fn(); + const mockUpdateSelectedVariables = vi.fn(); + const mockDeleteGraph = vi.fn(); + const mockTooltipDirective = vi.fn(); const defaultGraphState = { config: [ { @@ -66,7 +66,7 @@ describe("GraphConfig", () => { }; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("renders as expected", () => { @@ -92,7 +92,7 @@ describe("GraphConfig", () => { it("starting drag sets values in event and emits setDragging", async () => { const wrapper = getWrapper(); const s = wrapper.findAll(".graph-config-panel .badge").at(0)!; - const setData = jest.fn(); + const setData = vi.fn(); await s.trigger("dragstart", { dataTransfer: { setData }, ctrlKey: false, metaKey: false }); expect(setData).toHaveBeenNthCalledWith(1, "variable", "S"); expect(setData).toHaveBeenNthCalledWith(2, "srcGraphConfig", "0"); @@ -102,7 +102,7 @@ describe("GraphConfig", () => { it("start drag sets values copyVar to true in event when Ctrl key pressed", async () => { const wrapper = getWrapper(); const s = wrapper.findAll(".graph-config-panel .badge").at(0)!; - const setData = jest.fn(); + const setData = vi.fn(); await s.trigger("dragstart", { dataTransfer: { setData }, ctrlKey: true, metaKey: false }); expect(setData).toHaveBeenNthCalledWith(3, "copyVar", "true"); }); @@ -110,7 +110,7 @@ describe("GraphConfig", () => { it("start drag sets values copyVar to true in event when meta key pressed", async () => { const wrapper = getWrapper(); const s = wrapper.findAll(".graph-config-panel .badge").at(0)!; - const setData = jest.fn(); + const setData = vi.fn(); await s.trigger("dragstart", { dataTransfer: { setData }, ctrlKey: false, metaKey: true }); expect(setData).toHaveBeenNthCalledWith(3, "copyVar", "true"); }); diff --git a/app/static/tests/unit/components/graphConfig/graphConfigs.test.ts b/app/static/tests/unit/components/graphConfig/graphConfigs.test.ts index 6803ef7e..c2a25214 100644 --- a/app/static/tests/unit/components/graphConfig/graphConfigs.test.ts +++ b/app/static/tests/unit/components/graphConfig/graphConfigs.test.ts @@ -1,19 +1,19 @@ import Vuex from "vuex"; import { shallowMount } from "@vue/test-utils"; import { nextTick } from "vue"; -import { BasicState } from "../../../../src/app/store/basic/state"; +import { BasicState } from "../../../../src/store/basic/state"; import { mockBasicState } from "../../../mocks"; -import { GraphsAction } from "../../../../src/app/store/graphs/actions"; -import GraphConfigs from "../../../../src/app/components/graphConfig/GraphConfigs.vue"; -import GraphConfig from "../../../../src/app/components/graphConfig/GraphConfig.vue"; -import HiddenVariables from "../../../../src/app/components/graphConfig/HiddenVariables.vue"; +import { GraphsAction } from "../../../../src/store/graphs/actions"; +import GraphConfigs from "../../../../src/components/graphConfig/GraphConfigs.vue"; +import GraphConfig from "../../../../src/components/graphConfig/GraphConfig.vue"; +import HiddenVariables from "../../../../src/components/graphConfig/HiddenVariables.vue"; describe("GraphConfigs", () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); - const mockNewGraph = jest.fn(); + const mockNewGraph = vi.fn(); const namespaced = true; const getWrapper = () => { const store = new Vuex.Store({ diff --git a/app/static/tests/unit/components/graphConfig/graphConfigsCollapsible.test.ts b/app/static/tests/unit/components/graphConfig/graphConfigsCollapsible.test.ts index daaca313..da24f7e0 100644 --- a/app/static/tests/unit/components/graphConfig/graphConfigsCollapsible.test.ts +++ b/app/static/tests/unit/components/graphConfig/graphConfigsCollapsible.test.ts @@ -1,10 +1,10 @@ import Vuex from "vuex"; import { shallowMount } from "@vue/test-utils"; -import { ModelState } from "../../../../src/app/store/model/state"; -import { BasicState } from "../../../../src/app/store/basic/state"; -import { mockBasicState, mockCodeState, mockModelState } from "../../../mocks"; -import GraphConfigsCollapsible from "../../../../src/app/components/graphConfig/GraphConfigsCollapsible.vue"; -import VerticalCollapse from "../../../../src/app/components/VerticalCollapse.vue"; +import { ModelState } from "../../../../src/store/model/state"; +import { BasicState } from "../../../../src/store/basic/state"; +import { mockBasicState, mockModelState } from "../../../mocks"; +import GraphConfigsCollapsible from "../../../../src/components/graphConfig/GraphConfigsCollapsible.vue"; +import VerticalCollapse from "../../../../src/components/VerticalCollapse.vue"; describe("GraphConfigsCollapsible", () => { const defaultModelState = { diff --git a/app/static/tests/unit/components/graphConfig/hiddenVariables.test.ts b/app/static/tests/unit/components/graphConfig/hiddenVariables.test.ts index c0a7781a..1f54f191 100644 --- a/app/static/tests/unit/components/graphConfig/hiddenVariables.test.ts +++ b/app/static/tests/unit/components/graphConfig/hiddenVariables.test.ts @@ -1,12 +1,12 @@ import Vuex from "vuex"; import { shallowMount } from "@vue/test-utils"; -import { BasicState } from "../../../../src/app/store/basic/state"; -import { GraphsAction } from "../../../../src/app/store/graphs/actions"; -import HiddenVariables from "../../../../src/app/components/graphConfig/HiddenVariables.vue"; -import { GraphsGetter } from "../../../../src/app/store/graphs/getters"; +import { BasicState } from "../../../../src/store/basic/state"; +import { GraphsAction } from "../../../../src/store/graphs/actions"; +import HiddenVariables from "../../../../src/components/graphConfig/HiddenVariables.vue"; +import { GraphsGetter } from "../../../../src/store/graphs/getters"; // Mock the fadeColor util which uses color package, which doesn't play nicely with jest -jest.mock("../../../../src/app/components/graphConfig/utils", () => { +vi.mock("../../../../src/components/graphConfig/utils", () => { return { fadeColor: (input: string) => { const match = input.match(/rgb\(([0-9]*), ([0-9]*), ([0-9]*)\)/); @@ -18,10 +18,10 @@ jest.mock("../../../../src/app/components/graphConfig/utils", () => { describe("HiddenVariables", () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); - const mockUpdateSelectedVariables = jest.fn(); + const mockUpdateSelectedVariables = vi.fn(); const getWrapper = (hiddenVariables = ["I", "R"]) => { const store = new Vuex.Store({ @@ -85,7 +85,7 @@ describe("HiddenVariables", () => { it("starting drag sets values in event and emits setDragging", async () => { const wrapper = getWrapper(); const s = wrapper.findAll(".hidden-variables-panel .variable").at(0)!; - const setData = jest.fn(); + const setData = vi.fn(); await s.trigger("dragstart", { dataTransfer: { setData } }); expect(setData.mock.calls[0][0]).toBe("variable"); expect(setData.mock.calls[0][1]).toStrictEqual("I");